Files
llm-wiki/raw/errors/ca-tmpl-import-gate-false-positive-shared-contract.md
T

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
hooks
write-gate
false-positive
clean-architecture
shared-contract
refactoring
comment-cleanup
ca-skeleton
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건

  1. 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 를 막지 않음).
  2. 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 입구로, ArchUnit no_jackson_enable_default_typing_call 의 대상 토큰.
  • 원본도 같은 토큰을 JavaDoc 에 갖고 있었으나 훅 도입 전 커밋이라 통과했을 뿐 — 위 G7 분석과 동일(사용 vs 언급 미구분).
  • 대응: 소스 주석은 "Jackson's unsafe default-typing entry point (CVE-2019-14379)" 로 우회(리터럴 메서드명 제거), 정확한 메서드명은 sample-portfolio/README.md(.md, 게이트 비대상)에 보존. 규칙 변경·우회 쓰기 모두 거부.

관련