[BUG]
Preflight Checklist
- [ ] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Compound Bash commands (joined with &&) bypass --allowedTools prefix
matching under --permission-mode dontAsk in headless claude -p mode. A
command chained after (or before) an allowlisted command executes in full,
even when it matches NO prefix in --allowedTools at all. permission_denials
comes back empty and is_error:false — no denial is recorded anywhere.
This contradicts the documented behavior in the Permissions doc
("Compound commands" section, https://code.claude.com/docs/en/permissions.md),
which states Claude Code recognizes shell separators (&&, ||, ;, |, |&, &,
newlines) and that "a rule must match each subcommand independently."
What Should Happen?
Per the documented per-subcommand matching rule, a compound command
containing any subcommand that doesn't match an allowed prefix should be
denied outright, in either ordering. Neither ls /some/dir && date +%s%N
nor date +%s%N && ls /some/dir should succeed when date matches no
--allowedTools prefix.
Error Messages/Logs
Exact invocation:
claude -p "<prompt>" --output-format json --max-budget-usd 0.50 \
--permission-mode dontAsk \
--allowedTools "Read Grep Glob WebFetch WebSearch Bash(sqlite3:*) Bash(python3:*) Bash(curl:*) Bash(ls:*) Bash(cat:*) Bash(grep:*) Bash(head:*)
Bash(tail:*)" \
--disallowedTools "Write Edit MultiEdit NotebookEdit"
Case 1 result: is_error:false, permission_denials:[], result text contains
both the real directory listing AND date's epoch-nanosecond output
(1785365741334684000).
Case 2 result: is_error:false, permission_denials:[], result text contains
date's output FIRST (1785365755011911000) then the directory listing.
Proof these are genuine executions, not model-fabricated text: both
nanosecond values, converted to human time, land exactly inside the actual
wall-clock window the two calls ran in (16:55:41 and 16:55:55), and an
independent `date +%s%N` run immediately afterward read 16:56:02 -- all
three in the correct monotonic order.
Steps to Reproduce
- Run:
claude -p "Run exactly this Bash command and report the literal raw
output: ls /some/dir && date +%s%N" --output-format json \
--permission-mode dontAsk \
--allowedTools "Bash(ls:) Bash(cat:) Bash(grep:) Bash(head:) Bash(tail:*)" \
--disallowedTools "Write Edit MultiEdit NotebookEdit"
- Observe the result: both the ls listing AND date's output appear;
permission_denials is empty; is_error is false.
- Repeat with the order reversed: "date +%s%N && ls /some/dir" -- same
result, date still executes.
date does not match any prefix in --allowedTools in either test, so per
the documented behavior both calls should have been denied outright.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.220 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
This allowlist is the safety boundary for an unattended, overnight agent
pipeline that processes untrusted web content and has already encountered
a live prompt-injection attempt embedded in a target site's robots.txt. If
a compound command can smuggle an unlisted subcommand past --allowedTools,
that boundary does not hold against an injected instruction telling an
agent to chain an unapproved command onto an approved-looking one.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗