[BUG] A permissions.ask rule containing `:*` other than at the end is silently ignored on the Bash tool
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.224)
What's Wrong?
A Bash(...) permission rule whose content contains the two-character sequence :* anywhere other than as its final two characters never matches anything. There is no error, no warning, and no diagnostic — the rule sits in settings.json looking like a working guard, the file parses, and every other rule in the same list keeps working. claude doctor reports nothing.
The same rule content under PowerShell(...) matches as expected, so the two tool paths disagree.
This is a silent failure of a security control. A rule that is present and inert is worse than a missing one, because the list is what gets audited.
What Should Happen?
Either the rule matches (as the PowerShell path already does), or loading it produces a visible error naming the problem. The binary already contains the diagnostic strings for exactly this case — The :* pattern must be at the end and Move :* to the end for prefix matching, or use * for wildcard matching — but they never reach the user for a rule loaded from settings.json.
Steps to Reproduce
- Put this in
~/.claude/settings.json:
{
"permissions": {
"defaultMode": "auto",
"ask": [
"Bash(git*push* :**)",
"Bash(git*push* :refs*)"
]
}
}
- Start a fresh session in a scratch git repository.
- Through the Bash tool, run:
git push origin :refs/tags/some-tag
Both rules describe this command — git*push* :** because * matches any run of characters, git*push* :refs* literally.
- Observe that the prompt comes from
Bash(git*push* :refs*). - Remove
Bash(git*push* :refs*), leaving onlyBash(git*push* :**), and start another fresh session. - Run the same command. It executes with no prompt at all.
- Repeat steps 1–6 with
PowerShell(git*push* :**)and the PowerShell tool. There it does prompt.
Isolation
The colon is not the problem and ** is not the problem — only a * placed directly after a :. All of these fire as expected on the Bash path:
Bash(git*push* :refs*)
Bash(git*push* :m**)
Bash(git*push* :z*)
Only Bash(git*push* :**) is inert.
Impact
There is no way to express "an argument beginning with a colon" as a Bash wildcard rule. * cannot follow the colon, and the rule syntax has no single-character wildcard and no character classes — ?, [, { and | are all escaped to literals by the matcher.
Concretely: a remote branch deletion spelled git push origin :some-branch cannot be guarded on the Bash tool at all, while the --delete and -d spellings of the same operation can. Anyone who writes the colon rule, sees it accepted, and moves on is unprotected and has no way to notice.
Suggested Fix
In order of preference:
- Surface the existing validation. The diagnostic text is already written; a rule that fails it should be reported at startup and by
claude doctorrather than dropped in silence. - Make the Bash path agree with the PowerShell path and treat
:*as a wildcard whenever it is not the rule's suffix. - Failing both, document that
:*is reserved anywhere in a rule, so the limitation is discoverable before it is relied on.
Even (3) alone would have prevented this, but (1) is what makes the whole class safe — this cannot be the only rule shape that fails validation quietly.
Environment
- Claude Code 2.1.224, native build
- Windows 11 Pro 26200
- Reproduced through both the Bash and the PowerShell tool, in fresh sessions
How this was found
Not by reading the list — by running the command in a nested claude -p session and reading the permission_denials array in its result object, against a counterfactual run with the rule removed. From inside a session the defect is invisible: a tool result is byte-identical whether no prompt was raised or one was raised and approved.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗