Skip to content

fix(hooks): Bash 보호경로 탐지 훅의 억제가 «이 세션 · 이 내용» 까지만 — 한 번 알린 경로가 클론 수명 내내 침묵하던 것 (#1746 K16) - #1777

Merged
xzawed merged 1 commit into
mainfrom
fix/k16-bash-hook-suppression
Oct 28, 2026
Merged

xzawed merged 1 commit into
mainfrom
fix/k16-bash-hook-suppression

Conversation

@xzawed

@xzawed xzawed commented Oct 28, 2026

Copy link
Copy Markdown
Owner

pytest tests/unit → 8631 passed / 5 skipped

무엇을 고쳤나 (#1746 K16 — 5건이 한 뿌리)

.claude/hooks/detect_protected_bash_write.py(테스트 불가 환경에서 보호 경로가 Bash 로 바뀌면 «알리는» PostToolUse 훅)의 억제 상태 .git/protected-bash-write-reported 가 알린 경로를 클론 수명 내내 쌓기만 했다 — 그 경로가 커밋·되돌림 뒤 다시 바뀌어도, 다른 세션에서 바뀌어도 다시는 알리지 않았다(P1-17 · P2-18 · P2-29 · P2-36 · P2-48).

수정 전 수정 후
상태 알린 경로 목록(만료·해제 없음) {"session": id, "reported": {경로: 내용 지문}}
같은 세션·같은 내용 조용 조용(유지 — 매 Bash 마다 떠들지 않는다)
같은 경로가 다시 바뀜 영구 침묵 다시 알림(지문 = 작업 트리 파일 sha256 앞 16자, 지워지면 absent)
커밋·되돌림으로 깨끗해진 뒤 다시 더러워짐 영구 침묵 다시 알림(매 실행 «지금 더러운 경로» 만 남긴다)
다른 세션 영구 침묵 다시 알림
세션 id 를 모름(없음·빈 값·문자열 아님) — 억제를 쓰지도 남기지도 않는다
깨진 stdin · 옛 형식/깊은 중첩 상태 파일 깊은 중첩 stdin 은 RecursionError 로 훅이 죽음 죽지 않고 «알리는» 쪽으로

판정·환경 조건(can_run_tests())·보호 경로 정의(check_edit_allowed 공유)·메시지 문구는 그대로다.

증거

  • 새 시험 17건(tests/unit/hooks/test_detect_protected_bash_write.py): 훅만 옛 판으로 되돌리면 이 파일 8 failed / 24 passed, 새 훅 32 passed · tests/unit/hooks 전체 292 passed.
    • 행동 시험은 지문 함수가 없는 옛 판에서도 «행동» 단언으로 red 가 나게 raising=False 로 심었다(예: «같은 경로가 Bash 로 «다시» 바뀌었는데 침묵한다»).
  • 단위 전체(작업트리): 8631 passed / 5 skipped — 작업트리에서는 skip 이 1건 더 나온다(메인 체크아웃은 4). 수집 8636 = 8619 + 17.
  • pre_push_gate.py: 16 OK · 1 FAIL 은 작업트리에 node_modules 가 없어서였고 npm ci 뒤 check_lint_js_nonvacuous.py 단독 재실행 통과.

Grok claim-review

  • session: 40b2e83c-617b-429d-a1c1-0d9d70cc04d7
  • claim: 같은 세션 안에서 같은 내용은 조용하고, 내용이 다시 바뀐 경로와 깨끗해졌다가 다시 더러워진 경로는 다시 알린다
  • verdict: SURVIVES
  • session: 7a6afbaa-c5db-4b0a-bc3d-cebd3ddcbb43
  • claim: 다른 세션은 아직 더러운 경로를 다시 알리고, 어떤 stdin·상태 파일 내용에도 훅이 예외로 죽지 않는다
  • verdict: WEAKENED

7a6afbaa 의 경과(같은 세션 3라운드):

  1. BROKEN — ⑤ 10,000단 중첩 JSON 은 RecursionError(ValueError 아님)라 안 잡힌다 · ④ 숫자 session_id: 1 이 저장된 "1" 과 같은 세션으로 합쳐진다 → 둘 다 코드로 고침(RecursionError 포착 · 문자열 id 만 세션).
  2. BROKEN — 문자열이 아닌 id 가 "" 로 접혀 «id 없던 기록» 과 합쳐진다 → 코드로 고침(id 를 모르면 억제를 읽지도 쓰지도 않는다).
  3. BROKEN — 이유는 제 재질문이 «can_run_tests() 거짓» 전제를 빠뜨린 것(검증 가능한 환경에선 의도대로 침묵 — test_verifiable_environment_stays_silent 가 지키는 설계). 그 아래 경로는 Grok 이 «다른 문자열 id 면 seen == {} 라 모든 더러운 경로를 알리고, id 를 모르면 읽기·기록을 건너뛰고 알린다» 로 확인했다. 문구 정밀도만 남아 여기서 멈췄다 — 최종 문구로 재판정은 받지 않았다(그래서 WEAKENED 로 적는다).

실제로는 Claude Code 가 늘 UUID 문자열 session_id 를 보내고 상태 파일은 훅 자신만 쓰므로(깊이 2) 1·2차 반례는 운영에서 생기지 않는다. 그래도 «모르면 알리는 쪽» 이 이 훅의 원칙이라 코드로 닫았다.

사람이 볼 것

  1. 이 훅은 테스트 불가 환경(can_run_tests() 거짓)에서만 말한다 — 이 PC·Codespaces 에서는 동작이 바뀐 것을 체감할 수 없다. 클라우드 세션에서 보호 경로(alembic/versions/·src/templates/*.html 등)를 Bash 로 두 번 고쳐 두 번 다 알림이 뜨는지가 실사용 확인이다.
  2. 옛 형식 상태 파일이 남아 있는 클론은 첫 실행에서 아직 더러운 보호 경로를 한 번 더 알린다(의도 — 모르는 상태는 알리는 쪽).

스스로 판단한 것

  • 억제 범위를 «세션 + 내용 지문» 으로 정했다. 내용 지문 없이 세션만 두면 같은 세션 안에서 같은 파일을 두 번째로 바꾼 것을 놓친다(P2-29 의 «경로당 1회»).
  • 작업트리에서는 .git 이 파일이라 상태를 못 남겨 매번 알린다 — 종전과 같은 동작이고 «조용해지는 것보다 낫다» 주석대로라 이 PR 범위 밖으로 둔다.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R4jYRzichV75XnYLSEFguX

@xzawed

xzawed commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

⚠️ AI 리뷰가 완전 검증되지 않음 — 기본값이 적용됐습니다(비활성/API 키 없음/빈 diff). AI 배점은 측정값이 아니므로 총점을 부분 점수로 보세요.

🔵 SCAManager 분석 결과

총점: 81/100 (등급 B)

점수 상세

항목 점수 만점
커밋 메시지 13 15
코드 품질 23 25
보안 14 20
구현 방향성 (AI) 21 25
테스트 10 15

정적 분석 이슈 (상위 10건)

  • [pylint] Import "from check_edit_allowed import can_run_tests, protected_reason" should be placed at the top of the module (.claude/hooks/detect_protected_bash_write.py:40)
  • [bandit] Consider possible security implications associated with the subprocess module. (.claude/hooks/detect_protected_bash_write.py:31)
  • [bandit] Starting a process with a partial executable path (.claude/hooks/detect_protected_bash_write.py:58)
  • [bandit] subprocess call - check for execution of untrusted input. (.claude/hooks/detect_protected_bash_write.py:58)
  • [pylint] Import outside toplevel (io) (tests/unit/hooks/test_detect_protected_bash_write.py:279)

@codecov

codecov Bot commented Oct 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…내내 침묵하던 것 (#1746 K16)

- 상태 = {세션, 경로 → 내용 지문}. 같은 세션·같은 내용만 조용하고, 다시 바뀌면 다시 알린다
- 매 실행 지금 더러운 경로만 남긴다 — 커밋·되돌림으로 깨끗해진 경로는 다음 변경에서 다시 알린다
- 다른 세션 · 세션 id 를 모르는 실행은 억제를 쓰지 않는다
- 깨진 입력·옛 형식·깊은 중첩 상태 파일에도 죽지 않고 «알리는» 쪽으로 간다(Grok 반례 2건 반영)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4jYRzichV75XnYLSEFguX
@xzawed
xzawed force-pushed the fix/k16-bash-hook-suppression branch from 6f6d07c to e9f42cd Compare October 28, 2026 22:40
@xzawed

xzawed commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

⚠️ AI 리뷰가 완전 검증되지 않음 — 기본값이 적용됐습니다(비활성/API 키 없음/빈 diff). AI 배점은 측정값이 아니므로 총점을 부분 점수로 보세요.

🔵 SCAManager 분석 결과

총점: 81/100 (등급 B)

점수 상세

항목 점수 만점
커밋 메시지 13 15
코드 품질 23 25
보안 14 20
구현 방향성 (AI) 21 25
테스트 10 15

정적 분석 이슈 (상위 10건)

  • [pylint] Import "from check_edit_allowed import can_run_tests, protected_reason" should be placed at the top of the module (.claude/hooks/detect_protected_bash_write.py:40)
  • [bandit] Consider possible security implications associated with the subprocess module. (.claude/hooks/detect_protected_bash_write.py:31)
  • [bandit] Starting a process with a partial executable path (.claude/hooks/detect_protected_bash_write.py:58)
  • [bandit] subprocess call - check for execution of untrusted input. (.claude/hooks/detect_protected_bash_write.py:58)
  • [pylint] Import outside toplevel (io) (tests/unit/hooks/test_detect_protected_bash_write.py:279)

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant