v0.2.23 — 붙여 쓴 프롬프트도 프롬프트다 (전체 코드 감사 A24·A25)
2026-09-06. 릴리스된 산출물과 소스 전체(19모듈 3,930줄)를 Grok과 함께 검증한 결과다.
채점 대상은 사용자가 실제로 실행하는 것이었다 — 태그 v0.2.22의 dist와 origin/main의
dist가 바이트 동일함을 먼저 확인했으므로, 캐시 번들 채점이 곧 릴리스 채점이다.
두 결함 모두 배포 번들에서 재현한 뒤 고쳤고, 같은 페이로드로 다시 쟀다.
A24 — 인증 게이트를 통과하고 이력에도 남지 않는 유료 턴
-p는 값을 붙여서도 받는다. clap은 -p2+2를 -p 2+2와 똑같이 읽는다. 그런데 이 래퍼의
짧은 플래그 판정은 /^-[A-Za-z][A-Za-z]+$/ — 토큰 전체가 알파벳이어야 했다. 그건 붙인 값이
우연히 알파벳일 때만 참이므로, 숫자·슬래시·점을 포함한 값은 기록기와 인증 게이트 양쪽에서
동시에 사라졌다.
grok_cli에는 서버 측 checkAuth가 없다 — hook.ts가 스스로 적어둔 대로 PreToolUse hook이
유일한 게이트다. 그 게이트가 열려 있었다.
실측 (배포 번들 + 배포 dist/hook.js):
| 확인 | 수정 전 | 수정 후 |
|---|---|---|
hook, 로그아웃 · ["-p","2+2"] |
deny | deny |
hook, 로그아웃 · ["-p2+2"] |
allow | deny |
hook, 로그아웃 · ["-p/tmp/x"] · ["-pfoo.txt"] |
allow | deny |
hook · ["sessions","list"] · ["--version"] · ["-wq","name"] |
allow | allow (유지) |
번들 · ["-p2+2","--output-format","json"] |
status: ok, grok이 text:"**4**" · stopReason: end_turn · 실제 sessionId — 유료 턴 |
동일하게 성공 |
그 턴의 promptRun / 이력 |
부재 / 행 없음 (2→2) | true / 행 기록 (promptPreview: "2+2") |
즉 A2가 고친 피해(위임이 /grok:usage·/grok:status에서 사라짐)가 다른 문으로 재발해
있었고, 이번에는 인증 게이트까지 함께 열려 있었다.
수정: 판정 대상을 "토큰 전체"에서 **"앞쪽 옵션 문자열"**로 바꿨다. 붙인 값은 옵션이 아니므로
판정에서 제외되고(-s01a0p619의 p는 데이터다), -p=VALUE도 clap과 같게 읽는다.
Grok의 몫: 게이트 구멍을 독립적으로 먼저 찾았다 — 실제 코드를 주고 반증을 시켰더니
-p0, -p/tmp/x, -pfoo.txt로 GAP_FOUND를 돌려줬다. 수정 후 다시 검토시켰을 때는 누락을
하나도 찾지 못했고, 대신 -mp(= --model p)를 과잉 게이트라고 지적했다. 그건 기각했다 —
실측 결과 수정 전후가 동일하고(옛 규칙도 true), 모듈 주석과 기존 테스트가 이미 명시한
의도적 정책이다("과잉 게이트는 로그인한 사용자에게 비용이 없지만, 누락은 검사 없이 쿼터를 태운다").
A25 — cwd를 안 넘긴 grok_cli 이력이 cwd: ""로 기록
runGrokCli는 생략된 cwd를 process.cwd()로 채우는데, 이력을 쓰는 호출부는 그걸 알 수 없어
cwd ?? ''를 적었다. 같은 디렉터리에서 돈 위임 2건 중 1건만 그 디렉터리로 집계됐다.
| 질의 | 수정 전 | 수정 후 |
|---|---|---|
| 전체 | 2건 (recent[].cwd = ["D:/…", ""]) |
2건, 둘 다 실제 경로 |
| 그 디렉터리로 필터 | 1건 | 2건 |
A17이 읽는 쪽에서 고친 문제를 쓰는 쪽에서 만들고 있었던 셈이다.
수정: 호출부에서 기본값을 다시 유도하지 않는다 — 실행이 자기가 쓴 디렉터리를 보고한다
(GrokCliResult.cwd, 필수 필드). 호출부에 cwd ?? process.cwd()를 적는 대안은 기각했다:
A7이 정확히 그것(하나의 기본값을 두 곳에서 유도하다 어긋남) 때문에 생겼다.
Grok 판정은 이 수정안에 FIX_SOUND였고, 유일한 공격(cwd:""을 넘기면 여전히 "")은
실측으로 반증했다 — 빈 문자열은 spawn 전에 거부되고 이력 행도 남지 않는다.
결함이 아닌 것 (감사가 확인하고 넘어간 것)
- 레닥션은 사람이 외우는 비밀번호를 못 가린다 (
password = Password1!, 12자 하한).
Grok이LEAK_FOUND를 냈으나 4건 중 2건은 검토용 발췌본이 실제 키 목록보다 짧아서 생긴
오탐이었고(실제 코드는OPENAI_API_KEY·GH_TOKEN모두 마스킹), 남은 2건은
CLAUDE.md절대 원칙 4와docs/04가 이미 **"마스킹은 완화이지 보장이 아니다"**로 명시한
설계상 한계다. 하한을 낮추면 A6 라운드에서 Grok이 잡았던 과교정(45개 중 19개 훼손)이 그대로
돌아온다. - 표면 일치: 도구 9개 전부 등록·전부
additionalProperties: false, 커맨드 27·스킬 2·
에이전트 1이 참조하는 도구 이름 9개가 모두 실재,hooks/hooks.json래핑 정상,
accept-release10/10.
유닛
572 → 578. 수치는 npm test가 원천이다.