Deny permission rules not blocking commands, falling through to ask
Resolved 💬 3 comments Opened Feb 22, 2026 by michaelmcc-mf Closed Feb 25, 2026
Description
Deny rules in ~/.claude/settings.json are not enforced. Commands that match a deny pattern are not blocked — instead they fall through to the ask confirmation prompt.
Configuration
~/.claude/settings.json:
{
"permissions": {
"deny": [
"Bash(ls *)",
"Bash(ls)",
"Bash(cat *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(find *)",
"Bash(grep *)",
"Bash(rg *)",
"Bash(sed *)",
"Bash(awk *)"
],
"allow": [
"Read",
"Glob",
"Grep",
...
],
"ask": [
"Bash(*)",
"Edit(*)",
"Write(*)",
"NotebookEdit(*)"
]
}
}
Steps to reproduce
- Add
Bash(ls *)andBash(ls)to the deny list in~/.claude/settings.json - Add
Bash(*)to the ask list - Start a Claude Code session
- Verify rules are loaded via
/permissions— deny tab showsBash(ls *)andBash(ls)correctly - Have Claude attempt to run
ls -d /some/path/*/
Expected behavior
The command is hard-blocked immediately. No confirmation prompt is shown.
Actual behavior
The command is not blocked. Instead, a confirmation prompt is shown (ask behavior), as if the deny rule does not exist:
Bash command
ls -d /Users/.../mcp-servers/*/
List subdirectories
Permission rule Bash requires confirmation for this command.
Do you want to proceed?
Analysis
- The deny rules are correctly loaded and visible in
/permissions - Per the docs, deny rules evaluate first: "Rules are evaluated in order: deny -> ask -> allow. The first matching rule wins, so deny rules always take precedence."
- The glob pattern
ls *should matchls -d /some/path/*/ - The deny rule is being skipped and the generic
Bash(*)ask rule is matching instead
Environment
- macOS (Darwin 25.3.0)
- Claude Code with Claude Opus 4.6
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗