[BUG] Bash permission glob matching fails when command contains curly braces with spaces combined with pipes
Preflight Checklist
- [x] I have searched existing issues to make sure this isn't a duplicate
- [x] This is a single bug report (not multiple bugs in one issue)
- [x] I am using the latest version of Claude Code
What's Wrong?
Bash permission glob patterns (e.g. Bash(pnpm test:execute-action *)) fail to match commands that contain curly braces { } with spaces inside them when the command also includes a pipe |. The glob matcher appears to misparse the curly braces as brace expansion patterns rather than treating them as literal characters in the command string.
This means any command passing JSON arguments with standard formatting (spaces after colons/commas) cannot be auto-allowed when piped.
What Should Happen?
The glob * (or **) in a permission pattern should match the entire command string literally, regardless of whether it contains characters that have special meaning in glob syntax ({, }, ,). The command string is not a pattern — it's a literal string being tested against the pattern.
Steps to Reproduce
Permission rule in settings.local.json:
{
"permissions": {
"allow": [
"Bash(pnpm test:execute-action *)",
"Bash(tail *)"
]
}
}
Commands tested (all start with pnpm test:execute-action):
| Command suffix | Pipe | Auto-allowed? |
|---|---|---|
| --input '{"a":"b"}' -v | \| tail -5 | ✅ Yes |
| --input '{"a","b"}' -v | \| tail -5 | ✅ Yes |
| --input '{"a"}' -v | \| tail -5 | ✅ Yes |
| --input '{a b}' -v | \| tail -5 | ✅ Yes |
| -i x -a "y" | \| tail -5 | ✅ Yes |
| --input '{"a": "b"}' -v | \| tail -5 | ❌ Prompted |
| --input '{"a", "b"}' -v | \| tail -5 | ❌ Prompted |
| --input '{"a" "b"}' -v | \| tail -5 | ❌ Prompted |
| --input '{"a": "b"}' -v | (none) | ✅ Yes |
| --input '{"a" "b"}' -v | (none) | ✅ Yes |
Pattern: The match fails when ALL three conditions are met:
- Command contains
{...} - Content inside braces has a space separating double-quoted strings (e.g.
"a": "b","a", "b","a" "b") - Command includes a pipe
|
Remove any one of the three conditions and the match succeeds.
Root Cause Hypothesis
Curly braces are glob brace expansion characters ({a,b} matches a or b). When the glob matcher encounters content like {"key": "value"} with spaces inside the braces, it likely attempts brace expansion, fails or produces an unexpected expansion, and the overall match breaks. This only manifests when combined with a pipe — possibly because without the pipe, the * consumes the entire remaining string before the brace parser activates.
The command string should be treated as a literal — special glob characters in the string being matched (not the pattern) should not be interpreted.
Error Messages/Logs
No error — the command simply prompts for user confirmation instead of being auto-allowed by the permission rule.
Claude Model
Opus
Is this a regression?
I don't know
Claude Code Version
2.1.76
Platform
Claude Team subscription
Operating System
macOS
Terminal/Shell
Cursor (integrated terminal, zsh)
Additional Information
Changing the pattern to ** instead of * does not help — the issue is not about / matching (though * not matching / is a separate, known limitation). The ** variant has the same failure with braces + spaces + pipes.
Related issues (same class of bug — glob metacharacter interpretation of literal content):
- #34379 —
#character breaks glob matching - #27088 —
'-sequence breaks glob matching - #27688 — Compound commands with pipes and quoted paths never match stored permissions
- #13154 — Feature request for regex support in permission patterns
Practical impact: Any integration test command passing formatted JSON (with spaces after : or ,) cannot be piped to tail/head/grep without manual confirmation, even when both the base command and the pipe target are in the allow list.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗