anthropics / anthropics/claude-code
[BUG] 2.1.278: a file redirection on a single excluded command defeats sandbox.excludedCommands (gh fails TLS)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 147k
- Forks
- 24k
- PR merge metrics
- PR metrics pending
Description
Preflight Checklist
- I have searched existing issues and this hasn't been reported yet
- This is a single bug report
- I am using the latest version of Claude Code
What's Wrong?
With sandbox.excludedCommands: ["gh *"], a bare gh … call runs outside the sandbox as documented. The same single command with a file redirection on stdout or stderr is sandboxed instead, and on macOS then fails TLS verification (x509: OSStatus -26276, the documented Go/Seatbelt failure that excludedCommands exists to work around).
A redirection is not a command. Under the 2.1.277 rule ("every part must now match"), gh api user > out.json has exactly one part, and that part matches gh *.
Measured on 2.1.278, each line a separate Bash tool call, same session:
| Command | Runs outside the sandbox? |
|---|---|
gh api user --jq .login |
yes |
cd /repo && gh api user --jq .login |
yes |
gh api user --jq .login && gh api user --jq .name |
yes |
gh api user --jq .login 2>&1 |
yes |
gh api user --jq .login > "$TMPDIR/out.txt" |
no |
gh api user --jq .login > /path/literal/out.txt (no variable expansion) |
no |
gh api user --jq .login > /dev/null |
no |
gh api user --jq .login 2>/dev/null |
no |
So a descriptor duplication (2>&1) is fine, but any redirection to a file, including /dev/null, drops the exclusion. $(gh …) and gh … | head are also sandboxed, which I take to be the intended 2.1.277 behaviour and is not what this report is about.
This matters because writing gh's JSON to a file and post-processing it in a separate call is the natural workaround for the "every part must match" rule (you cannot pipe, so you redirect). That workaround does not work either, which leaves --jq as the only way to get structured output out of an excluded gh without a per-call sandbox bypass.
What Should Happen?
A redirection should not count as a non-matching part. gh api user > out.json should run outside the sandbox exactly like gh api user, as it did on earlier builds (see the regression note).
If redirections are excluded deliberately, the settings reference and the "Go-based CLIs fail TLS verification on macOS" troubleshooting entry should say so, since that entry currently names excludedCommands as the remedy with no caveat.
Error Messages/Logs
# excluded — works
$ gh api user --jq .login
potiuk
# same command, stdout redirected — sandboxed
$ gh api user --jq .login > /private/tmp/claude-501/.../scratchpad/probe-h.txt
Get "https://api.github.com/user": tls: failed to verify certificate: x509: OSStatus -26276
# stderr redirected — sandboxed (error is swallowed, exit 1)
$ gh api user --jq .login 2>/dev/null
(exit 1)
# descriptor dup — excluded, works
$ gh api user --jq .login 2>&1
potiuk
Steps to Reproduce
- macOS, sandbox enabled, project or user settings:
{ "sandbox": { "enabled": true, "excludedCommands": ["gh *"], "network": { "allowedDomains": ["api.github.com", "github.com"] } } } - Log in with
gh auth loginin a normal terminal. - As one Bash tool call:
gh api user --jq .login→ prints the login. - As a separate Bash tool call:
gh api user --jq .login > /dev/null→tls: failed to verify certificate: x509: OSStatus -26276. - As a separate Bash tool call:
gh api user --jq .login 2>&1→ prints the login again.
Only the redirection differs between steps 3 and 4.
Claude Model
Other (Fable 5.1)
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Not bisected. #82109 shows gh --version > ~/sbprobe running excluded on an earlier build (its case 1), so the redirection form was exempted before the 2.1.277 matching change.
Claude Code Version
2.1.278 (Claude Code)
Platform
Anthropic API
Operating System
macOS (26.6, Apple silicon)
Terminal/Shell
Other (Ghostty, zsh)
Additional Information
- gh 2.x from Homebrew, linked with go1.26.5. Python
urllibthrough the same sandbox proxy returns 200, so the proxy and certificates are fine; the failure is Go's Security.framework verification withtrustdunreachable (#85857).GODEBUG=x509usefallbackroots=1andSSL_CERT_FILEdo not help, soexcludedCommandsreally is the only remedy forghinside the sandbox. - Related: #95455 (single command with a value-taking pre-subcommand flag also loses the exclusion after 2.1.277 — same over-matching family, different trigger), #82109 (loops), #81157 (the compound-command bypass that 2.1.277 fixed; this is not a request to revert that).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the separate Bash tool calls using sandbox.excludedCommands with gh api user, file stdout or stderr redirection, and descriptor duplication. Then trace the sandbox command-matching entry point for the 2.1.277 matching change; the report names no source file or test. Done means file redirections preserve the gh exclusion without changing the intended compound-command behavior, with regression coverage for the listed forms.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, shell, typescript
- Domain
- cli, operating-systems, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100