[BUG] 2.1.278: a file redirection on a single excluded command defeats sandbox.excludedCommands (gh fails TLS)

Status Open
Reported on v2.1.277
Maintainer reply None cached
Activity 1 comment · opened Sep 19, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] 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

  1. macOS, sandbox enabled, project or user settings:

``json
{
"sandbox": {
"enabled": true,
"excludedCommands": ["gh *"],
"network": { "allowedDomains": ["api.github.com", "github.com"] }
}
}
``

  1. Log in with gh auth login in a normal terminal.
  2. As one Bash tool call: gh api user --jq .login → prints the login.
  3. As a separate Bash tool call: gh api user --jq .login > /dev/null → tls: failed to verify certificate: x509: OSStatus -26276.
  4. 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 urllib through the same sandbox proxy returns 200, so the proxy and certificates are fine; the failure is Go's Security.framework verification with trustd unreachable (#85857). GODEBUG=x509usefallbackroots=1 and SSL_CERT_FILE do not help, so excludedCommands really is the only remedy for gh inside 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).

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗