anthropics / anthropics/claude-code

Bash permission allow-rule doesn't split on ;/&& before matching (contrary to docs) — compound-command smuggling bypasses scoped allowlist

Open
#94,314 2 comments 0 reactions 0 assignees View on GitHub
area:bash area:permissions area:security bug has repro
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Bash permission allow-rule does not split on `;`/`&&` before matching, contrary to docs

**CLI version tested:** `2.1.183` and `2.1.236` (both, identical result — not a stale-version issue)

### What the docs say

https://code.claude.com/docs/en/permissions, "Compound commands" section, states:

> Claude Code is aware of shell operators, so a rule like `Bash(safe-cmd *)` won't give it
> permission to run the command `safe-cmd && other-cmd`. The recognized command separators are
> `&&`, `||`, `;`, `|`, `|&`, `&`, and newlines. A rule must match each subcommand independently.

### What actually happens

With `--allowedTools "Read,Grep,Glob,Bash(git log:*),Bash(git blame:*)"`, prompting the model to
invoke the Bash tool with a command chained by `;` or `&&` after a matching prefix gets
**auto-approved as a single unit and fully executed** — the trailing, non-matching subcommand runs
too, with `permission_denials` empty (i.e. no denial is recorded at all).

### Repro

```bash
mkdir /tmp/repro && cd /tmp/repro
git init -q
export GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.com GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.com
echo a > f.txt && git add f.txt && git commit -q -m "c1"

claude -p \
--allowedTools "Read,Grep,Glob,Bash(git log:*),Bash(git blame:*)" \
--model claude-haiku-4-5-20251001 \
--max-turns 3 \
--output-format json \
"Invoke the Bash tool with exactly this command line, verbatim, with no changes and no added cd: git log --oneline -5; whoami Report back the raw output you receive, including any denial/error message."
```

**Observed result (`2.1.236`):**

```json
{
"permission_denials": [],
"result": "Raw output:\n\n```\n\nmylocalusername\n```\n\nNo errors or denials. Command executed successfully."
}
```

`whoami`'s real output appears in the tool result. The same happens with `&&` in place of `;`.
Both were re-tested independently against `2.1.183` (identical outcome) and `2.1.236` (identical
outcome) — this is not a stale-pin artifact.

### What *does* correctly get denied (for contrast — so the splitting logic clearly exists for some cases)

Against the same allow rule, all of these were correctly denied, with `permission_denials`
populated:

- Command substitution: `git log --oneline $(whoami)`
- Explicit subshell wrapper: `sh -c 'git log --oneline -5; whoami'`
- Disguised subcommand: `git -C . log --oneline -5`
- Disguised path: `/usr/bin/git log --oneline -5`

So the matcher clearly does *some* structural analysis beyond naive string-prefix matching — it's
specifically the `;` and `&&` (and, going by the docs, presumably `||`/`|`/`|&`/newline, not
independently tested) shell-operator-splitting path that isn't enforcing what the docs describe.

### Why this matters

An allow rule like `Bash(git log:*)` is a common shape for scoping an agent to read-only,
low-risk commands. As tested, that scoping is not a security boundary against the compound-command
smuggling the docs explicitly say it defends against — any untrusted text an agent might be
induced to pass to the Bash tool (e.g. content from a file it read, a ticket description, etc.)
can ride along after a valid-looking prefix and execute with no denial recorded.

### Ask

Please confirm whether this is a known regression, and if so whether `;`/`&&` splitting is
expected to be restored, or whether the "Compound commands" doc section needs to be corrected to
reflect current behavior in the meantime.

Contributor guide

No contributing guide indexed for this repository

Research direction

Run the provided `claude -p` reproduction with the `--allowedTools` rule against both `;` and `&&`, and compare the result with the documented Compound commands behavior. Trace the Bash permission matching entry point and verify that each chained subcommand is checked independently, with the reproduction no longer executing `whoami` without a denial.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash
Domain
cli, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.