Worktree-isolation Bash guard refuses a curl command that references a settings.json env var
Summary
In a worktree-isolated session, the Bash guard refuses a curl POST with "this command names git in a form too complex to verify that it stays inside the worktree". The command contains no git invocation. The same command runs when the one env var it references is swapped for any other variable. The var's value does not contain "git" in any case.
Environment
- Claude Code 2.1.220, desktop app (Code tab), macOS (Darwin 25.6.0), zsh
- Session isolated in a git worktree (
<repo>/.claude/worktrees/<name>) - The var is set only through the
envblock of~/.claude/settings.json(not in any shell profile). Value is 64 characters. - Only user hook: a PreToolUse hook on
Write|Edit|NotebookEdit. Nothing on Bash.
Steps to reproduce
- Put a token in
~/.claude/settings.jsonunderenv, e.g."IRANTI_SYNC_TOKEN": "<64-char value>". - Start a session isolated in a worktree.
- Have Claude run:
``bash``
curl -s -o <scratchpad>/out.json -w "%{http_code}\n" -X POST https://<host>/api/github/sync-trigger -H "Authorization: Bearer $IRANTI_SYNC_TOKEN" -H "Content-Type: application/json" -d @<scratchpad>/payload.json
Not yet confirmed: whether a different var name or a different value in env also reproduces.
Expected
The command runs. It has no git invocation, and the only difference from commands that pass is which variable it expands.
Actual
This session is isolated in the worktree <path>, but this command names git in a form too complex to verify that it stays inside the worktree. Refusing to run it
What was ruled out (same session, same worktree)
| Command | Result |
|---|---|
| echo https://<host>/api/github/sync-trigger | ran |
| curl GET to that same URL | ran |
| curl -X POST to that URL with -H "X-Test: $SHLVL" | ran |
| curl -X POST to that URL with -d @<scratchpad>/payload.json | ran |
| Full command from step 3 with $SHLVL in place of $IRANTI_SYNC_TOKEN | ran |
| Full command from step 3 as written | refused |
| Same as step 3 (still using $IRANTI_SYNC_TOKEN), with $(date -u ...) in an inline JSON body instead of -d @file | refused |
| echo "${#IRANTI_SYNC_TOKEN}" | ran |
| echo "x $FOO_BAR_TOKEN" (unset var) | ran |
So the refusal is tied to that variable, not to the URL path containing "github", to -o/-d paths, or to the flags. The var's value was checked outside Claude Code: it contains no "git" in any case. So a plain substring match on the expanded value does not explain it.
Possibly related, same session:
- A
sedcommand that took a shell variable was refused with the same message. - Filing this report with
gh issue create --repo anthropics/claude-code --title "... as \"names git\" ..."was refused with a different message: "this command runs gh with the text … in a plain command, so what it runs cannot be shown not to be git". The word "git" inside a quoted--titleargument was enough; the title had to be reworded to file it.
Impact
Any token-bearing command (webhooks, API triggers) has to be handed to the user to run, even when the user approved it in-session.