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>
This commit is contained in:
60
.claude/hooks/block-secrets.mjs
Executable file
60
.claude/hooks/block-secrets.mjs
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/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);
|
||||
35
.claude/settings.json
Normal file
35
.claude/settings.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
||||
"permissions": {
|
||||
"deny": [
|
||||
"Read(.project-env)",
|
||||
"Read(.env)",
|
||||
"Read(.env.*)",
|
||||
"Read(//home/coder/.env)",
|
||||
"Read(~/.env)"
|
||||
],
|
||||
"allow": [
|
||||
"Bash(npm install)",
|
||||
"Bash(npm run dev:*)",
|
||||
"Bash(npm run start:*)",
|
||||
"Bash(npm run db:check:*)",
|
||||
"Bash(npm run minio:check:*)",
|
||||
"Bash(npm run files:check:*)",
|
||||
"Bash(podman build:*)",
|
||||
"Bash(podman compose:*)"
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Read|Edit|Write|NotebookEdit|Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/block-secrets.mjs\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user