[BUG] Sandbox network allowlist bypassed via in-sandbox relayed CONNECT (direct CONNECT correctly blocked)

Status Open
Reported on v2.1.90
Maintainer reply None cached
Activity 0 comments · opened Aug 17, 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?

Security impact

The sandbox domain allowlist (allowManagedDomainsOnly: true) can be bypassed by any sandboxed process that forwards its own CONNECTs through a trivial in-sandbox local relay. A direct proxied CONNECT to a non-allowlisted host is correctly blocked (403), but the same host is reachable when the CONNECT is relayed — in the same session, with the same credentials, targeting the same authority. In practice this means allowManagedDomainsOnly does not actually constrain the set of reachable destinations.

Summary

The sandbox network allowlist (sandbox.network.allowedDomains with allowManagedDomainsOnly: true) is enforced inconsistently depending on how a CONNECT reaches the sandbox proxy. A direct proxied request to a non-allowlisted host is correctly blocked (HTTP 403), but a request to the same host is allowed through when forwarded by a local relay process running inside the same sandboxed command. Both occur in the same session, minutes apart, so the block on non-allowlisted domains is not effective in practice.

Environment

  • Sandbox: enabled: true, allowUnsandboxedCommands: false, failIfUnavailable: true, allowManagedDomainsOnly: true.
  • allowedDomains lists a few infra hosts (e.g. api.example-registry.com); the tested host (blocked.example.com) is not listed.
  • Linux container; bubblewrap with --unshare-net and an in-namespace socat bridge on localhost:3128 → host proxy.

Steps to reproduce

  1. Direct probe via the proxy:

``sh
curl -sS -o /dev/null -w '%{http_code}\n' https://blocked.example.com/
# → 403 (correctly blocked)
``

  1. In the same session, run a sandboxed command that starts a small local HTTP CONNECT relay on 127.0.0.1:<port> (forwarding to localhost:3128, injecting the sandbox's own proxy credentials), then launch a child process configured to use 127.0.0.1:<port> as its proxy and connect to https://blocked.example.com/.
  1. Re-run the step-1 probe:

``sh
curl -sS -o /dev/null -w '%{http_code}\n' https://blocked.example.com/
# → still 403
``

Expected behavior

Step 2 is also blocked — blocked.example.com is not allowlisted, so no path should reach it.

Actual behavior

Step 2 succeeds; the child reaches blocked.example.com. Steps 1 and 3 stay 403. The allowlist is enforced for the direct CONNECT but bypassed for the relayed CONNECT, in the same session.

Ruled out

  • Not session/resume/time dependent: reproduces in a brand-new session; the 403 and the success occur in the same session minutes apart.
  • Not unsandboxed fallback: during the relayed run, PID 1 is bwrap … --unshare-net in an isolated net namespace; allowUnsandboxedCommands: false.
  • Not a config grant: host absent from settings.json; no settings.local.json; no matching rule in ~/.claude.json, user, or managed settings; no /etc/claude-code; /permissions shows only configured tool rules; PreToolUse hooks can only deny.
  • Command-scoped, not agent-scoped: two Bash calls in one subagent — direct curl (403) and the relay-launching command (allowed) — differ only in the command string.

Diagnostic detail

Both CONNECTs carry the sandbox's per-call <user>:<pass> proxy credentials and target the same authority; the only difference is direct issuance vs. forwarding by an in-sandbox relay. Enforcement diverges between the two.

How this differs from prior sandbox bypasses

This is a distinct mechanism from the two previously reported Claude Code network-sandbox bypasses. It is not a validation-layer trick (no crafted hostname) and not a config-parsing fault (the allowlist parses and enforces correctly for the direct path); enforcement simply diverges by how the CONNECT reaches the proxy.

  • Not CVE-2025-66479 (empty-allowlist fail-open): that bug was a config-parsing fault where a misinterpreted/empty allowlist was treated as "allow everything." Here the allowlist is populated and is actively enforced — the direct probe to blocked.example.com returns 403 in the same session.
  • Not the SOCKS5 hostname null-byte injection (fixed ~v2.1.88–2.1.90): that bug was a parser differential — endsWith() approved a crafted hostname like attacker.com\x00.google.com while libc's getaddrinfo() truncated at the null byte. Here there is no malformed hostname: both CONNECTs target the same well-formed, non-allowlisted authority, and the allowlist check itself is not being fooled.
  • Not the allowedDomains over-blocking reports (e.g. legitimate domains being incorrectly blocked): this is the opposite failure — a non-allowlisted domain that should be blocked is reachable.

The divergence is purely direct-issuance vs. in-sandbox relayed-forwarding of an otherwise identical, correctly-credentialed CONNECT to a non-allowlisted host.

What Should Happen?

Access or blocking of a site in the Claude sandbox should not be affected by other (non address) details of the connection

Error Messages/Logs

Steps to Reproduce

  1. Direct probe via the proxy:

``sh
curl -sS -o /dev/null -w '%{http_code}\n' https://blocked.example.com/
# → 403 (correctly blocked)
``

  1. In the same session, run a sandboxed command that starts a small local HTTP CONNECT relay on 127.0.0.1:<port> (forwarding to localhost:3128, injecting the sandbox's own proxy credentials), then launch a child process configured to use 127.0.0.1:<port> as its proxy and connect to https://blocked.example.com/.
  1. Re-run the step-1 probe:

``sh
curl -sS -o /dev/null -w '%{http_code}\n' https://blocked.example.com/
# → still 403
``

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.233

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Windows Terminal

Additional Information

_No response_

View original on GitHub ↗