Files
claudekit/scripts/guard-sensitive-files.cjs
T

53 lines
1.6 KiB
JavaScript

#!/usr/bin/env node
/**
* PreToolUse hook (matcher: Write|Edit): blocks edits to sensitive files —
* env files, private key material, credential dotfiles.
* Exit 0 = allow, Exit 2 = block.
* Fails open on errors (exit 0) so a hook bug never stalls the session.
*
* Template env files (.env.example / .env.sample / .env.template) stay
* editable — they exist to be written.
*/
"use strict";
const ALLOWED = [/\.env\.(example|sample|template)$/i];
const SENSITIVE_PATTERNS = [
{ name: "env file", re: /(^|\/)\.env(\.[^/]+)?$/i },
{ name: "key material", re: /\.(pem|key|p12|pfx)$/i },
{ name: "SSH private key", re: /(^|\/)id_(rsa|ed25519|ecdsa|dsa)[^/]*$/i },
{ name: "credential file", re: /(^|\/)\.(netrc|npmrc|git-credentials)$/i },
{ name: "AWS credentials", re: /(^|\/)\.aws\/credentials$/i },
];
async function main() {
try {
let data = "";
for await (const chunk of process.stdin) data += chunk;
const input = JSON.parse(data);
const raw = input?.tool_input?.file_path ?? "";
// Normalize Windows separators so path-anchored patterns hold.
const filePath = (typeof raw === "string" ? raw : "").replace(/\\/g, "/");
if (!filePath) process.exit(0);
if (ALLOWED.some((re) => re.test(filePath))) process.exit(0);
for (const { name, re } of SENSITIVE_PATTERNS) {
if (re.test(filePath)) {
console.error(
`BLOCKED: "${filePath}" is a ${name} — edit it manually, outside the agent session.`
);
process.exit(2);
}
}
process.exit(0);
} catch {
// Fail open — never block on hook errors
process.exit(0);
}
}
main();