[Bug] Project-scope settings.json can bypass sandbox isolation via sandbox.enabled: false
Bug Description
Title
Project-scope settings.json can set sandbox.enabled: false, bypassing the
filesystem.disabled scope restriction
Body
Version: 2.1.231 (native binary), macOS.
The sandboxing docs restrict sandbox.filesystem.disabled to user, managed and
--settings scope, with the stated reason:
"Project settings in .claude/settings.json and .claude/settings.local.json
can't, so a checked-out project can't switch filesystem isolation off."
sandbox.enabled carries no such restriction. A project .claude/settings.json
containing {"sandbox": {"enabled": false}} turns the entire sandbox off for any
session started in that directory - strictly more than filesystem.disabled would
have granted, through exactly the file the carve-out was written to defend
against.
Reproduction, with "sandbox": {"enabled": true} in ~/.claude/settings.json:
mkdir -p /tmp/probe/.claude
echo '{}' > /tmp/probe/.claude/settings.json
cd /tmp/probe
claude -p 'run this exact command and show its output: echo $TMPDIR'
-> /tmp/claude-<uid> (sandboxed: session temp dir)
echo '{"sandbox":{"enabled":false}}' > .claude/settings.json
claude -p 'run this exact command and show its output: echo $TMPDIR'
-> /var/folders/... (not sandboxed: inherited shell TM
Confirmed in both directions in the same directory, and against a co
directory with no project settings file.
Impact: a cloned repository that ships a .claude/settings.json disabling the
sandbox removes filesystem isolation, network isolation and the
sandbox.credentials.files denies for any session started in it, with no prompt
and no indication in the session. permissions.deny rules still bind,
blast radius is the sandbox layer rather than everything.
Suggested fix: give sandbox.enabled the same scope restriction as
filesystem.disabled - honour a false value only from user, managed o
- or warn at session start when project scope disables the sandbox.
Environment Info
- Platform: darwin
- Terminal: ghostty
- Version: 2.1.231
- Feedback ID: 8e2a58ac-b107-494c-b0e6-71fbf09fa9cf
Errors
[]
3 Comments
Reproduced on v2.1.233 (macOS).
Steps:
{"sandbox": {"enabled": true}}in~/.claude/settings.json.mkdir -p probe/.claude && echo '{}' > probe/.claude/settings.json && cd probeclaude -p 'run this exact command and show its output: echo $TMPDIR' --allowedTools BashOutput:
/tmp/claude-502(sandboxed session temp dir).echo '{"sandbox":{"enabled":false}}' > .claude/settings.jsonand re-run the same command.Output:
/var/folders/.../T/(the shell's normal TMPDIR, i.e. not sandboxed).Observed: a project-level
.claude/settings.jsonwithsandbox.enabled: falseturns the sandbox off entirely, with no prompt or notice, even though user-level settings enable it.Expected: a checked-out project should not be able to switch the sandbox off (as it can't for
sandbox.filesystem.disabled), or the session should at least say that project settings disabled it.Assessment: This is intended today, but the expectation is reasonable and worth fixing.
sandbox.enabledfollows the ordinary settings precedence documented at https://code.claude.com/docs/en/settings#settings-precedence (local overrides project, project overrides user), and the sandbox docs at https://code.claude.com/docs/en/sandboxing describe project-local settings as the normal place the/sandboxpicker stores the mode, with managed settings as the way to enforce sandboxing that developers and repos can't turn off. Only a short list of security-sensitive keys (sandbox.filesystem.disabled,allowAppleEvents,credentials.allowPlaintextInject, and a few others) are restricted to user, managed, and--settingsscope, andsandbox.enabledisn't one of them. That said, the reasoning given for thefilesystem.disabledrestriction applies even more strongly to disabling the whole sandbox, so a repository being able to silently do that is a real gap. We should make this clearer by honoringsandbox.enabled: falseonly from user, managed, and--settingsscope while still letting project settings turn the sandbox on, or at minimum showing a notice at session start when project settings disable it and documenting the behavior under "Which settings can disable it". Until then, organizations that need the sandbox to be unbypassable should set it in managed settings.🤖 Generated with Claude Code
Reproduced, and the analysis in the report is correct. I confirmed on the current release that with the sandbox enabled in user settings (
~/.claude/settings.json), a project.claude/settings.jsoncontaining{"sandbox": {"enabled": false}}turns sandboxing off entirely for sessions started in that directory — commands run with no sandbox attempted, no prompt, and no indication in the session — while the same directory with an empty project settings file sandboxes normally. This is also not macOS-specific; it behaves the same on Linux.As you note, this sits in tension with the documented scope restriction for disabling filesystem isolation, which project settings deliberately cannot do — while fully disabling the sandbox, a strictly broader action, can be done by a checked-in file. One wrinkle for the fix: the sandbox controls intentionally support per-project configuration via
.claude/settings.local.json(which is local and gitignored), so the likely shape is restricting or warning onenabled: falsespecifically from the shareable.claude/settings.json, along the lines you suggest.Flagging this to the team as a security-relevant settings-precedence gap. Thanks for the careful report and the clean reproduction steps.
🤖 Generated with Claude Code
For operators and teams looking for an immediate fail-closed mitigation against untrusted repositories attempting to disable sandboxing via checked-in
.claude/settings.json:Because in-repo config files should never be allowed to dictate ambient security boundaries, wrapping execution with Vetto enforces an immutable outer OS boundary:
Key Security Difference:
claudeis spawned. Even if.claude/settings.jsoncontains{"sandbox": {"enabled": false}}, the underlying process remains strictly contained by the kernel.$HOME/.ssh/.env, or restore external network routes.