[BUG] sandbox.network.strictAllowlist is silently discarded from project settings — no warning at startup, in --debug, /status, /sandbox, or doctor

Status Open
Reported on v2.1.250
Maintainer reply None cached
Activity 0 comments · opened Aug 28, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

sandbox.network.strictAllowlist is honored only from user, managed, or CLI --settings scope. Set in a repository's .claude/settings.json or .claude/settings.local.json, it is discarded. That restriction is documented on the sandboxing page — the bug is that nothing surfaces it at any point where an operator would notice:

  1. No startup warning, and no session notice. The key is accepted into a project settings file, silently dropped at resolution, and never mentioned again — not at startup, not in the --debug log, not in /status, not in the /sandbox panel, not in claude doctor (evidence below).
  2. The editor-facing schema is stale. Claude Code's own settings schema (embedded in the 2.1.250 binary) describes the key as "Only honored from user, managed/policy, or CLI (--settings) settings — project settings (.claude/settings.json and .claude/settings.local.json) are ignored." The copy the docs point editors at (json.schemastore.org/claude-code-settings.json, last synced at v2.1.220) still says "UNDOCUMENTED. Deny non-allowlisted hosts…" with no scope note, while its five sibling scope-restricted keys carry one. So schema validation flags nothing. (SchemaStore is community-maintained and a sync PR fixes that copy; it is noted here only because it is why the runtime signal is the last line of defense.)
  3. Every other signal says the config took. With sandbox.enabled: true in the same project file, the sandbox really does initialize and the filesystem layer really does enforce. allowedDomains is honored from project scope too (an allowlisted host is reached with no prompt and no classifier call). Only the deny-vs-prompt behavior silently reverts.

The result is a security control that reads as active and is not. An operator who commits a hardened sandbox block to a repo believes sandboxed Bash denies non-allowlisted egress; what they have is the documented false behavior. Under auto mode the fallback is not a human prompt. The settings reference is explicit about what false means: "it runs the classifier in auto mode, denies in dontAsk mode, allows in bypassPermissions mode and in plan mode when bypass is available, and otherwise asks you" — so a repo that set true silently gets classifier-gated egress inside the sandbox, with no denial ever surfaced to the operator or the transcript. (When the key is honored, the sandboxed attempt fails visibly with a 403; any unsandboxed retry is a separate, documented fallback that allowUnsandboxedCommands: false closes.)

Why this one fails quietly. The other project-ignored sandbox keys (filesystem.disabled, allowAppleEvents, tlsTerminate, credentials.allowPlaintextInject) are ignored from project scope to stop a checked-out repo from weakening isolation — a sensible default, and one that fails safe. strictAllowlist is ignored while a repo is strengthening isolation, so the failure is not self-announcing: nothing breaks, no command errors, egress just stays in prompt/classifier mode. There is no point at which the operator learns the control was dropped.

Filed as a bug rather than a feature request because a security control is accepted into config, partially applied (filesystem layer, allowedDomains), and partially dropped with no signal — the same class as #87545. The maintainer's close on #87163 already noted that a warning for inert sandbox.network.* keys "would make this discoverable"; this is that case from the other direction (sandbox on, key dropped by scope).

What Should Happen?

In preference order:

  1. A startup warning when sandbox.network.strictAllowlist appears in a settings source that cannot honor it, naming the file. There is direct precedent for exactly this in the sandbox subsystem — the docs already promise it for the analogous credential-masking misconfiguration: "Claude Code reports this misconfiguration at startup." The mechanism exists for other keys too: 2.1.250 already emits "<key> in .claude/settings.json is ignored — project-scoped settings can't set this key. Set it in ~/.claude/settings.json or managed settings instead." for protected env entries, and similar notices for spellcheck and spinnerTipsOverride.
  2. Get the scope caveat into the schema editors actually use. The embedded description already has it; the schemastore copy (v2.1.220 sync) does not. A sync PR there fixes that copy; a first-party pointer to the canonical schema would keep it from drifting again.
  3. Have the /sandbox Config tab show the effective network mode. Today it lists only Network Restrictions: Allowed: <domains> — it shows neither the requested nor the effective strict/prompt state.

Error Messages/Logs

None on any surface. Checked on 2.1.250 with the step-1 file in place:

$ claude -p 'Reply with exactly: ok' --debug
ok
# debug output goes to ~/.claude/debug/<session-id>.txt (~190 lines), not stdout; stderr is empty
$ grep -iE 'sandbox|strictallowlist' ~/.claude/debug/<session-id>.txt
# (no output — the log never mentions the sandbox or the key, including in sessions where the sandbox is demonstrably enforcing)

Interactive session startup (same project file, trust accepted): no notice.
/status  ->  "Setting sources: User settings, Shared project settings"   (no per-key information)
/sandbox -> Config tab -> "Network Restrictions:  Allowed: registry.npmjs.org"   (nothing about strict vs prompt mode, requested or effective)
$ claude doctor
No installation issues found.

Steps to Reproduce

Measured 2026-08-28 on 2.1.250, macOS 26.5.1 (Darwin 25.5.0). No sandbox block in ~/.claude/settings.json (jq '.sandbox' ~/.claude/settings.jsonnull); additionally, claude -p ... --setting-sources user runs unsandboxed (a touch ~/probe succeeds), so project scope is the only source of sandbox.enabled.

1. In a scratch repo, create .claude/settings.json:

{
  "sandbox": {
    "enabled": true,
    "network": {
      "strictAllowlist": true,
      "allowedDomains": ["registry.npmjs.org"]
    }
  }
}

2. Confirm the sandbox is live — this is the control, and it passes:

$ claude -p 'Run exactly this once with the Bash tool and report its full output verbatim. Do not retry and do not disable the sandbox: touch ~/.sandbox-probe-1 && echo CREATED'

Exit code 1
touch: /Users/<me>/.sandbox-probe-1: Operation not permitted

No file is created on the host. The sandbox initialized and the filesystem layer is enforcing, from this same project settings file. (Without the "do not retry" instruction, the model may retry with dangerouslyDisableSandbox under the default allowUnsandboxedCommands: true; that retry is the documented fallback and is not the subject of this report.)

3. Probe a non-allowlisted host in the same configuration. example.com is not in allowedDomains:

$ claude -p 'Run exactly this once with the Bash tool and report its stdout verbatim. Do not retry and do not disable the sandbox: curl -sS -m 10 https://example.com -o /dev/null -w "%{http_code}\n"'

200

Expected: denial without prompting, per the setting's contract.
Actual: 200, inside the sandbox, on the first and only attempt. No denial, no prompt, no warning — at startup or at request time — that strictAllowlist was discarded. The --debug log for this session shows the request going to the auto-mode classifier as {"SandboxNetworkAccess":{"host":"example.com","port":443}} — the documented strictAllowlist: false path — not a denial.

4. Same block, trusted scope. Move the identical JSON to CLI scope and rerun step 3:

$ claude -p '<same prompt>' --settings '{"sandbox":{"enabled":true,"network":{"strictAllowlist":true,"allowedDomains":["registry.npmjs.org"]}}}'

curl: (56) CONNECT tunnel failed, response 403

Identical bytes, different file → denial. The setting works; the project-scope copy is dropped, and nothing says so.

Steps 2–4 ran under identical settings, same version, minutes apart. Step 2 rules out the "sandboxing was never enabled" explanation: the sandbox is demonstrably on and enforcing its filesystem layer while the network layer silently runs in prompt/classifier mode. Step 4 rules out "the setting is broken": it is scope resolution, not enforcement.

Claude Model

Not sure / Multiple models — behavior is model-independent.

Is this a regression?

No, this never worked. (strictAllowlist was added in v2.1.219; measured here on 2.1.250.)

Last Working Version

_No response_

Claude Code Version

2.1.250 (Claude Code)

Platform

Anthropic API (Claude Max account login)

Operating System

macOS

Terminal/Shell

Non-interactive/CI environment — the repro above is headless claude -p for determinism. The same behavior was observed in ordinary interactive sessions on 2.1.240/241 and 2.1.250 (startup screen, /status, and /sandbox checked interactively on 2.1.250).

Additional Information

The schema inconsistency, reproducible in one command. Against the schemastore copy (https://www.schemastore.org/claude-code-settings.json, v2.1.220 sync); Claude Code's embedded description does carry the caveat — see What's Wrong §2.

| key | schema description states the scope restriction? |
|---|---|
| sandbox.filesystem.disabled | yes — "Only honored from user, managed, or CLI --settings settings (default: false)." |
| sandbox.allowAppleEvents | yes — "Honored only from user, managed, or CLI settings; project settings cannot enable it." |
| sandbox.network.tlsTerminate | yes — "Only honored from user, managed, or CLI --settings settings, not from .claude/settings.json or .claude/settings.local.json." |
| sandbox.credentials.allowPlaintextInject | yes — same sentence, plus "(default: false)" |
| sandbox.network.allowManagedDomainsOnly | yes — "(Managed settings only)" |
| sandbox.network.strictAllowlist | no"UNDOCUMENTED. Deny non-allowlisted hosts for sandboxed commands without prompting (added v2.1.219). See https://code.claude.com/docs/en/sandboxing#network-isolation"* |

curl -sSL https://www.schemastore.org/claude-code-settings.json \
  | jq -r '.. | objects | select(has("strictAllowlist")) | .strictAllowlist.description'

The embedded description, for comparison:

strings -n 6 "$(readlink -f "$(which claude)")" \
  | grep -o 'describe("When true, the sandbox runtime deterministically[^;]*are ignored\."' | sed 's/"+"//g'
# ... Only honored from user, managed/policy, or CLI (--settings) settings — project settings (.claude/settings.json and .claude/settings.local.json) are ignored.

The restriction is stated in the docs — sandboxing page, Network isolation: "if you set strictAllowlist to true in user, managed, or CLI --settings settings, Claude Code denies sandboxed commands access to any host outside the allowlist instead of prompting." and "Setting it in a repository's .claude/settings.json or .claude/settings.local.json has no effect." — and the settings reference lists its scope as User or managed ("A repository can't turn it on or off."). It is the runtime and the editor-facing schema that are silent. (Separately: the schemastore description opens with "UNDOCUMENTED", which is stale — the key is documented.)

Related, distinct:

  • #87545 — autoMode silently ignored in project settings, "settings schema does not mark it user-only, unlike sibling restricted keys." Same class of defect, different key. If a general fix is planned (validate settings keys against their permitted scopes and warn), these should share it. (Its table quotes strictAllowlist's embedded description, which does state the restriction — the schemastore copy does not; see What's Wrong §2.)
  • #87163 — closed 2026-08-25 as configuration; the original Docker report lacked sandbox.enabled. A 2026-08-23 comment on that thread reported this project-scope case on macOS with enabled: true; the close addressed the original report. Step 2 above rules that cause out.
  • #87296 — open, on sandbox settings interactions that silently remove a control; adjacent theme, different mechanism.

Suggested labels: area:sandbox, area:security, platform:macos

View original on GitHub ↗