[BUG] ask list is ignored when "Bash" is in allow list

Open 💬 22 comments Opened Aug 25, 2025 by orpheuslummis

Environment

  • Platform (select one):
  • [X] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • Claude CLI version: Latest (as of 2025-08-25)
  • Operating System: Linux
  • Terminal: Terminal App

Bug Description

The ask list in permissions is completely ignored when "Bash" is in the allow list, making it impossible to implement a "allow all commands except require confirmation for destructive ones" permission model at the user level.

Steps to Reproduce

  1. Set user-level configuration in ~/.claude/settings.json:

``json
{
"permissions": {
"allow": ["Bash"],
"ask": ["Bash(rm *)", "Bash(git push*)", "Bash(git branch*-D*)"]
}
}
``

  1. Ensure no project-level overrides exist (empty or no .claude/settings.json in project)
  2. Restart Claude Code completely
  3. Run: touch test.txt && rm test.txt

Expected Behavior

The rm command should trigger a confirmation prompt because it matches the "Bash(rm *)" pattern in the ask list, despite "Bash" being in the allow list.

Actual Behavior

The file is deleted immediately without any confirmation prompt. All commands matching patterns in the ask list execute without prompting.

Additional Context

Goal: Single user-level configuration for "bypass all permissions except blacklisted destructive commands" across all projects.

Impact: No way to achieve both convenience (no prompts for safe commands) and safety (protection against destructive commands).

Workarounds attempted (none work):

  • defaultMode: "bypassPermissions" - Bypasses everything including ask list
  • defaultMode: "default" - Prompts for every new command type

View original on GitHub ↗

22 Comments

github-actions[bot] · 10 months ago

---

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/6413
  2. https://github.com/anthropics/claude-code/issues/4364
  3. https://github.com/anthropics/claude-code/issues/6128

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

---

cwardgar · 10 months ago

I'm encountering this as well. I have the following settings.json:

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash",
      "Edit",
      "MultiEdit",
      "NotebookEdit",
      "WebFetch",
      "WebSearch",
      "Write"
    ],
    "ask": [
      "Bash(git commit:*)",
      "Bash(git push --force:*)",
      "Bash(git push -f:*)",
      "Bash(git reset --hard:*)",
      "Bash(git clean -f:*)",
      "Bash(git clean -d:*)",
      "Bash(git rebase:*)",
      "Bash(git merge:*)",
      "Bash(git branch -D:*)",
      "Bash(git branch -d:*)",
      "Bash(git branch --delete:*)",
      "Bash(git checkout --:*)",
      "Bash(git restore:*)",
      "Bash(git stash drop:*)",
      "Bash(git stash clear)",
      "Bash(git filter-branch:*)",
      "Bash(git filter-repo:*)",
      "Bash(git push --delete:*)",
      "Bash(git tag -d:*)",
      "Bash(git tag --delete:*)",
      "Bash(git remote add:*)",
      "Bash(git remote set-url:*)",
      "Bash(git remote remove:*)",
      "Bash(git config:*)",
      "Bash(rm -rf:*)",
      "Bash(rm -r:*)"
    ],
    "deny": [
      "Read(.env)"
    ]
  }
}
  • I run mkdir -p foo && touch foo/bar.txt in the terminal.
  • I then start Claude Code and tell it: "Remove the foo directory and all of its contents using rm".
  • Claude then blithely executes Bash(rm -rf foo), with no prompt appearing, despite the command matching my last two "ask" rules.
  • The permissions documentation states "Ask rules take precedence over allow rules." That's not the behavior demonstrated.
Nxt3 · 9 months ago

Same thing:

  "permissions": {
    "allow": [
      "Bash",
      "Edit",
      "Glob",
      "Grep",
      "Read",
      "Task",
      "TodoWrite",
      "Write"
    ],
    "deny": [
      "Bash(curl :*)",
    ]
  },

but it will still run curl commands if I ask it to.

We are essentially asking for "allow all Bash commands except some" and that isn't currently being honored.

github-actions[bot] · 7 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

Nxt3 · 7 months ago

Still a problem.

FANTASYSSL · 6 months ago

Still a problem.

amosjyng · 6 months ago

Still a problem. Has anyone ever seen the Claude team fix any of these issues?

jgato · 5 months ago
jackaldenryan · 5 months ago

Also facing this issue. Want to require asking before recursive removal just to be safe

lostbean · 5 months ago

Same here. I just want to confirm rm -rf

1mono2 · 5 months ago

+1

zacharypodbela · 5 months ago

I'm on MacOS using Claude Code CLI and am also facing this issue.

  • Version: 2.1.29 (Claude Code)
  • Installed via the https://claude.ai/install.sh method
matijaoe · 5 months ago

For the love of god please fix this

drmercer · 4 months ago

As a workaround, I think a PreToolUse or PermissionRequest hook could acheive this kind of behavior. You'd remove Bash from your allow list in settings.json, and then make the hook decide to allow things as long as they don't match any of your "ask" rules.

orpheuslummis · 4 months ago

Another approach is "LLM as guardrail": a PreToolUse prompt hook sends every Bash command to a (fast) model judging whether the command would have negative side effects. If negative side effects are predicted, user confirmation is needed. This can be used along a block list, but would be more general.

davidegreenwald · 4 months ago

+1

Aureliaar · 4 months ago

+1.
Simplest possible repro:
Settings (.claude/settings.json)
```json
{
"permissions": {
"allow": ["Bash(*)"],
"ask": ["Bash(echo*)"]
}
}

Steps to reproduce

  1. Create the above settings.json in project root
  2. Ask Claude Code to run: echo "hello world"
  3. Observe: command executes without prompting
yurukusa · 3 months ago

The permission system's allow + ask interaction has been a pain point for a while. Here's a pattern that works today using PreToolUse hooks to get exactly the behavior described (allow all bash, block specific dangerous commands):

settings.json:

{
  "permissions": {
    "allow": ["Bash(*)"]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "~/.claude/hooks/destructive-guard.sh" }]
      }
    ]
  }
}

destructive-guard.sh reads the command from stdin JSON, checks against patterns like dangerous rm invocations, git reset --hard, git clean -fd, and exits with code 2 (hard block) when matched. Exit 0 = allow.

Minimal example:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Block dangerous rm commands on sensitive paths
if echo "$COMMAND" | grep -qE 'rm\s+(-[rf]+\s+)*(\/\s|\/home|~)'; 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

exit 0

This gives you "allow everything except these specific dangerous commands" — which is what ask should have done but doesn't when combined with allow.

The hook approach is actually more flexible than the permission system because you can add mount-point detection (prevent rm on paths with NFS mounts), force-push blocking, .env staging prevention, etc.

npx cc-safe-setup installs a production-tested set of these guards if you want a ready-to-go solution.

yurukusa · 3 months ago

The ask list being ignored when Bash is in allow is a known interaction bug. A hook gives you precise control:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
BASE=$(echo "$COMMAND" | sed 's/^\s*//' | awk '{print $1}')
case "$BASE" in
    ls|cat|head|tail|grep|find|echo|pwd|git)
        jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Allowed"}}'
        exit 0
        ;;
esac
case "$BASE" in
    rm|mv|cp|chmod|chown|docker|kubectl)
        exit 0
        ;;
esac
case "$BASE" in
    sudo|shutdown|reboot)
        echo "BLOCKED: $BASE is denied." >&2
        exit 2
        ;;
esac
exit 0
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/ask-override.sh" }]
    }]
  }
}

Three tiers:

  • Allow: ls, cat, grep, git — auto-approved via permissionDecision: "allow"
  • Ask: rm, mv, docker — hook exits silently, normal prompt appears
  • Deny: sudo, shutdownexit 2 blocks unconditionally

This replaces the broken allow/ask/deny interaction with reliable hook-based logic.

jbert50021 · 2 months ago

Same root cause, deny variant (more safety-critical): if you have allow: ["Bash(git:*)"] and deny: ["Bash(git
status)"], the deny is completely ignored — git status runs without any prompt. Removing the broader allow rule from
the allow list restores deny behavior.

Repro (Windows, Claude Code desktop, PowerShell/bash):

{
"permissions": {
"allow": ["Bash(git:*)"],
"deny": ["Bash(git status)"]
}
}

Run git status — executes freely, no prompt, deny rule ignored. Remove "Bash(git:*)" from allow, run again — now
correctly blocked/prompted.

The deny case matters more than ask: documentation says deny wins over allow unconditionally. If allow beats deny,
there's no safe way to build an allowlist with carve-outs for dangerous commands. Verified on 2026-05-10, Claude Code
Windows desktop app.

jbert50021 · 2 months ago

Claude code wrote these - btw...

To add to my previous comment: I want to flag the security severity of the deny bypass specifically.

The documented contract is that deny is an unconditional block — the docs state "deny wins over allow." Users
reasonably rely on this to protect against catastrophic commands. A typical real-world setup:

{
"permissions": {
"allow": ["Bash"],
"deny": [
"Bash(rm -rf *)",
"Bash(DROP TABLE*)",
"Bash(DROP DATABASE*)"
]
}
}

A user who reads the docs and configures this believes they've protected their database. They haven't — the deny rules
are silently ignored because Bash is in allow. Claude Code will run DROP DATABASE mydb without any prompt or warning.

This isn't a UX issue — it's a security bypass. The permission system is advertising safety guarantees it doesn't
deliver, which is worse than having no safety guarantee at all (because at least then users would know to rely on
something else).

Workarounds via PreToolUse hooks exist (see comments above) but require bash scripting skills and aren't discoverable
by users who trusted the documented behavior. The fix needs to be in the core permission system: deny must win over
allow, unconditionally, exactly as documented.

I will also send this to security@anthropic.com for triage as a security issue rather than a feature gap.

nemhods · 1 month ago

@jbert50021 did you end up contacting the security team?
From my research, an allow Bash(*) by the user even works when allowManagedPermissionRulesOnly is set. From my point of view, allowManagedPermissionRulesOnly is useless at the moment and there is no way for an organization to control user-side permissions besides hooks.