Write-time secret detection
No API key ever lands in a file
Scans the content being written (Write) or inserted (Edit new_string) for API keys, GitHub tokens, private keys and password assignments, and blocks the call before the secret ever lands on disk. Closes the file-write gap left by bash-level secret detection: an agent pasting a key into source code or a config file is stopped with a pointer to environment variables instead.
What does the Write-time secret detection hook do?
Write-time secret detection is a Claude Code PreToolUse hook matching Write|Edit. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. No API key ever lands in a file.
As a PreToolUse hook it runs before the action completes, so it can block or adjust what Claude is about to do. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Blocking an agent from hardcoding an API key into source code
- Stopping .env values from being copied into committed files
- Preventing private keys from being pasted into the repo
Tags
settings.json fragment
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/pre-write-secret-detection.mjs",
"type": "command"
}
],
"matcher": "Write|Edit"
}
]
}
}Script · .claude/hooks/pre-write-secret-detection.mjs
#!/usr/bin/env node
// @hookstack pre-write-secret-detection
// Bloque les écritures de fichiers contenant des secrets potentiels (PreToolUse Write|Edit)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const SECRET_PATTERNS = [
/(?:ANTHROPIC|OPENAI|CLAUDE|GEMINI|GROQ)_API_KEY\s*=\s*['"]?\S{20,}/i,
/sk-(?:ant-|proj-)?[a-zA-Z0-9_-]{32,}/,
/ghp_[a-zA-Z0-9]{36}/,
/-----BEGIN (?:RSA |EC )?PRIVATE KEY/,
/(?:password|passwd|secret|token)\s*=\s*['"][^'"]{6,}/i,
];
export function run(input) {
const content =
input.tool_input?.content ?? input.tool_input?.new_string ?? "";
if (!content) return null;
const match = SECRET_PATTERNS.find((p) => p.test(content));
if (!match) return null;
return {
decision: "block",
reason:
"[secret-detection] Potential secret in the content being written. Reference it via an environment variable or .env (gitignored) instead of hardcoding it.",
};
}
/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
const result = run(input);
if (result) process.stdout.write(JSON.stringify(result));
}
Learn more
Related hooks
- Secret detection before Bash executionCatch a leaked API key before it ever runs
- Destructive command blockingStops a disk-wiping shell command before it runs
- Sensitive file write protectionYour .env and keys stay untouched by the agent
- Lock file write protectionLock files stay intact - package manager only