sandbox.excludedCommands has no effect — excluded commands still run sandboxed (2.1.232, macOS)
sandbox.excludedCommands in user settings appears to be read but not honoured: a command listed there is still executed inside the sandbox, for both filesystem and network operations.
Environment
- Claude Code 2.1.232 (Homebrew cask,
/opt/homebrew/Caskroom/claude-code@latest/2.1.232/claude) - macOS 26.5.1, arm64
permissions.defaultMode: bypassPermissions- No
.claude/settings.local.jsonin the project (confirmed absent — see "Ruled out" below)
Relevant ~/.claude/settings.json:
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": true,
"excludedCommands": ["dotnet", "mvn", "mvnw", "./mvnw", "gh", "git", "flutter", "dart", "specfuse"]
}
Minimal repro
git is in excludedCommands. In a sandboxed Bash tool call:
$ git init -q /Users/Shared/sbtest-git
fatal: cannot mkdir /Users/Shared/sbtest-git: Operation not permitted
EXIT=128
The identical command with the sandbox explicitly disabled succeeds, so the directory is writable and it is the sandbox producing the denial:
$ git init -q /Users/Shared/sbtest-control # sandbox bypassed
EXIT=0
Expected: git is in excludedCommands, so it should run outside the sandbox and succeed in both cases.
Actual: the excluded command is sandboxed.
Also affects network, same cause
Both of these are listed in excludedCommands and both fail sandboxed, in ways that show the sandbox's proxy environment is being applied to them:
$ gh api repos/OWNER/REPO --jq .full_name
Get "https://api.github.com/repos/OWNER/REPO": tls: failed to verify certificate: x509: OSStatus -26276
$ git ls-remote git@github.com:OWNER/REPO.git HEAD
ssh_dispatch_run_fatal: Connection to UNKNOWN port 65535: Broken pipe
Sandboxed shells receive HTTPS_PROXY/ALL_PROXY=http://…@localhost:<port> plusGIT_SSH_COMMAND=ssh -o ProxyCommand='nc -X 5 -x localhost:<port> %h %p'. The gh failure is the MITM proxy's certificate not being in Go's trust store; the SSH failure is the proxy refusing port 22. Unsetting GIT_SSH_COMMAND turns the second into ssh: connect to host github.com port 22: Operation not permitted, i.e. the seatbelt layer, confirming both layers are applied to a command that should be exempt from them. The same gh call with the sandbox bypassed returns normally.
Ruled out
- Project-settings override.
.claude/settings.local.jsondid previously exist with its ownsandbox.excludedCommands; it was deleted, and the behaviour is unchanged. Reproduced with the file confirmed absent. - Settings staleness / capture-at-startup.
~/.claude/settings.jsonmtime07:51:05; session started07:54:38. The settings predate the session. - Replace-vs-merge across settings sources. Not the cause — one of the readers in the bundle collects the key with
flatMapacross sources, i.e. it merges. - Path-specific denial. The unsandboxed control above writes to the same path successfully.
- Compound-command parsing. First observed with
cmd | headappended, which could plausibly defeat prefix matching. Re-run bare, as shown above — same result.
Possibly useful
Strings in the bundle suggest the intended flow is a shouldSandbox-style predicate that returns false when the command matches excludedCommands (read via …sandbox?.excludedCommands, matched by a prefix/startsWith(prefix + " ") rule). If that predicate is reached at all here, the match seems to be failing; a bare git … should match a "git" prefix entry. Offered as a pointer only — I have not verified it against source.
Impact
With excludedCommands inert, git-over-SSH and gh are unusable in sandboxed calls, so every such invocation needs an explicit sandbox bypass. That makes the setting's security value illusory in the other direction too: a user who believes they have narrowed their exclusion list has not actually changed anything.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗