[BUG] Bash permission matcher misparses operators inside quoted arguments — a quoted | in a grep pattern defeats Bash(grep:*) and forces a prompt
Preflight Checklist
- [x] I searched existing issues — this is a deliberately minimal, isolated case within the permissions-matching cluster (#16561, #27688, #30519, #43713); see "Relationship to existing issues" below.
- [x] Single bug report.
- [x] Using the latest version.
What's wrong?
With a wildcard allow rule Bash(grep:*) in settings.local.json, a single grep command whose pattern contains a | (regex alternation) — e.g. grep -E "foo|bar" file.txt — still triggers a permission prompt, even though the rule should cover any grep invocation.
The permission matcher appears to split the command on shell control operators (|, ;, &&) to check each component against the allowlist (cf. #16561), but the split is not shell-quote-aware: the | inside the quoted regex is treated as a pipe operator. The command splits into fragments like grep -E "foo and bar", neither of which matches Bash(grep:*), so Claude Code falls back to prompting.
The same defect affects any allowlisted command with a control character inside a quoted argument, e.g.:
gh pr view 1 --jq '.[] | .body'— pipe inside a jq stringsed 's/a|b/c/' file— pipe inside a sed scriptawk '/x/{a} ; /y/{b}' file— semicolon inside an awk program
Steps to reproduce (minimal, macOS)
- In a project, set
.claude/settings.local.json:
``json``
{ "permissions": { "allow": ["Bash(grep:*)"] } }
- Use the default permission mode (not
bypassPermissions). - Have Claude run
grep -E "foo|bar" somefile.txt→ a permission prompt appears (the bug). - Have Claude run
grep -E "foobar" somefile.txt→ auto-approved (correct). - Have Claude run
grep -e foo -e bar somefile.txt→ auto-approved (correct).
The only difference between (3) and (4)/(5) is the literal | inside the quoted pattern.
Expected behavior
A single grep … command should match Bash(grep:*) regardless of characters inside its quoted arguments. Component-splitting for permission checks should honor shell quoting: a | / ; / && inside single or double quotes is data, not an operator. (Reference: Python's shlex(command, punctuation_chars=True) yields operators as their own tokens while leaving quoted text intact — a quoted | stays part of its token.)
Impact
- Frequent, needless permission prompts for very common commands (grep alternation; jq/awk/sed with operators in their scripts) despite explicit wildcard allow rules.
- Severe for headless / background / autonomous (Agent SDK) sessions: a permission prompt with no interactive approver is not a quick click — it becomes an indefinite hang. Teams are writing PreToolUse hooks to hard-deny such commands specifically to convert the hang into a fast failure — a workaround for this matcher gap.
Environment
- Claude Code 2.1.205
- macOS (darwin)
Relationship to existing issues
This is intentionally a single-command, non-compound reproducer to isolate the quote-awareness defect in the command splitter, distinct from:
- #16561 — "Parse compound Bash commands and match each component against permissions": the component-matching this bug is about; the split needs to be quote-aware.
- #27688 — bundles a genuinely compound command + quoted Windows path +
.exe+ Always-Allow persistence (several confounded factors). - #30519 / #43713 — broader permissions-matching problems.
Providing a minimal reproducer in the hope it helps triage the specific quote-awareness fix.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗