Files
bokdev-sample/.claude/hooks/block-secrets.mjs
2620227 3252dfaa66 chore(harness): 비밀 파일 읽기 차단 가드 + 규약 문서화
- .claude/hooks/block-secrets.mjs: PreToolUse 훅으로 비밀값 파일 접근 하드 차단
  (Read/Edit/Write + Bash cat/less 우회까지). example 파일은 허용.
- .claude/settings.json: permissions.deny + 자주 쓰는 dev 명령 allow + 훅 연결
- CLAUDE.md: init 표준 헤더, 하네스 가드/아키텍처 섹션 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 08:01:46 +00:00

61 lines
1.8 KiB
JavaScript
Executable File

#!/usr/bin/env node
// PreToolUse 가드 — 비밀값 파일 접근을 하드 차단한다.
//
// 차단 대상: .project-env, .env, .env.*, ~/.env (LITELLM_KEY 등).
// - 허용: .project-env.example (참조용 예시는 OK)
// 적용 도구: Read/Edit/Write/NotebookEdit 의 경로, 그리고 Bash 명령 문자열(cat/less/grep 등 우회 포함).
//
// settings.json 의 permissions.deny 는 Read 도구만 막지만, 이 훅은 Bash 우회까지 막는다.
// 반환: permissionDecision=deny 면 도구 호출이 실행 전에 거부된다.
import { readFileSync } from "node:fs";
let input = {};
try {
input = JSON.parse(readFileSync(0, "utf8") || "{}");
} catch {
process.exit(0); // 입력 파싱 실패 시 통과(다른 가드에 위임).
}
const tool = input.tool_name || "";
const ti = input.tool_input || {};
// .project-env(단, .example 제외) 또는 .env / .env.* / ~/.env 를 가리키는 토큰.
const SECRET_RE =
/(?:^|[\s'"=(/])\.project-env(?!\.example)|(?:^|[\s'"=(/])\.env(?:\.[\w.-]+)?(?![\w])/;
let target = "";
switch (tool) {
case "Read":
case "Edit":
case "Write":
target = ti.file_path || "";
break;
case "NotebookEdit":
target = ti.notebook_path || "";
break;
case "Bash":
target = ti.command || "";
break;
default:
process.exit(0);
}
if (SECRET_RE.test(target)) {
process.stdout.write(
JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason:
"비밀값 파일(.project-env / .env / ~/.env)은 읽거나 수정할 수 없습니다. " +
"DB·MinIO·LiteLLM 자격증명은 런타임에 환경변수로만 주입됩니다($DATABASE_URL 등). " +
"키 구조가 궁금하면 .project-env.example 을 보세요.",
},
})
);
process.exit(0);
}
process.exit(0);