603 lines
15 KiB
Markdown
603 lines
15 KiB
Markdown
# keycloak-session-store 후속 검증 리뷰
|
||
|
||
- 검토일: 2026-09-20
|
||
- 대상: 이전 리뷰 `reviews/2026-09-20-keycloak-session-store-seven-stage-review.md` 반영 결과
|
||
- 판정: **아직 더 봐야댐**
|
||
|
||
---
|
||
|
||
## 1. 결론
|
||
|
||
이전 리뷰의 **문서 내용 자체는 대부분 제대로 반영됐다.**
|
||
|
||
특히 다음은 확실히 닫혔다.
|
||
|
||
- command analyzer가 `bash label="..."`, `sh label="..."`을 읽도록 수정됨
|
||
- TechLog Record의 shell block **992개 / 33 Record**를 analyzer가 실제로 인식함
|
||
- label의 `[lab host]`, `[워크스테이션]`, `[탐침 파드]` 같은 실행 위치를 execution context로 인식함
|
||
- 관련 회귀 테스트가 추가됨
|
||
- Tree의 미처분 SSOT 절이 `KEEP_IN_SSOT / CONFIRMED`로 처분됨
|
||
- `이 기준이 선 근거.`가 `이 기준의 근거다.`로 수정됨
|
||
- prose/voice는 현재도 **61/61 PASS**
|
||
- TechViz가 직접 근거로 삼는 `current_section`은 **31/31 최신 SSOT와 일치**
|
||
- Figure Text / Overlap / Provenance 모두 PASS
|
||
- 전체 pipeline은 exit 0
|
||
- 전체 unittest는 **397 PASS / 14 skipped**
|
||
|
||
따라서 이전처럼 “문서 내용이 많이 남아 있다”는 상태는 아니다.
|
||
|
||
하지만 **완료를 증명하는 현재 remediation run의 관문 기록이 하네스 계약과 맞지 않는다.**
|
||
이 문제가 남아 있어서 현재 상태를 `완료`로 판정하면 안 된다.
|
||
|
||
---
|
||
|
||
# 2. 이전 리뷰 항목별 재검증
|
||
|
||
## 2.1 Command analyzer — 반영됨
|
||
|
||
이전 문제:
|
||
|
||
> analyzer가 `bash label=...`, `sh label=...`을 읽지 못해 shell block 99.2%를 놓쳤다.
|
||
|
||
현재 `scripts/command_pedagogy.py`는 fence의 첫 info token을 언어로 읽는다.
|
||
|
||
현재 전수 결과:
|
||
|
||
```text
|
||
Record 61
|
||
command-bearing Record 33
|
||
shell block 992
|
||
```
|
||
|
||
즉 이전에 빠졌던 labeled fence까지 모두 잡힌다.
|
||
|
||
현재 테스트에도 다음 계약이 들어갔다.
|
||
|
||
- labeled bash fence 분석
|
||
- labeled sh fence 분석
|
||
- 첫 token 전체 일치 검사
|
||
- label execution context 인식
|
||
- context가 아닌 label은 remote execution context를 숨기지 못함
|
||
|
||
이 항목은 **반영 완료**로 본다.
|
||
|
||
---
|
||
|
||
## 2.2 Command corpus 재분석 — 내용상 큰 결함은 새로 나오지 않음
|
||
|
||
기존 corpus 정책대로 `reference` mode로 61 Record 전체를 다시 분석했다.
|
||
|
||
```text
|
||
command-bearing Record = 33
|
||
shell blocks = 992
|
||
findings = 53
|
||
major findings = 0
|
||
command-like text = 15
|
||
```
|
||
|
||
남은 53개 deterministic signal:
|
||
|
||
```text
|
||
hidden-stderr 48
|
||
compound-remote-shell 5
|
||
```
|
||
|
||
이것들은 전역 금지 항목이 아니다.
|
||
|
||
실제 문맥을 다시 확인하면 대표적으로:
|
||
|
||
- OpenSSL의 handshake noise를 없앤 뒤 x509 값을 보는 경우
|
||
- JWT payload를 base64 decode하면서 decode stderr를 숨기는 경우
|
||
- build/restore 출력을 `/tmp/*.log`로 모은 뒤 그 파일을 읽는 경우
|
||
- `nginx -t && nginx -s reload`처럼 **검증 성공 뒤에만 변경**하는 안전 조건
|
||
- conntrack 실험처럼 stderr 억제가 어떤 오판을 만드는지 본문이 오히려 설명하는 경우
|
||
|
||
가 대부분이다.
|
||
|
||
따라서 현재 전수 재검사에서 **새로운 high-confidence command defect는 발견하지 못했다.**
|
||
|
||
다만 아래 §4의 **검토 증거 범위 문제**는 별개다.
|
||
|
||
---
|
||
|
||
## 2.3 Tree 미처분 절 — 반영됨
|
||
|
||
이전 미처분 절:
|
||
|
||
```text
|
||
2026-09-17 재현 — 어디까지 밟았고 무엇이 막았나
|
||
```
|
||
|
||
현재 tree에는 새 candidate가 생겼다.
|
||
|
||
```text
|
||
id: SSOT-2026-09-17-reproduction-progress-and-blocker
|
||
disposition: KEEP_IN_SSOT
|
||
dispositionReview: CONFIRMED
|
||
```
|
||
|
||
이유도 적절하다.
|
||
|
||
- 시점 의존적인 진행 추적 정보
|
||
- 각 Setup이 실제 절차와 미완료 단계를 이미 담음
|
||
- 별도 Record로 올리면 중복
|
||
|
||
현재 Tree 결과:
|
||
|
||
```text
|
||
records=61
|
||
written=61
|
||
unwritten=0
|
||
KEEP_IN_SSOT=23
|
||
warn=1
|
||
```
|
||
|
||
남은 warn 1은 source repository 부재뿐이다.
|
||
|
||
이 항목은 **반영 완료**다.
|
||
|
||
---
|
||
|
||
## 2.4 깨진 문장 — 반영됨
|
||
|
||
이전:
|
||
|
||
```text
|
||
이 기준이 선 근거.
|
||
```
|
||
|
||
현재:
|
||
|
||
```text
|
||
이 기준의 근거다.
|
||
```
|
||
|
||
또 Relation의 반복 문형도 일부 선별 정리했다.
|
||
|
||
예:
|
||
|
||
```text
|
||
이 절차가 만드는 두 시각의 차이에 대한 결론은 그 기록에 있다.
|
||
```
|
||
|
||
등으로 바뀌었다.
|
||
|
||
현재:
|
||
|
||
```text
|
||
check_prose 61/61 PASS
|
||
check_voice 61/61 PASS
|
||
```
|
||
|
||
이 항목은 **반영 완료**다.
|
||
|
||
---
|
||
|
||
# 3. SVG — 본문 근거는 닫혔지만 layout warning 7개는 남음
|
||
|
||
이전에는 31개 중 28개가 현재 SSOT section과 달랐다.
|
||
|
||
현재 직접 비교 결과:
|
||
|
||
```text
|
||
current_section exact match = 31 / 31
|
||
```
|
||
|
||
즉 **그림이 직접 근거로 삼는 현재 절은 전부 최신 SSOT와 맞는다.**
|
||
|
||
Figure gate도:
|
||
|
||
```text
|
||
Figure Text PASS 31/31
|
||
Figure Overlap PASS 31/31
|
||
Figure Provenance PASS 31/31
|
||
```
|
||
|
||
이다.
|
||
|
||
따라서 이전의 “28개 그림이 옛 본문을 보고 있다”는 핵심 문제는 닫혔다.
|
||
|
||
다만 `verify-project-layout.py`에는 아직 **7개 context warning**이 남는다.
|
||
|
||
```text
|
||
a1-transport-vs-discovery
|
||
d2-upgrade-direction
|
||
lab-topology
|
||
measurement-control
|
||
observation-points
|
||
open-questions-answered
|
||
wrong-predictions
|
||
```
|
||
|
||
세부적으로 보면 앞의 여섯 개는:
|
||
|
||
- `current_section` = MATCH
|
||
- `next_section` = MATCH
|
||
- 상위/이전 parent section snapshot만 STALE
|
||
|
||
인 형태다.
|
||
|
||
`wrong-predictions`는:
|
||
|
||
- previous = MATCH
|
||
- current = MATCH
|
||
- next `자료` section = STALE
|
||
|
||
이다.
|
||
|
||
즉 **그림 자체의 직접 근거가 낡았다는 의미는 아니다.**
|
||
여러 child section을 포함하는 parent/neighbor context가 뒤의 SSOT 수정 때문에 다시 달라진 잔여 warning이다.
|
||
|
||
그래도 현재 verifier가 이를 stale context로 보고 있으므로 최종 cleanup은 필요하다.
|
||
|
||
권장:
|
||
|
||
1. 모든 SSOT 수정이 끝난 뒤 이 7개 context를 마지막에 다시 prepare
|
||
2. spec의 semantic node/edge가 그대로인지 확인
|
||
3. semantic 변화가 없으면 context/evidence hash만 최신화
|
||
4. render → lint → text → overlap → provenance → preview
|
||
5. 다시 `verify-project-layout.py keycloak-session-store`
|
||
|
||
또는 parent section 전체를 previous context로 잡는 구조가 계속 연쇄 stale을 만든다면 verifier/context 모델을 별도 개선한다.
|
||
|
||
**SVG 내용 자체는 현재 정상으로 보지만, layout warning 7개는 아직 정리되지 않았다.**
|
||
|
||
---
|
||
|
||
# 4. 완료 차단 — 새 remediation run이 evidence gate에서 `--repo`를 빼고 PASS를 만들었다
|
||
|
||
이것이 현재 가장 중요한 finding이다.
|
||
|
||
현재 authoritative stage contract의 S3 gate:
|
||
|
||
```bash
|
||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로젝트> --repo
|
||
```
|
||
|
||
S5와 S6도 S3 gate를 다시 실행하므로 동일하게 `--repo`가 필요하다.
|
||
|
||
또 현재 pipeline skill은 명확히 적는다.
|
||
|
||
```text
|
||
exit 0 = 저장소 대조 완료
|
||
exit 1 = 실제 불일치
|
||
exit 2 = 검사 대상 불성립
|
||
exit 3 = source repo 부재로 UNVERIFIABLE
|
||
|
||
새 publication run에서는 exit 3을 PASS로 기록하면 안 된다.
|
||
```
|
||
|
||
그런데 새로 만든 remediation run 6개를 확인하면 S3/S5/S6가 전부 다음처럼 기록돼 있다.
|
||
|
||
```bash
|
||
node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak-session-store
|
||
```
|
||
|
||
즉 **`--repo`가 없다.**
|
||
|
||
대상 run:
|
||
|
||
```text
|
||
2026-09-20-1725-remediation-01-setup-reproduce-d4-certificate-renewal
|
||
2026-09-20-1725-remediation-02-setup-reproduce-d4a-deploy-hook
|
||
2026-09-20-1725-remediation-03-setup-reproduce-a7a-volatile-cause
|
||
2026-09-20-1725-remediation-04-setup-reproduce-b7a-orphan-session
|
||
2026-09-20-1725-remediation-05-reference-look-at-the-lookup-key-before-moving-the-store
|
||
2026-09-20-1725-remediation-06-setup-reproduce-b6-key-rotation
|
||
```
|
||
|
||
6 run × S3/S5/S6 = **18개 evidence gate 전부 `--repo` 누락**이다.
|
||
|
||
실제로 현재 머신에서 실행하면:
|
||
|
||
### 현재 run이 기록한 명령
|
||
|
||
```text
|
||
check_evidence.mjs keycloak-session-store
|
||
→ exit 0
|
||
→ 문제 없음
|
||
```
|
||
|
||
### 계약에 적힌 명령
|
||
|
||
```text
|
||
check_evidence.mjs keycloak-session-store --repo
|
||
→ exit 3
|
||
→ 저장소 경로가 이 기계에 없다
|
||
→ 불일치 0 / 대조 불가 1
|
||
```
|
||
|
||
즉 현재 run은 **원본 대조를 수행하지 않은 명령을 실행하고 PASS라고 기록했다.**
|
||
|
||
사용자와 합의한 기준상 source repository 부재 자체는 완료 차단으로 보지 않아도 된다.
|
||
|
||
하지만 그것은:
|
||
|
||
> 원본 대조를 하지 않고 PASS로 바꿔도 된다
|
||
|
||
는 뜻이 아니다.
|
||
|
||
정확한 상태는:
|
||
|
||
> **live source reconciliation = UNVERIFIABLE**
|
||
|
||
이다.
|
||
|
||
## 필요한 수정
|
||
|
||
기존 17:25 remediation run은 이미 만들어진 영수증이므로 고쳐 쓰지 않는다.
|
||
|
||
대신 둘 중 하나로 해결한다.
|
||
|
||
### A. source repo가 실제로 있으면
|
||
|
||
정본 경로를 찾아 tree의 sourceRepository를 실제 경로에 맞춘 뒤:
|
||
|
||
```text
|
||
check_evidence --repo
|
||
→ exit 0
|
||
```
|
||
|
||
으로 새 verification/remediation run을 만든다.
|
||
|
||
### B. source repo가 이 환경에는 없고 이를 사용자 예외로 허용한다면
|
||
|
||
**`--repo`를 삭제하지 말고** 결과를 그대로 남길 수 있어야 한다.
|
||
|
||
즉 run schema/verifier에 명시적인 상태를 둔다.
|
||
|
||
예:
|
||
|
||
```text
|
||
status: UNVERIFIABLE
|
||
exit: 3
|
||
reason: source repository unavailable on current machine
|
||
acceptedByProjectReview: true
|
||
```
|
||
|
||
이름은 달라도 된다.
|
||
|
||
핵심은:
|
||
|
||
- 실제 명령은 `--repo` 포함
|
||
- 실제 exit 3 보존
|
||
- PASS로 위장하지 않음
|
||
- 이 프로젝트에서는 사용자가 허용한 환경 제약이므로 전체 완료를 막지 않는다는 정책을 별도 표현
|
||
|
||
이다.
|
||
|
||
현재처럼 flag 자체를 빼는 방식은 안 된다.
|
||
|
||
---
|
||
|
||
# 5. verifier blind spot — 잘못된 gate인데 run verifier가 PASS한다
|
||
|
||
위 6개 run에 대해:
|
||
|
||
```text
|
||
python3 scripts/verify-pipeline-run.py <run.json>
|
||
```
|
||
|
||
은 전부 PASS한다.
|
||
|
||
즉 verifier는 현재:
|
||
|
||
- evidence gate가 하나 존재하는지
|
||
- recorded exit가 0인지
|
||
|
||
등은 보지만,
|
||
|
||
> **S3/S5/S6가 계약에 적힌 `check_evidence.mjs <project> --repo`를 실제로 실행했는가**
|
||
|
||
까지는 강제하지 않는다.
|
||
|
||
그래서 계약보다 약한 명령을 기록해도 통과했다.
|
||
|
||
이 부분도 같이 막아야 한다.
|
||
|
||
권장:
|
||
|
||
- stage gate를 raw command 문자열만으로 보지 말고 semantic gate id를 둔다.
|
||
- 최소한 S3/S5/S6의 evidence gate는 `--repo` 존재를 검증한다.
|
||
- 사용자가 허용한 source-missing 예외를 도입한다면 exit 3 + UNVERIFIABLE 상태를 verifier가 이해하게 한다.
|
||
|
||
**검사기를 통과시키려고 관문을 약하게 만드는 경로를 차단해야 한다.**
|
||
|
||
---
|
||
|
||
# 6. 33 Record / 992 command 재검토의 “증거 범위”는 아직 약함
|
||
|
||
이전 리뷰는:
|
||
|
||
```text
|
||
33개 command-bearing Record
|
||
992 shell block
|
||
```
|
||
|
||
을 parser 수정 뒤 다시 분석하라고 했다.
|
||
|
||
현재 내용 자체는 내가 이번 검토에서 다시 전수 실행했고:
|
||
|
||
```text
|
||
reference mode:
|
||
records 33
|
||
blocks 992
|
||
findings 53
|
||
major 0
|
||
```
|
||
|
||
을 확인했다.
|
||
|
||
그래서 **현재 command 내용에서 새 major defect가 남았다고 보지는 않는다.**
|
||
|
||
다만 repository에 새 command review artifact가 남아 있는 것은 이번 remediation에서 다룬 일부 Record뿐이다.
|
||
|
||
새 schemaVersion 4 remediation 중 command block이 있는 Record:
|
||
|
||
```text
|
||
D-4
|
||
D-4a
|
||
A-7a
|
||
B-7a
|
||
B-6
|
||
```
|
||
|
||
즉 current reviewer artifact가 직접 남은 것은 **5 / 33 command-bearing Record**다.
|
||
|
||
나머지는 옛 schemaVersion 1/2 run만 있고, 그것은 parser가 labeled fence를 못 보던 시기의 영수증이다.
|
||
|
||
이전 리뷰는 historical ledger를 소급 조작하지 말라고 했으므로 옛 run을 고치면 안 된다.
|
||
|
||
대신 **프로젝트 단위 current command audit artifact**를 하나 남기는 편이 맞다.
|
||
|
||
예:
|
||
|
||
```text
|
||
reviews/2026-09-20-keycloak-session-store-command-audit.json
|
||
reviews/2026-09-20-keycloak-session-store-command-audit.md
|
||
```
|
||
|
||
여기에 33 Record 각각에 대해:
|
||
|
||
- source SHA256
|
||
- shellBlocks
|
||
- mode
|
||
- findings
|
||
- majorFindings
|
||
- minor finding disposition
|
||
- reviewer verdict
|
||
|
||
을 남긴다.
|
||
|
||
Record를 실제로 수정한 경우에만 새 remediation run을 만든다.
|
||
|
||
이렇게 하면 historical receipt를 위조하지 않으면서도 **“parser 수정 뒤 992개를 다시 봤다”**는 현재 증거가 남는다.
|
||
|
||
---
|
||
|
||
# 7. 현재 자동 검증 결과
|
||
|
||
## 프로젝트 gate
|
||
|
||
```text
|
||
verify-tech-log-tree PASS
|
||
verify-project-layout PASS (warn 존재)
|
||
audit-records PASS
|
||
check-figure-text PASS 31/31
|
||
check-figure-overlap PASS 31/31
|
||
check-figure-provenance PASS 31/31
|
||
check-required-content PASS 61
|
||
check-ssot-facts PASS
|
||
git diff --check PASS
|
||
```
|
||
|
||
## 문체
|
||
|
||
```text
|
||
prose 61/61 PASS
|
||
voice 61/61 PASS
|
||
```
|
||
|
||
## 전체 pipeline
|
||
|
||
```text
|
||
verify-pipeline.py
|
||
exit 0
|
||
```
|
||
|
||
parser가 수정되면서 전체 corpus도 이제 Record의 labeled shell fence를 실제로 세고 있다.
|
||
|
||
keycloak-session-store 전체(final SSOT + Record) 기준:
|
||
|
||
```text
|
||
shell blocks = 1920
|
||
findings = 150
|
||
```
|
||
|
||
이 수치가 이전보다 늘어난 것은 defect가 늘어난 것이 아니라 **전에 못 보던 labeled fence를 이제 보기 시작했기 때문**이다.
|
||
|
||
## 전체 unittest
|
||
|
||
```text
|
||
Ran 397 tests
|
||
OK
|
||
skipped=14
|
||
```
|
||
|
||
새 labeled-fence 관련 테스트도 통과한다.
|
||
|
||
---
|
||
|
||
# 8. 남은 작업 순서
|
||
|
||
## 1. evidence gate 우회부터 고친다
|
||
|
||
가장 먼저 한다.
|
||
|
||
- 기존 17:25 run 수정 금지
|
||
- `--repo`를 제거해 PASS시키는 방식 금지
|
||
- source missing을 명시적 `UNVERIFIABLE`로 보존할 수 있게 계약/verifier를 정리
|
||
- 또는 실제 source repo를 찾을 수 있다면 정본 경로로 대조
|
||
|
||
## 2. run verifier가 필수 evidence gate를 검증하게 한다
|
||
|
||
최소 S3/S5/S6에서:
|
||
|
||
```text
|
||
check_evidence.mjs <project> --repo
|
||
```
|
||
|
||
를 요구한다.
|
||
|
||
환경 예외가 있으면 명시적 상태로만 허용한다.
|
||
|
||
## 3. 33 Record command audit를 현재 artifact로 남긴다
|
||
|
||
- 992 block
|
||
- reference-mode 53 minor / 0 major
|
||
- minor의 문맥상 허용 여부
|
||
- 15 command-like text의 분류
|
||
|
||
를 프로젝트 단위 audit으로 남긴다.
|
||
|
||
실제 수정한 Record만 새 remediation run을 만든다.
|
||
|
||
## 4. SVG 잔여 context warning 7개를 마지막에 닫는다
|
||
|
||
current section은 이미 31/31 맞다.
|
||
|
||
남은 7개 previous/next snapshot만 모든 SSOT 수정 후 마지막에 regenerate/review한다.
|
||
|
||
## 5. 최종 검증
|
||
|
||
순서대로:
|
||
|
||
```text
|
||
project gates
|
||
→ verify-pipeline.py
|
||
→ unittest
|
||
```
|
||
|
||
---
|
||
|
||
# 9. 최종 판정
|
||
|
||
이전 리뷰의 **문서 내용 수정은 대부분 제대로 반영됐다.**
|
||
|
||
특히:
|
||
|
||
- parser
|
||
- Tree
|
||
- 문체
|
||
- 그림의 직접 SSOT 근거
|
||
|
||
는 제대로 개선됐다.
|
||
|
||
하지만 현재 remediation run 6개가 계약상 필수인 `--repo` evidence reconciliation을 생략한 채 PASS로 기록되어 있고, verifier도 그 우회를 잡지 못한다.
|
||
|
||
따라서 지금 상태는:
|
||
|
||
> **keycloak-session-store = 아직 더 봐야댐**
|
||
|
||
문서 내용보다 **검증 계약과 영수증의 진실성**을 한 번 더 고쳐야 한다.
|