Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

Safety hooks for Claude Code

Three small PreToolUse guardrail hooks, extracted from a setup that runs Claude Code unattended (cron jobs, headless agents, multi-machine automation) every day. Each one exists because of a real incident — or a near miss.

Hook What it stops Decision
check-bash-safety.mjs Dangerous shell commands (rm -rf, sudo rm, git push --force, git reset --hard, DROP/TRUNCATE in SQL, destructive MCP calls like apply_migration, delete_branch, deploy_to_vercel, bulk email sends) ask — pops a confirmation with a plain-language explanation of the worst case
secret-guard.py Real secrets (API keys, JWTs, private key blocks) being hardcoded into files that will be committed to git deny — tells Claude to use an env-var reference instead; .env* files pass through
cmd-ascii-gate.py Non-ASCII bytes written into .cmd/.bat files — cmd.exe parses batch files in the ANSI codepage, so a CJK comment or a smart quote can literally get executed as a command fragment deny

Why hooks and not vigilance

An agent that runs 200 tool calls a day will eventually type the one command you'd never approve. Rules in a CLAUDE.md are suggestions; a PreToolUse hook is a mechanical gate that fires every time. The philosophy here:

  • ask, not deny, for commands — dangerous ≠ wrong. The gate makes you look at it, with a one-line explanation of what the blast radius is, then you decide.
  • deny for secrets and encoding hazards — there is no legitimate reason to hardcode a live API key into a tracked file, so those don't get a dialog.
  • Fail-open — if a hook crashes or the payload shape is unexpected, the operation is allowed. These are safety nets, not an availability risk. A broken hook should never brick your session.

Install

Hooks are registered in Claude Code's settings.json (official docs). Two placements, pick per hook:

Global (~/.claude/settings.json) — applies to every project. Best for check-bash-safety.mjs:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell|mcp__.*__(execute_sql|apply_migration|delete_branch|pause_project|deploy_to_vercel|send-email|send-batch-emails|send-broadcast|send-event)",
        "hooks": [
          { "type": "command", "command": "node /absolute/path/to/hooks/check-bash-safety.mjs" }
        ]
      }
    ]
  }
}

Per-project (<project>/.claude/settings.json) — relative paths resolve from the project root. Best for the two Python gates:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "python .claude/hooks/secret-guard.py" },
          { "type": "command", "command": "python .claude/hooks/cmd-ascii-gate.py" }
        ]
      }
    ]
  }
}

Both gates share the Write|Edit matcher and disambiguate internally (secret-guard skips .env* files; cmd-ascii-gate only acts on .cmd/.bat), so chaining them is safe.

Requirements: Node ≥ 18 for the .mjs hook, Python ≥ 3.9 for the .py hooks. No dependencies. Windows-friendly (stdin/stdout forced to UTF-8 to survive GBK consoles).

Maintenance caveat

For check-bash-safety.mjs, the MCP tool names appear twice: in the settings.json matcher (decides whether the hook fires at all) and in the MCP_DANGER_TOOLS list inside the script (decides ask vs allow). If you add a new dangerous MCP tool, update both.

How they work

Every PreToolUse hook receives the pending tool call as JSON on stdin and answers with a permissionDecision (allow / ask / deny) plus a human-readable reason on stdout. That's the whole contract — each script here is a single file with a pattern list and a verdict, readable in five minutes. Fork them, edit the pattern lists, make them yours.

More Claude Code tooling

License

MIT

About

Guardrail hooks for Claude Code: confirmation gate for dangerous shell/MCP commands (rm -rf, force-push, DROP TABLE, migrations), secret-hardcoding blocker, and an ASCII gate for .cmd files. Battle-tested on an unattended multi-machine setup.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages