PreToolUse hook permissionDecision 'ask' silently overrides permissions.deny rules
Summary
A PreToolUse hook returning permissionDecision: "ask" silently disables permissions.deny rules in settings.json, allowing denied commands to execute without any prompt. This is a security bypass — deny rules should always take precedence over hook "ask" decisions.
Severity
Critical — a hook can silently override security deny rules. Any PreToolUse hook that returns "ask" for a command that matches a deny rule will cause the deny rule to be ignored entirely. The command executes without even prompting the user.
Reproduction
Prerequisites
- Claude Code v2.1.84
- A
settings.jsonwith a deny rule and a PreToolUse hook
Step 1: Baseline — deny rule only (no hook)
settings.json:
{
"permissions": {
"deny": ["Bash(printf REPRO42*)"]
}
}
Start a new session, ask Claude to run printf REPRO42.
Result: DENIED ✅ — deny rule works correctly.
Step 2: Bug — deny rule + hook returning "ask"
Add a PreToolUse hook that returns permissionDecision: "ask" for the same command:
Hook script (/tmp/repro-hook.sh):
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
[ "$TOOL_NAME" != "Bash" ] && exit 0
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
case "$CMD" in
printf\ REPRO42*)
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"repro test"}}'
exit 0
;;
esac
exit 0
settings.json:
{
"permissions": {
"deny": ["Bash(printf REPRO42*)"]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash /tmp/repro-hook.sh"
}
]
}
]
}
}
Start a new session, ask Claude to run printf REPRO42.
Result: EXECUTED ❌ — prints REPRO42. The deny rule is completely bypassed.
Step 3: Control — hook returning "deny" works
Change the hook to return "deny" instead of "ask".
Result: DENIED ✅ — confirms the hook is running and "deny" works.
Expected behavior
When a command matches a permissions.deny rule, it should always be denied regardless of what any hook returns. The evaluation order should be:
- Check
permissions.deny— if matched, deny (non-overridable) - Run hooks — apply hook decisions
- Check
permissions.allow/permissions.ask
A hook returning "ask" should only be able to escalate an otherwise-allowed command to prompt — it should never be able to downgrade a denied command.
Actual behavior
Hook permissionDecision: "ask" overrides the deny rule entirely. The command executes without any prompt or denial.
Environment
- Claude Code v2.1.84
- macOS Darwin 25.2.0
- Model: claude-opus-4-6 (1M context)
Related
- #18312, #22018, #36059, #37420
4 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Important bug. As a workaround, implement your deny rules as hooks instead of (or in addition to) permissions.deny:
exit 2from a PreToolUse hook is a hard block that cannot be overridden by any other hook or permission rule. This gives you a deny-rule mechanism that is immune to the ask-override bug.Place this hook FIRST in the PreToolUse array to ensure it runs before any hook that might return "ask" and inadvertently override your deny rules.
This was fixed in v2.1.101 — A PreToolUse hook returning permissionDecision 'ask' no longer overrides explicit
permissions.denyrules. If you're still seeing this in the latest version, please comment with your version and repro and we'll reopen.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.