Skip to content

fix(sandbox): drop browser-controlled headers when forwarding - #217

Merged
quickbeard merged 1 commit into
mainfrom
fix/broker-forbidden-headers
Aug 13, 2026
Merged

quickbeard merged 1 commit into
mainfrom
fix/broker-forbidden-headers

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Reported from a real sandbox session: every message failed with Bad Gateway: net::ERR_INVALID_ARGUMENT, retrying forever — the same symptom #215 fixed for one case, but still present for the agent's actual requests.

Root cause

The guest's model requests carry sec-fetch-mode (plus accept-language, user-agent, sec-ch-* in principle). Chromium refuses to let a caller set Sec-* headers, so Electron's net.fetch rejects the whole request — the broker then returns 502 and the guest retries.

This is why it escaped earlier verification: my probes used curl, which sends none of those headers, so the same broker, same route, same body size all succeeded. Isolated with a temporary diagnostic on the broker's upstream call, which printed the real header set, and then reproduced deterministically with a single added header:

curl … -H "sec-fetch-mode: cors"     → {"error":"sandbox egress upstream failed",
                                        "message":"net::ERR_INVALID_ARGUMENT"}
curl … (same request, header removed) → 200, real completion

Fix

forwardableRequestHeaders now strips sec-fetch-*, sec-ch-*, and proxy-* alongside the existing hop-by-hop/host set. This belongs in the broker rather than in the desktop's fetch wrapper: any Chromium-backed forwarder hits the same wall, and the broker is where header hygiene already lives.

Verification

In-app, after the fix: a sandbox session answered "run pwd" with /home/codev — a guest path, so the agent genuinely ran inside the VM, reached the model through the broker, and executed its shell tool with no permission prompt (per #210). Package suites: sandbox 92 pass / 2 skip, desktop 76 pass.

Note on the earlier "session binding" concern

While chasing this I reported that selecting a sandbox project and clicking New session ran the session on the Local Server. That conclusion was too strong: the sidebar's Recents list holds one draft row per server, all labelled "New session", and my scripted click was taking the first (local) row. Clicking the sandbox's own draft binds correctly — proven by the /home/codev answer above. What remains is a discoverability wrinkle rather than a wiring bug: two identically-labelled rows distinguished only by a small icon, where picking the wrong one silently runs on the real machine instead of the sandbox. Worth a UX pass, not a fix here.

🤖 Generated with Claude Code

The guest's model requests carry sec-fetch-mode (and friends), which a
host forwarding through Chromium's network stack may not set: Electron's
net.fetch rejected every such request with net::ERR_INVALID_ARGUMENT, so
chat inside a sandbox failed on attempt after attempt while a plain
curl through the same broker succeeded.

Strip sec-fetch-*, sec-ch-*, and proxy-* alongside the existing
hop-by-hop set. Header hygiene belongs in the broker rather than in one
host's fetch wrapper, since any Chromium-backed forwarder hits this.

Verified in-app: a sandbox session now answers "run pwd" with
/home/codev — inside the guest, through the broker, no prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@quickbeard
quickbeard merged commit 431b338 into main Aug 13, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the fix/broker-forbidden-headers branch August 13, 2026 06:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant