PreToolUse hook permissionDecision 'ask' silently overrides permissions.deny rules

Status Fixed / completed
Maintainer reply ✓ Yes — ashwin-ant
Activity 4 comments · opened Mar 26, 2026 · closed Apr 18, 2026
💡 Likely answer: A maintainer (ashwin-ant, collaborator) responded on this thread — see the highlighted reply below.

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.json with 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:

  1. Check permissions.deny — if matched, deny (non-overridable)
  2. Run hooks — apply hook decisions
  3. 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

View original on GitHub ↗

4 Comments

github-actions[bot] · 6 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/18312
  2. https://github.com/anthropics/claude-code/issues/22018

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 6 months ago

Important bug. As a workaround, implement your deny rules as hooks instead of (or in addition to) permissions.deny:

CMD=$(cat | jq -r ".tool_input.command // empty" 2>/dev/null)
[ -z "$CMD" ] && exit 0
DENIED=(
    "rm -rf /"
    "git push --force"
    "git reset --hard"
    "DROP TABLE"
)
for d in "${DENIED[@]}"; do
    if echo "$CMD" | grep -qF "$d"; then
        echo "BLOCKED: Denied command: $d" >&2
        exit 2
    fi
done
exit 0

exit 2 from 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.

ashwin-ant collaborator · 6 months ago

This was fixed in v2.1.101 — A PreToolUse hook returning permissionDecision 'ask' no longer overrides explicit permissions.deny rules. If you're still seeing this in the latest version, please comment with your version and repro and we'll reopen.

github-actions[bot] · 5 months ago

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.