5.9 KiB
5.9 KiB
title, source_type, status, tags, created
| title | source_type | status | tags | created | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ca-tmpl write-time import-gate 훅(G5/G7) 오탐 — shared-contract 주석 정리 차단 | error-note | raw |
|
2026-06-19 |
ca-tmpl import-gate 훅의 오탐 — intra-module import 와 주석 속 금지 토큰
Parent
[[raw/branch-notes/chore-shared-contract-comment-cleanup]][[raw/branch-notes/chore-sample-portfolio-comment-cleanup]]— 동일 G7 오탐 재발(enableDefaultTyping()리터럴)
맥락
shared-contract 모듈의 결정-근거 주석을 README 로 이전(comment-only)하는 중, PreToolUse 훅
.claude/hooks/ca_import_gate.py 가 주석만 바꾸는 Edit 3건을 차단했다. 이 훅은 write 시점에
projected(편집 후 전체 파일) 내용을 스캔해 G1~G8 금지 패턴을 막는다 — ArchUnit/Gradle
빌드 게이트의 부분집합을 "디스크에 닿기 전"에 잡는 용도.
현상 — 차단 3건
-
G5 (shared-contract stdlib-only) —
response/BulkEnvelope.java,operation/Operation.java- 차단 라인:
import dev.caskeleton.shared.error.OperationalError;/import dev.caskeleton.shared.response.ApiError; - 이유:
JAVA_ONLY_RE = ^import\s+java\.만 허용하고, 그 외 모든^import \S를 위반으로 본다. shared-contract 의 gradle 매트릭스 의존이[]라서, 같은 모듈 내 다른 패키지 import (dev.caskeleton.shared.error.*)조차 cross-module 의존으로 오탐한다. - 실제로는 정당한 intra-module import — 컴파일·
verifyCleanArchitectureDependencies·ArchUnit 모두 통과하는 코드다(빌드 게이트는 모듈/프로젝트 단위라 패키지 간 import 를 막지 않음).
- 차단 라인:
-
G7 (
\bInheritableThreadLocal\b금지) —concurrency/DomainContextPropagator.java,concurrency/ThreadLocalDomainContextPropagator.java- 차단 라인: 주석이
{@code InheritableThreadLocal}을 언급(=쓰지 말라고 설명)하는 줄. - 이유:
CVE_RE가 줄 어디에든 토큰이 있으면 매치한다 — 사용과 언급을 구분하지 못한다. 원본 코드도 같은 토큰을 주석에 갖고 있었지만 훅 도입 전 커밋이라 통과했을 뿐. - whole-file scan 이므로, 한 파일에 토큰이 2곳(클래스 JavaDoc + 인라인 주석)이면 한 번의 write 로 둘 다 제거해야 통과한다(한 곳만 고치면 나머지가 여전히 차단).
- 차단 라인: 주석이
왜 위험/성가신가
- "주석만 바꾸는" 안전한 작업이 차단되어, 작업자가 (a) 정당한 import 를 지우거나(컴파일 깨짐) (b) gradle 매트릭스를 약화시키는(규칙 자체는 옳음) 잘못된 "수정"으로 유혹받기 쉽다.
- 훅 메시지가 "매트릭스/ArchUnit 을 먼저 바꾸라"고 안내하지만, 이 경우 규칙 변경은 틀린 대응이다 — 규칙은 정당하고 훅의 매칭이 과도할 뿐.
회피 (이번 작업에서 택한 대응)
- G5 파일(Operation/BulkEnvelope): import 를 건드리지 않기 위해 두 파일의 코드 주석은 미정리로 남기고, 두 클래스의 결정 근거는 README 에만 수록. import 제거·매트릭스 약화 둘 다 거부.
- G7 파일(concurrency): 주석에서
InheritableThreadLocal리터럴을 동의어로 표현 ("the inheritance-based variant" / snake_case 규칙명no_inheritable_thread_local")해 토큰을 제거. 의미는 README(.md — 이 훅은src/**/*.java` 만 검사하므로 미게이트)가 전체 용어로 보존. 한 파일의 두 토큰은 클래스 JavaDoc + 인라인 주석을 한 Edit 으로 묶어 동시 제거. - Bash heredoc /
echo >우회 쓰기는 시도하지 않음(설계상 동일 차단 대상이며 우회는 규약 위반).
재발 방지 / 교훈
- shared-contract 의 어떤 Java 파일이든 다른 shared 패키지 import 가 있으면 이 훅으로 주석 편집이
막힌다 — comment-only 작업을 계획할 때 미리
grep -l '^import dev\.caskeleton' src/shared-contract/...로 차단 대상 파일을 식별하고, 그 파일은 README-only(코드 미편집)로 처리한다. InheritableThreadLocal을 설명해야 하는 코드(주석)는 코드에 리터럴을 두지 말고 README 에 둔다.- 훅 개선 후보(미적용, 제안만): G5 는
^import dev\.caskeleton\.shared\.(자기 모듈 prefix)를 예외 처리하면 intra-module 오탐이 사라진다. G7 은 사용(new InheritableThreadLocal/extends InheritableThreadLocal/<...>)만 매치하고 주석/{@code ...}언급은 통과시키면 오탐이 준다. 단 규칙 변경은 매트릭스/ArchUnit/훅 SSOT 정렬 필요 — 본 작업 범위 밖.
재발 인스턴스 — sample-portfolio (2026-06-19)
- 파일:
adapter/web/dto/request/SamplePolymorphicRequest.java - 차단: G7 — 클래스 JavaDoc 을 한 줄로 합치며
{@code ObjectMapper.enableDefaultTyping()}리터럴이 한 라인에 들어가자 write 차단(G7 금지 패턴 (CVE/가상스레드 안전)). 이 메서드 호출은 CVE-2019-14379 RCE 입구로, ArchUnitno_jackson_enable_default_typing_call의 대상 토큰. - 원본도 같은 토큰을 JavaDoc 에 갖고 있었으나 훅 도입 전 커밋이라 통과했을 뿐 — 위 G7 분석과 동일(사용 vs 언급 미구분).
- 대응: 소스 주석은 "Jackson's unsafe default-typing entry point (CVE-2019-14379)" 로 우회(리터럴 메서드명 제거), 정확한 메서드명은
sample-portfolio/README.md(.md, 게이트 비대상)에 보존. 규칙 변경·우회 쓰기 모두 거부.
관련
- 형제 작업의 다른 함정: raw/errors/ca-comment-to-readme-subagents-overstrip-runtime-strings — comment→README 작업이 런타임 문자열까지 손대는 behavior change. (이번 작업은 diff audit 으로 enum 값/문자열 리터럴 불변 확인 → 해당 함정은 회피.)