---
title: "Claude Code: block dangerous commands"
url: https://perrotta.dev/2025/12/claude-code-block-dangerous-commands/
last_updated: 2026-07-29
---


The outcome:

```
> Run rm -rf /tmp/test

⏺ Bash(rm -rf /tmp/test)
  ⎿  PreToolUse:Bash hook returned blocking error
  ⎿  Error: rm -rf is blocked for safety

⏺ The rm -rf command was blocked by a safety hook in your configuration (~/.claude/hooks/block-dangerous-commands.sh).

  If you want to remove /tmp/test, you have a few options:

  1. Run the command directly in your terminal (outside of Claude Code)
  2. Modify your hooks configuration to allow this specific command
  3. Use a safer alternative like rm -r /tmp/test (without force flag) which may prompt for confirmation

  Would you like me to try one of the alternatives, or would you prefer to run this directly in your terminal?
```

How to achieve this?

So far I had a [deny
list](https://github.com/thiagowfx/.dotfiles/blob/8a61441dbe1adf5760544e0db6b4c64a145bb6e7/claude/.claude/settings.json#L63)
in `~/.claude/settings.json`:

```json
{
  "permissions": {
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(terraform apply:*)",
      "Bash(terraform destroy:*)"
    ]
  }
}
```

**This is enough**.

For extra safety though, I added a [pre-tool use
hook](https://code.claude.com/docs/en/hooks#pretooluse) for redundancy:

> Runs after Claude creates tool parameters and before processing the
tool call.

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/block-dangerous-commands.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}
```

The hook file (generated by Claude itself, needless to say):

```shell
#!/bin/bash
# PreToolUse hook to block dangerous commands like "rm -rf" and "terraform apply"

# Read hook input from stdin
input=$(cat)

# Extract tool name and command
tool_name=$(echo "$input" | jq -r '.tool_name // ""')
command=$(echo "$input" | jq -r '.tool_input.command // ""')

# Exit early if not a Bash tool
if [ "$tool_name" != "Bash" ]; then
    exit 0
fi

# Check for dangerous patterns
blocked=false
reason=""

# Check for rm -rf (with various flag orderings)
if echo "$command" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*f|(-[a-zA-Z]*f[a-zA-Z]*\s+)?-[a-zA-Z]*r|-rf|-fr)\b'; then
    blocked=true
    reason="rm -rf is blocked for safety"
fi

# Check for terraform apply (without -auto-approve is still dangerous)
if echo "$command" | grep -qE 'terraform\s+apply'; then
    blocked=true
    reason="terraform apply is blocked - use terraform plan first"
fi

# Check for terraform destroy
if echo "$command" | grep -qE 'terraform\s+destroy'; then
    blocked=true
    reason="terraform destroy is blocked for safety"
fi

# If blocked, return denial via JSON
if [ "$blocked" = true ]; then
    cat <<EOF
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "$reason"
  }
}
EOF
    exit 0
fi

# Allow command to proceed
exit 0
```

