[BUG] A permissions.ask rule containing `:*` other than at the end is silently ignored on the Bash tool

Status Open
Reported on v2.1.224
Maintainer reply None cached
Activity 2 comments · opened Aug 8, 2026

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

  1. Put this in ~/.claude/settings.json:
{
  "permissions": {
    "defaultMode": "auto",
    "ask": [
      "Bash(git*push* :**)",
      "Bash(git*push* :refs*)"
    ]
  }
}
  1. Start a fresh session in a scratch git repository.
  2. 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.

  1. Observe that the prompt comes from Bash(git*push* :refs*).
  2. Remove Bash(git*push* :refs*), leaving only Bash(git*push* :**), and start another fresh session.
  3. Run the same command. It executes with no prompt at all.
  4. 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:

  1. Surface the existing validation. The diagnostic text is already written; a rule that fails it should be reported at startup and by claude doctor rather than dropped in silence.
  2. Make the Bash path agree with the PowerShell path and treat :* as a wildcard whenever it is not the rule's suffix.
  3. 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗