anthropics / anthropics/claude-code

[BUG] Bash deny rules bypassed by any wrapper program outside ax()'s 8-item unwrap list

Offen
#92,542 1 Kommentar 1 Reaktion 0 zugewiesene Personen Auf GitHub ansehen
area:bash area:permissions area:security bug has repro platform:linux
Vorherrschende Sprache
Python
Sterne
145k
Forks
23.1k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

## Summary

`permissions.deny` rules for Bash are matched against the program name of each
sub-command in the parsed AST. Before matching, Claude Code unwraps a **fixed,
eight-item list** of wrapper programs. Any other wrapper — an ordinary binary or
script on `PATH` that execs its arguments — shifts the program name, so no
`Bash( ...)` rule applies to the command underneath it. Deny and allow
rules alike are silently skipped.

This is not the env-var prefix case (#31558, closed) and not the command-chaining
case (#4956). Compound splitting works correctly. The hole is the wrapper
allowlist itself, and I could not find it named in any existing issue.

## Root cause

From `~/.local/share/claude/versions/2.1.258`, function `ax()`. The complete set
of wrappers it strips before rule matching:

```
timeout time nice stdbuf nohup command builtin noglob
```

plus leading `VAR=value` assignments. There is no fallback and no attempt to
resolve what an unknown wrapper actually executes.

## Reproduction

1. `permissions.deny`: `"Bash(git add -A*)"`
2. Ask for `git add -A` → correctly **denied**.
3. Ask for `git add -A` preceded by any wrapper binary on `PATH` that forwards
its arguments → **runs**. The deny rule is never consulted.

Compound forms are correctly caught, which shows the AST splitter is not the
problem: `cd ; git revert ...`, `rm -f ; git add -A ...`, and
`git add -A && git status` were all denied in the same environment.

## Observed impact

In one real session, `git add -A` was denied six times over several weeks. The
same operation then ran four times behind a wrapper, committing 103 unintended
files and 657,373 insertions to a working repository. The user had explicitly
configured that rule to prevent exactly this.

Denials: 2026-08-27T18:15:51Z, 2026-08-28T07:23:26Z, 07:42:37Z, 07:59:53Z,
09:35:12Z, 09:41:26Z.
Bypasses: 2026-09-06T16:57:28Z, 17:12:26Z, 18:44:10Z, 19:01:59Z.

## Why this is worth fixing even though deny rules are "best effort"

The documented position is that deny rules are client-side filtering, useful
against accidents rather than a determined bypass. This case is squarely the
accident it is meant to catch: no adversary, no prompt injection, no attempt to
evade. The wrapper was adopted for an unrelated reason and silently disabled
every git rule in the config.

Related, and relevant: #49874 recommends putting commands behind a wrapper
script specifically so allowlist matching stops seeing them. The same mechanism
that makes that workaround function is what defeats the deny list.

## Suggested direction

1. When the program of a sub-command is not a known wrapper and is not on
`PATH` as a resolvable non-wrapper, treat the command as **unmatched rather
than unmatchable** — i.e. escalate to a prompt instead of falling through.
2. Or: surface an explicit warning at config-load time that `Bash(...)` rules
only bind the literal program name and can be trivially shifted.
3. Or: check every token of a sub-command against deny rules, not only the
leading program.

At minimum, document the eight-item unwrap list. Users writing deny rules today
have no way to know it exists or how short it is.

## Environment

- Claude Code 2.1.258
- Linux
- Permission prompts suppressed for the session, so an unmatched command ran
silently rather than prompting. The bypass itself does not depend on that;
in a prompting session the same command would have asked instead of being
refused outright.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Start by inspecting function ax() in ~/.local/share/claude/versions/2.1.258 and the Bash permission-matching path, then reproduce the behavior with a forwarding wrapper on PATH. The issue names no repository source files or tests; done should include an agreed handling direction and regression coverage showing that a wrapper cannot silently bypass Bash deny rules.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
bash
Bereich
cli, security
Issue-Typ
Bug
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Aktiv
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.