Bash allow wildcards match options before subcommands
August 25, 2026
Bash allow wildcards match options before subcommands. Claude Code 2.1.246 started warning about that on 25 August 2026, with Bash(git * main) as the example, because the star was never a branch lock.
The rule looks like a seatbelt for main. Claude Code's permissions docs list git push origin main in the matches column, sitting next to git -c core.fsmonitor=<script> diff main. That second line is not a merge. It is git taking a config override and a hook pathname before it even picks a subcommand.
You'll keep the rule if the new yellow line feels like a nag. The nag is the product telling you the grant is still live.
The star sits where git options sit#

The permissions page says it in one sentence. Put the star after the subcommand.
In git log --oneline main, git is the program and log is the subcommand, the word that picks what the program does. Claude Code matches everything before the first * as written. Those words are the fence. Bash(git log *) is git log. Bash(git *) is every git command. Bash(git * main) is every git spelling that can land main at the end, with the star eating the middle.
git's own synopsis already put the options first. git [-c <name>=<value>] <command>. The -c sits in the hole you drew for a subcommand. The reserved sign on the bumper says main. It does not say log.
The docs table already named the scary row#
No scavenger hunt. The same permissions page publishes the row.
From the official wildcard table
| You write | Matches | Does not match |
|---|---|---|
Bash(git * main) | git merge main, git push origin main, git -c core.fsmonitor=<script> diff main | git log |
Bash(git log * main) | git log --oneline main, git log -5 main | git push origin main |
The matching-rule bullets under that table are the sentences that should have been the changelog.
- The
*stands in for whatever text is in its place, including the subcommand and every option before it - A trailing
*with a space also matches the bare command, soBash(ls *)matchesls - The space before a trailing
*is part of the rule, soBash(ls *)misseslsofandBash(ls*)does not
The first bullet is the one that names -c. In Bash(git * main) the star stands in for the subcommand, so Claude Code matches every git subcommand and every option before it. That includes -c, which makes git run a program you name.
The git documentation is calmer about the flag. -c passes a configuration parameter to the command. The value overrides configuration files. git-config then says the quiet part about core.fsmonitor. If the value is not the boolean true, the variable contains the pathname of the fsmonitor hook command. That hook is how git finds files that may have changed.
That is the whole receipt. The table already wrote <script>. Nobody needs a working sample, and this post is not going to ship one.
Denying git push does not close this#

The steel-man is reasonable. The same docs page shows deny Bash(git push *) next to allow Bash(git commit *). Rules evaluate deny, then ask, then allow. First match wins. Specificity does not reorder them.
So yes. A deny on git push blocks git push origin main even when the allow git * main would have matched it. That is not nothing.
It is also not the -c row. git -c core.fsmonitor=<script> diff main is not a push. Deny-push never sees it. You'll ship that deny, sleep fine, and still have a hole the table already named.
People will reach for the old command-injection story next, the one where a star ate &&. Claude Code now splits on &&, ||, ;, |, and newlines, and a rule has to match each subcommand. 2.1.246 even started requiring approval for a dangling &&. That class got a real fix. This class got a warning.
Always Allow trained the wrong muscle#

The permission dialog writes the space-separated form when you select Yes, and don't ask again for a command prefix. That is how Bash(git commit *) lands in .claude/settings.local.json. The same click trains a family grant. Bash(git * main) is that shape with the star sitting where a subcommand should sit.
GitHub issue 29187, filed 27 February 2026, is the same click going wider than the line you saw. Always Allow on gcloud scheduler jobs list offered gcloud scheduler:*, which also covers create and delete. The user wanted the list. The dialog offered the family.
GitHub issue 36900 is the other door. A tight Bash(git status:*) missed git -C path/to/the/thing status, because Claude kept inserting -C before the subcommand. The allow looked redundant. The instinct is to open the star until -C fits. git * is that instinct with the safety peeled off.
Same family as the JFrog Boost Always Allow writeup. First token, first click, bigger grant than the line on screen. The wrapper version stamped the binary. This version stamps the hole between git and main.
A warning that still matches is the cost of keeping it#

The Claude Code changelog for 2.1.246 is two facts, not one.
- It added a startup warning for Bash allow rules with a wildcard before the subcommand, example
Bash(git * main) - It did not strip those rules. Present tense. They also match options inserted before the subcommand
Claude Code already ignores a different broken shape, Bash(command:rm *), and warns at startup because that one was bypassable. If this pattern ever moves into that bucket, the post is wrong and the rule can stay until you rewrite it for taste. Until then, the yellow line is you keeping a grant the docs already enumerated.
Put the star after the subcommand. That is the example the permissions page already prints.
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)"
]
}
}The cost of the position is one extra prompt on git merge. Pay it. A warning on an open gate is not a lock.
Wildcard allow questions
Why does git -C path status miss a Bash(git status *) allow?
Claude often inserts -C before the subcommand. A rule that locks git status as the first two words never sees that line. GitHub issue 36900 is that miss in the reporter's own words. Widening to git * is how people then grant the option hole this post is about.
Does Yes don't ask again stay on the subcommand you just ran?
Not reliably. GitHub issue 29187 reported Always Allow on gcloud scheduler jobs list offering gcloud scheduler:*, which also covers create and delete. The Claude Code permissions docs say the dialog writes the space-separated prefix form. Treat that click as a family grant, not a blessing of the exact line.
If git push is already denied, is git star main still a problem?
Deny-first evaluation does block git push origin main even when an allow Bash(git * main) would have matched it. The official table's third match is git -c core.fsmonitor=<script> diff main, which is not a push. A push deny never sees that line.
Does the 2.1.246 warning turn the rule off?
No. The changelog calls it a startup warning, in present tense, because those rules also match options inserted before the subcommand. A different startup warning ignores Bash(command:rm *). This one still matches until you rewrite it.
