17 people upvoted this bug and it has 18 comments. The ask permission list gets completely ignored when Bash is in the allow list.
You want: "auto-approve safe commands, confirm dangerous ones."
You get: everything auto-approved, no confirmation ever.
The Problem
{
"permissions": {
"allow": ["Bash(*)"],
"ask": ["Bash(rm *)"]
}
}
Expected: rm -rf / triggers a confirmation.
Actual: rm -rf / runs immediately. ask is silently ignored.
This has been open since August 2025. 17 upvotes, 18 comments. No fix.
The Workaround: PreToolUse Hooks
Hooks operate independently of the permission system. A PreToolUse hook runs after permissions are resolved but before the tool executes. Exit code 2 = hard block.
{
"permissions": {
"allow": ["Bash(*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/destructive-guard.sh" }]
}
]
}
}
The hook reads the command from stdin JSON and blocks dangerous patterns:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Block rm on sensitive paths
if echo "$COMMAND" | grep -qE 'rm\s+(-[rf]+\s+)*(\/|~|\.\./)'; then
echo "BLOCKED: rm on sensitive path" >&2
exit 2
fi
# Block git reset --hard
if echo "$COMMAND" | grep -qE '^\s*git\s+reset\s+--hard'; then
echo "BLOCKED: git reset --hard" >&2
exit 2
fi
# Block force push
if echo "$COMMAND" | grep -qE 'git\s+push\s+.*(-f\b|--force\b)'; then
echo "BLOCKED: force push" >&2
exit 2
fi
# Block git add .env
if echo "$COMMAND" | grep -qiE 'git\s+add\s+.*\.env'; then
echo "BLOCKED: git add .env" >&2
exit 2
fi
exit 0
Why This Is Better Than ask
The permission system has other issues too. Trailing wildcards in compound patterns like Bash(ssh * uptime *) don't match when the command has no arguments — ssh host uptime fails but ssh host uptime -s passes (#36873). Hooks use proper regex and don't have this problem.
-
Hooks can't be overridden by other permissions.
allowdoesn't cancel them. - They can inspect the full command. Not just pattern matching on the tool name.
- They can check system state. Mount points, file existence, git branch.
- Exit code 2 is a hard block. Claude can't override it.
One Command Setup
npx cc-safe-setup
Installs 8 production-tested hooks covering destructive commands, force push, .env leaks, syntax errors, and context window monitoring. 116 automated tests. GitHub
This pattern — allow for speed, hooks for safety — is what ask should have been. Until the permission system is fixed, hooks are the way.
Full autonomous setup: Claude Code Ops Kit — 16 hooks + 5 templates + 3 tools.
🛠 Free: claude-code-hooks — 16 production hooks, open source.
Top comments (0)