anthropics / anthropics/claude-code

Worktree command-safety analyzer misreads a non-leading `complete`/`compgen`/`compopt` token as an invoked builtin

Offen
#88,047 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area:sandbox bug has repro platform:windows stale
Vorherrschende Sprache
Python
Sterne
145k
Forks
23.1k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

### Environment

- **Claude Code version:** 2.1.207 (native WinGet build, `Anthropic.ClaudeCode`)
- **OS:** Windows 11 (26200), Git Bash + PowerShell tools
- **Component:** the in-worktree Bash command-safety analyzer (a PreToolUse-layer check, *not* the sandbox — `dangerouslyDisableSandbox: true` does not bypass it)

### Summary

In a git-worktree-isolated session, the analyzer that decides whether a Bash command "can be verified to stay inside the worktree" scans the command's tokens for shell-builtin names and treats any occurrence as an *invocation* of that builtin — regardless of the token's position. Because the completion builtins `complete` / `compgen` / `compopt` can execute arbitrary completion functions, a command containing one of those words **as an ordinary argument** is judged unverifiable and refused.

The false positive fires on an ordinary command whose only sin is having the word `complete` somewhere after the executable:

```
py -3 scripts/codex_continuity.py sprint complete --ledger --task-id --next-task-id
```

Here `complete` is a positional argument to `py` (a subcommand name for our own script), not a command being invoked. The analyzer refuses it with:

> This session is isolated in the worktree `…\worktrees\`, but this command runs a string through **complete**, which can't be verified to stay inside the worktree; run the command directly instead. Refusing to run it — a worktree-isolated session's git operations must target its own worktree. …

### Reproduction

1. Create and enter a real git worktree (`git worktree add -b test .git-worktrees/test`).
2. From inside it, run any Bash command that carries `complete`, `compgen`, or `compopt` as a **non-leading** token, e.g.:
- `py -3 anyscript.py sprint complete --flag x`
- `echo complete` *(a bare builtin name as an argument trips it too)*
- `some_tool complete --now`
3. The analyzer refuses the command.

Control cases that are **not** refused, confirming the collision is specifically the builtin-name token and not the surrounding command:

- `py -3 scripts/codex_continuity.py sprint status …`
- `py -3 scripts/codex_continuity.py sprint start …`
- `py -3 scripts/codex_continuity.py sprint approve …`
- `py -3 scripts/codex_continuity.py sprint next …`

Only the subcommand literally named `complete` is blocked.

### Expected vs. actual

- **Expected:** a builtin name counts as "invoking a string-running builtin" only when it is in **command-head position** (the first word of a simple command, i.e. after `;`, `&&`, `||`, `|`, `(`, newline, or the start of the command — and not consumed as an argument to a preceding executable).
- **Actual:** any token equal to a flagged builtin name anywhere in the command is treated as an invocation, so it is refused.

### Impact

This blocks the *documented, gated* completion step of our `/sprint` workflow for **every** worktree-isolated session. Quoting the token (`"complete"`) and `dangerouslyDisableSandbox: true` both fail to bypass it, because it is a PreToolUse-layer analyzer rather than the sandbox. The only bypass we found was an obfuscating Python shim that reassembles the function name at runtime (`getattr(g, 'compl'+'ete')`) — exactly the kind of construct these safety layers should discourage.

More broadly: `complete`, `compgen`, `compopt` are common English/argument words ("mark complete", "complete the run"), so this will surface for any user whose scripts or subcommands use them.

### Suggested fix

When scanning for string-running builtins, gate the match on **command-head position**. A shell-AST/tokenizer pass that already distinguishes a simple command's first word from its arguments should flag the builtin only in that head slot. A token consumed as an argument to a preceding executable (`py`, `echo`, our script, etc.) must not count as an invocation of the builtin of the same name.

The same reasoning applies to the whole builtin set the analyzer keys on — `eval`, `exec`, `source`, `.`, `complete`, `compgen`, `compopt`, etc.: it is the *head-position invocation* that runs a string, not the appearance of the word.

### Workaround in place (our side)

We added a collision-free alias `finish` for the `complete` subcommand of our sprint CLI, routed to the identical handler, and updated the workflow to call `sprint finish` inside worktrees. This is a local mitigation, not a fix for the analyzer — the underlying false positive remains for any command containing these tokens.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Start at the in-worktree Bash command-safety analyzer in the PreToolUse layer and reproduce the issue from a real git worktree with the listed commands. Trace how it identifies flagged builtins and compare command-head tokens with arguments. Done means non-leading complete, compgen, and compopt arguments are allowed while actual head-position invocations remain refused.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
bash, powershell, python
Bereich
cli, security, tooling
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Aktiv
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
48/100

Neue Issues direkt in Ihr Postfach

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