Allow loopback network access inside the workspace sandbox - #1221
Conversation
The workspace sandbox denied all network including loopback, so bb CLI calls only worked through the unsandboxed-command escape hatch. Turns with permission escalation denied (system-initiated turns, child threads) have no such path, leaving agents unable to reach the bb server at all. Pass network.allowLocalBinding so sandboxed commands can connect to localhost directly.
|
🚨 SLOP COP 🚨 · I am SlopCop. I am reviewing this pull request for security, code quality, performance, architecture, and practical test coverage. |
| // Loopback must stay reachable from inside the sandbox: the bb CLI talks | ||
| // to the local bb server, and escalation-denied turns (system-initiated | ||
| // turns, child threads) have no unsandboxed-retry path around a block. | ||
| network: { allowLocalBinding: true }, |
There was a problem hiding this comment.
🚨 slopcop/review — High: loopback access lets sandboxed code start an unsandboxed terminal through BB's unauthenticated API.
The /api/v1/* routes have no authentication check. POST /api/v1/terminals accepts a command and asks the host daemon to start it outside this Claude sandbox. After this line, untrusted workspace content can call bb terminal create or this route during an escalation-denied turn. This bypasses both allowUnsandboxedCommands: false and the workspace write boundary. Please authenticate the local API with a session token before you enable loopback access, or use a narrower authenticated transport.
SawyerHood
left a comment
There was a problem hiding this comment.
🚨 SLOP COP 🚨 · review
ELI5: This PR lets sandboxed Claude commands reach local BB. The fix works on macOS, but it also gives those commands an unchecked path to start unsandboxed terminals.
I found one blocking security issue.
- High — the BB API becomes a sandbox escape path. The local
/api/v1/*routes have no authentication check.POST /api/v1/terminalsstarts a host terminal outside the Claude sandbox. A command influenced by untrusted repository content can use this path during an escalation-denied turn. The request bypasses bothallowUnsandboxedCommands: falseand the workspace write boundary. Add per-session API authentication before this network grant, or use a narrower authenticated transport.
Other findings:
- Medium — the grant is wider than the PR description. Claude 2.1.226 emits rules for outbound access to every localhost port. It also permits bind and inbound access on every interface. A live macOS sandbox probe reached the BB server. It also reached a sandboxed listener through the machine's LAN address. External outbound access stayed blocked.
- Medium — the agent guidance stays stale. The BB CLI skill and thread guide still tell subagents to use Full Access. That instruction bypasses the sandbox which this PR aims to make useful. Related memory, secrets, and plugin-authoring documents still say loopback needs Full Access.
- Medium — Linux keeps the original problem. The current Claude Linux sandbox path ignores
allowLocalBinding. The PR states this limit, but the code comment reads as platform-neutral. - Low — the flag changes Java behavior. Claude injects
JAVA_TOOL_OPTIONS=-Djava.net.preferIPv4Stack=trueinto macOS sandboxed commands when this flag applies.
The implementation has no meaningful performance cost. It adds a constant session option and starts no new proxy or process. The sandbox construction has one owner, so I found no duplicate code to refactor. A path-scoped Unix socket could provide a narrower future transport.
Validation results:
@bb/agent-runtimetypecheck passed.- The focused bridge suite passed all 62 tests.
- The full package suite passed 837 of 838 tests. One unchanged process lifecycle test failed again when run alone.
- A live macOS
sandbox-execprobe confirmed IPv4 and IPv6 loopback access after the new rule. - A browser test did not apply because this change has no UI route. The sandbox probe tested the changed boundary directly.
- No daemon protocol version change is necessary because this option stays inside the Claude bridge.
I would not merge this until the local API cannot turn loopback access into unsandboxed command execution.
|
Addressed the Mediums: the sandbox comment now states the grant is macOS-only and coarse (Linux --unshare-net ignores the flag), the stale loopback/Full-Access caveats in the plugin-authoring, memory, and secrets docs are updated, and the subagent guidance now defaults to inheriting the parent's permission mode. On the High: /api/v1 being unauthenticated is pre-existing and deliberate (trusted local machine, loopback bind by default). A session token cannot create the boundary described here — the bb CLI itself runs inside the sandbox in escalation-denied turns, so any credential it can read is equally readable by every other sandboxed command. The real fix is a capability-scoped transport for sandboxed callers (e.g. a Unix socket listener mounting only agent-safe routes via allowUnixSockets), which deserves its own design and PR. |
|
Yeah I agree on the high one! Slopcop is being picky |
|
I'll merge when ci passes! TY for the contribution |
Summary
Fixes #1220.
The workspace sandbox (
auto/accept-editsmodes) denies all network including loopback, sobbCLI calls from sandboxed shells only ever worked through the unsandboxed-command escape hatch (allowUnsandboxedCommands, enabled only when permission escalation isask). Turns with escalationdeny— system-initiated turns such as child-completed notifications, and all child threads — have no retry path, so everybbcall fails withEPERM. A parent thread orchestrating children loses bb access in exactly the turns that exist so it can dispatch the next child.This passes
network.allowLocalBinding: truein the sandbox config, which permits direct loopback connections (on macOS the profile emits(allow network-outbound (remote ip "localhost:*"))). Sandboxed commands reach the bb server in every turn without involving unsandboxed escalation; escalation semantics for everything else are unchanged.Notes
allowLocalBindingalso permits binding local ports (its primary documented purpose, e.g. dev servers), so this loosens the sandbox from "no network" to "loopback + local binding, nothing external". The SDK exposes no loopback allowance scoped to a single port; this is the narrowest available knob and is far tighter than the previous workaround of spawning subagents with--permission-mode fullto keep bb reachable.HOST_DAEMON_PROTOCOL_VERSIONbump: the change is local to the claude-code bridge session options; nothing on the server/daemon wire changes.