Claude Hookbook

Hooks / safety

Block destructive shell commands

Blocks rm -rf, disk tools, force pushes, and destructive git commands before they run.

PreToolUseBashblocking riskneeds manual check

What it does

Fires before Bash tool calls. The script scans the command for a deliberately narrow list of destructive patterns (rm -rf, mkfs, dd to devices, fork bombs, git reset --hard, git clean -f, force push) and denies the call with a reason. Narrow on purpose: a broad blocker cries wolf and gets disabled.

When it runs

EventPreToolUse
MatcherBash
Scopeproject or user
Platformsmacos, linux
Timeout10s
Requiresjq
Failure modeblock
Verified againstClaude Code hooks reference, event/matcher schema checked 2026-09-10
Test statusneeds manual check - run the test steps below before relying on it

Config

Merge this into .claude/settings.json (or ~/.claude/settings.json for user scope) - or use the generator to combine hooks.

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive-commands.sh",
            "timeout": 10
          }
        ],
        "matcher": "Bash"
      }
    ]
  }
}

Script

Save as .claude/hooks/block-destructive-commands.sh and chmod +x it.

#!/bin/bash
# block-destructive-commands: deny a narrow list of destructive commands.
set -u
cmd=$(jq -r '.tool_input.command // empty')
[ -z "$cmd" ] && exit 0
block_reason=""
case "$cmd" in
  *"rm -rf"*|*"rm -fr"*) block_reason="recursive force delete" ;;
  *"mkfs"*|*"dd if="*"/dev/"*) block_reason="disk-level operation" ;;
  *":(){"*) block_reason="fork bomb" ;;
  *"git reset --hard"*) block_reason="git reset --hard discards uncommitted work" ;;
  *"git clean -f"*) block_reason="git clean deletes untracked files" ;;
  *"push --force"*|*"push -f "*) block_reason="force push rewrites remote history" ;;
esac
if [ -n "$block_reason" ]; then
  jq -n --arg r "$block_reason" --arg c "$cmd" '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: ("Blocked by hook: " + $r + ". Command: " + $c)
    }
  }'
  exit 0
fi
exit 0

Install

  1. Save the script to .claude/hooks/block-destructive-commands.sh and chmod +x it.
  2. Add the config fragment to settings.json.

Test it

If it blocks something you wanted

The deny reason names the pattern that matched, so you can run the command yourself in a terminal if it was intentional. Adjust the case list to your taste - it is meant to be edited.

Sources

Related hooks

Protect .env and secret files

Blocks Claude from writing to .env files, keys, and credential files - unless you say otherwise.

PreToolUseEdit|Writeblocking riskneeds manual check

Block writes outside the repo

Stops Edit/Write calls whose target path resolves outside the project root.

PreToolUseEdit|Writeblocking riskneeds manual check

Pro Pack (later)

Framework bundles, cross-platform scripts, tested blocking policies, and a test harness. Free recipes and the generator stay free.

Waitlist opens soon.