Claude Code에서 guard-edit hook을 만들어 민감 파일(.env, lock 파일 등)의 수정을 차단했다. 잘 동작하는 줄 알았는데, 일괄 자동화 작업 중에 두 가지 우회 경로를 발견했다.
Table of contents
Open Table of contents
문제 1: Write 도구로 Edit 보호 우회
상황
guard-edit hook은 PreToolUse:Edit 이벤트에 등록되어 있다. .env.example 같은 보호 대상 파일을 Edit 도구로 수정하면 정상적으로 차단된다.
# guard-edit.ps1 (간략화)
if ($FilePath -match '\.env($|\.)') {
Write-Output "BLOCK: .env 파일 직접 수정 금지."
exit 1
}
그런데 에이전트가 Edit 대신 Write 도구를 사용하면 이 hook이 동작하지 않는다. Write는 PreToolUse:Write 이벤트이고, Edit hook은 PreToolUse:Edit 이벤트만 감시하기 때문이다.
Edit .env.example → guard-edit 차단 ✓
Write .env.example → guard-edit 미감지 ✗ (우회)
해결
Write 이벤트에도 동일한 보호 로직을 적용한다. 하나의 스크립트를 두 이벤트에 등록하는 것이 가장 간단하다.
// settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"command": ".claude/hooks/guard-edit.ps1"
}
]
}
}
matcher에 Edit|Write를 지정하면, 두 도구 모두에서 동일한 검사가 실행된다.
문제 2: 일괄 작업에서 파일 오분류
상황
13개 Docker 프로젝트를 일괄 표준화하는 작업에서, 에이전트에게 “불필요한 파일을 정리하라”고 지시했다. 에이전트는 git status의 untracked 파일을 확인하고, 이를 “에이전트가 임의 생성한 파일”과 “의도적으로 생성한 파일”로 분류했다.
문제는 모든 untracked 파일을 “에이전트가 임의 생성한 파일”로 분류했다는 것이다. 실제로는 사용자가 의도적으로 생성한 파일(스크립트, 설정 파일)이 포함되어 있었다.
untracked files:
service-a/run-1.sh ← 사용자가 테스트용으로 만든 파일
service-b/custom.xml ← 사용자가 커스터마이징한 설정
database/backup.sh ← 사용자가 만든 백업 스크립트
에이전트는 이 파일들을 모두 삭제 대상으로 분류했다.
해결
자동화 작업에서 파일 삭제/정리 전에 반드시 확인 단계를 넣는다.
- 화이트리스트 방식: 삭제할 파일이 아니라, 유지할 파일을 지정
- 사전 목록 제시: 삭제 대상을 나열하고 사용자 확인 후 실행
- dry-run 선행: 실제 삭제 전에 “이 파일들을 삭제할 것이다” 출력
# 삭제 전 확인 패턴
$filesToDelete = @("file1", "file2", "file3")
Write-Host "삭제 예정 파일:"
$filesToDelete | ForEach-Object { Write-Host " $_" }
Write-Host "계속하시겠습니까? (y/n)"
교훈
도구 대칭성
Claude Code에서 파일을 수정하는 도구는 Edit와 Write 두 가지다. 하나만 차단하면 다른 하나로 우회된다. 보호 규칙은 행위 기준(파일 수정)으로 설계해야지, 도구 기준(Edit 차단)으로 설계하면 안 된다.
에이전트의 분류 한계
AI 에이전트는 파일의 “의도”를 모른다. run-1.sh가 사용자가 만든 것인지, 에이전트가 생성한 것인지 파일명만으로는 판단할 수 없다. 자동화된 정리 작업에서는:
- 삭제보다 보관 우선 — 확실하지 않으면 삭제하지 않는다
- 사용자 확인 필수 — 특히 untracked 파일은 git 히스토리가 없으므로 복구 불가
- 범위 제한 — “모든 불필요한 파일”이 아니라 “이 패턴의 파일만” 정리
hook 설계 체크리스트
- 모든 수정 경로를 커버하는가? — Edit뿐 아니라 Write, Bash의
echo >등 - 패턴이 충분히 구체적인가? —
.env만 잡으려다.environment도 잡히지 않는지 - 에이전트 자동화에서 테스트했는가? — 수동 사용과 에이전트 사용은 패턴이 다르다
- 우회 가능성을 검토했는가? — 차단하는 도구 외에 같은 결과를 내는 다른 도구가 있는지
핵심 정리
guard hook은 “에이전트가 실수하지 않도록” 돕는 도구이지, “에이전트를 신뢰하지 않는” 도구가 아니다. 그러나 hook의 커버리지가 불완전하면 오히려 잘못된 안전감을 준다. Edit만 차단했으니 안전하다고 생각했는데, Write로 우회되는 것을 모르고 있었다. 보호 규칙은 도구가 아니라 행위를 기준으로 설계해야 한다.
참고 자료
- Claude Code Settings — Claude Code 공식 설정 문서
- Claude Code Hooks Guide — Claude Code 공식 hooks 가이드