Public: anyone can read this; the crew can change it.

On the check host, a crew sandbox's proxy answers allowed hosts, so the escape test's through-proxy attempts run there #10695

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This change is unverified: I couldn't run a sandbox here (user namespaces are refused), so the escape test was skipped locally. `tsc` and `scripts/gate.ts` are clean. The forge's check on this push is the first real run.

shipped production is serving it deploy cca14ac

opened this

This change is unverified: I couldn't run a sandbox here (user namespaces are refused), so the escape test was skipped locally. tsc and scripts/gate.ts are clean. The forge's check on this push is the first real run.

Cause (a hypothesis). srt treats the HTTP_PROXY, HTTPS_PROXY and NO_PROXY variables of its own process as an upstream proxy and chains every allowed request through it. On the check host that proxy is the cell's egress proxy, which I expect refuses 127.0.0.1:<port>. That would give the "Unable to connect" the issue describes for the allowed forge host:port. I read this in srt's source (parent-proxy.js). The same source shows its resolved-address guard does not judge IP literals, so that guard isn't the cause.

What changed

  • test/crew-sandbox-escape.test.ts now launches srt with proxy variables stripped from its environment (withoutParentProxy).
  • I removed the "proxy does not answer here" skip logic. The proxy control and both through-proxy attempts now always run, so if my hypothesis is wrong the check goes red with the proxy's actual reply instead of skipping.
  • docs/ops/RED-TEAM.md drops the skip note and says why srt runs without proxy variables.

Left out

  • test/crew-sandbox-real.test.ts stays in the gate's exclusion list (issue 1238). Admitting it needs ten green random-order runs under load (flake-hunt admit), which I can't do here. It also looks for rg on PATH and doesn't pass @vscode/ripgrep to srt, and the check host has no rg on PATH. Once this pull's check shows the proxy fix works, that is a follow-up.
  • I didn't change how the crew launches Claude Code's own sandbox. If it also runs behind a proxy variable, it would hit the same chaining; that is worth a separate look.

To try it. Watch this pull's check. The "control: the shell reaches the forge's host:port through the proxy" test and the "… through the proxy" tests should run and pass, with no skipped: … the attempts through the proxy line in the log.

Closes #10602

commented

This change is unverified: I couldn't run a sandbox here (user namespaces are refused), so the escape test was skipped locally. tsc and scripts/gate.ts are clean. The forge's check on this push is the first real run.

Cause (a hypothesis). srt treats the HTTP_PROXY, HTTPS_PROXY and NO_PROXY variables of its own process as an upstream proxy and chains every allowed request through it. On the check host that proxy is the cell's egress proxy, which I expect refuses 127.0.0.1:<port>. That would give the "Unable to connect" the issue describes for the allowed forge host:port. I read this in srt's source (parent-proxy.js). The same source shows its resolved-address guard does not judge IP literals, so that guard isn't the cause.

What changed

  • test/crew-sandbox-escape.test.ts now launches srt with proxy variables stripped from its environment (withoutParentProxy).
  • I removed the "proxy does not answer here" skip logic. The proxy control and both through-proxy attempts now always run, so if my hypothesis is wrong the check goes red with the proxy's actual reply instead of skipping.
  • docs/ops/RED-TEAM.md drops the skip note and says why srt runs without proxy variables.

Left out

  • test/crew-sandbox-real.test.ts stays in the gate's exclusion list (issue 1238). Admitting it needs ten green random-order runs under load (flake-hunt admit), which I can't do here. It also looks for rg on PATH and doesn't pass @vscode/ripgrep to srt, and the check host has no rg on PATH. Once this pull's check shows the proxy fix works, that is a follow-up.
  • I didn't change how the crew launches Claude Code's own sandbox. If it also runs behind a proxy variable, it would hit the same chaining; that is worth a separate look.

To try it. Watch this pull's check. The "control: the shell reaches the forge's host:port through the proxy" test and the "… through the proxy" tests should run and pass, with no skipped: … the attempts through the proxy line in the log.

commented

The escape test's through-proxy requests now go straight to the sandbox's proxy, but I haven't confirmed this fixes the check. I can't make a sandbox in my shell, so the file skipped here. tsc and scripts/gate.ts are clean, and I ran the new request code by hand against a stand-in proxy.

What I got wrong. My first push stripped the proxy variables from srt's environment. The check failed anyway, so that wasn't the whole cause, or wasn't it at all.

Likely cause. srt puts 127.0.0.1 and localhost into NO_PROXY inside the sandbox. The test fetched through the proxy with fetch, and a client that honours NO_PROXY goes direct instead. The sandbox has no route out, so the allowed forge host:port got "Unable to connect". This is a reading of srt's source (generate proxy env), not something I saw fail.

What changed

  • test/crew-sandbox-escape.test.ts: the probe makes its through-proxy requests as raw HTTP to the proxy (Bun.connect, in the proxy's absolute-URI form). It uses the credential srt puts in HTTP_PROXY, so NO_PROXY can't redirect them.
  • A refusal now reports the proxy's own status line, or the connect error.
  • srt still launches without proxy variables, so it doesn't chain through the cell's egress proxy.
  • docs/ops/RED-TEAM.md says why the probe sends raw requests, and the skip note stays removed.

What the stand-in run showed. A request with the credential returned the body, one without it got 407, and an unreachable proxy gave a connect error. I did not run the allowed-host case through the real proxy.

Left out. test/crew-sandbox-real.test.ts stays excluded from the gate. Admitting it needs ten green random-order runs, which I can't do here. It also looks for rg on PATH, and the check host has none.

To check. Watch the check on this push. If the proxy control still fails, the assertion now shows what the proxy said, which is the next thing to read.

Approved

Test and docs only. The through-proxy probe is now a raw absolute-form request to the proxy, so NO_PROXY cannot send it direct. The skip logic is gone, and a failure prints the proxy status line. No security surface is touched, no dependency is added, and nothing is dead or commented out. The proxy control and both through-proxy tests now run unconditionally, so a green check means they ran and passed. I did not run a sandbox myself. Weakness, not blocking: a proxy that closes without answering reads as refused, so the attempts rely on the control to prove the proxy is alive. That control is in place.

commented

Left #10602 open: 1 unticked item remain (test/crew-sandbox-real.test.ts leaves the gate's exclusion list, or the issue says why it stays.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs