Skip to content

Trial sandboxes get their own network behind an allowlisting proxy - #1214

Merged
JacobStephens2 merged 4 commits into
issue-977from
issue-1013
Oct 24, 2026
Merged

JacobStephens2 merged 4 commits into
issue-977from
issue-1013

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

Each Trial sandbox now has its own network, holding only its own loopback. An allowlisting CONNECT proxy outside bwrap reaches only that Harness's provider hosts from #1009. It checks names before DNS, refuses loopback and the host's addresses (including IPv4-mapped IPv6), and dials only the checked addresses.

Trial network (loopback only)
  Harness → HTTPS_PROXY → socat → bound Unix socket
Host network
  CONNECT proxy → provider allowlist → DNS/address guard → provider

A Harness that ignores the proxy has no route; known network/DNS failures produce a Void Trial. Trial invocations disable web tools for Claude, Codex, Grok, Muse and OpenCode. agy's missing search-off control remains a Spec decision in #1215. Protocol and hand-written site pages describe the Trial sandbox.

CONNECT tunnels remain reusable after long model calls and forward EOF while allowing the provider's final response. A private network adapter lets the successful CONNECT/request/response test use local sockets without weakening production policy.

Closes #1013

Test seams

  • Sandbox preparation and launch (sandbox::prepare / Launch::command): proxy routing and isolation from the host network.
  • CONNECT proxy (proxy::start), address policy (address_refusal), and tunnel forwarding (read_head → relay over local sockets): allowed traffic, refusal, and connection reuse.
  • Harness invocation (Adapter::trial_session) and Trial result protocol (thirdshift --trial): web-search settings and Void Trials.

Evidence

  • Before: the reviewers' exact local reproductions failed: mapped loopback was allowed, forwarding stopped after 11 idle seconds, and closing the client did not send EOF upstream.
    After: retained regressions pass: proxy::tests::mapped_loopback_and_host_addresses_are_refused, proxy::tests::connect_tunnels_remain_writable_after_waiting_for_a_model_response, and proxy::tests::connect_tunnels_forward_half_closes_and_the_final_response. The original idle and closure reproduction commands also pass after the fixes.
  • cargo test --bin thirdshift -- proxy::tests sandbox::tests harness::: 211 passed, 0 failed, 2 existing ignored tests. Includes sandbox/provider reachability, GitHub/loopback/host refusal, no route without the proxy, per-Harness web-tool settings, and deterministic CONNECT request/response forwarding.
  • cargo fmt --check and cargo clippy --all-targets -- -D warnings: passed.
  • cargo test --bin thirdshift sandbox::tests::a_sandboxed_process_reaches_an_allowed_host_through_the_proxy -- --exact --nocapture: passed with no environment skip; the sandbox reached the real allowed host through the proxy.
  • All GitHub checks passed on 3bc774e9, including the Linux Rust suite.
  • /usr/bin/time -p scripts/affected-tests issue-977: passed — 43 site tests and 2,526 Rust tests, with 58 existing Rust skips. Wall time 2,341.88 s (39m 02s), including compilation; the Rust build took 2m 05s and nextest execution took 2,146.862 s. No full-suite rerun was started after CI succeeded on this head.

Merge Danger

Door: two-way

Reverting restores the previous Trial networking; no data migration.

Blast Radius: Trials

Ordinary Runs keep their existing networking and web tools. Sandboxed Trials require socat; unavailable sandbox dependencies produce a Void Trial. OpenCode Trials must name a supported provider. THIRDSHIFT_TRIAL_NO_SANDBOX retains its existing escape-hatch behavior.

Unaddressed findings

Standards

None. All four findings were addressed: idle forwarding, injectable network I/O, the duplicate agy override, and EOF propagation. Each behavior finding was reproduced with the reviewer's written command before fixing it; regressions are retained.

Spec

  • S3 — agy web search remains enabled: deferred, not refuted. agy inherits src/harness/adapter.rs:42-44, which returns the unchanged code-review invocation; src/harness/agy.rs:156-159 records the limitation. The initial and follow-up source checks failed as reported; a passing suite is not evidence that search is disabled. docs/research/eval-sandboxes.md:831 records no CLI/settings switch and server-side google_search beyond the proxy. Whether to block agy Trials or permit that exception belongs to the Day shift/Spec authors. Existing open #1215 already records this finding, its evidence, PR Trial sandboxes get their own network behind an allowlisting proxy #1214 and why that call is not the implementer's; it has only needs-triage. No duplicate issue was created. The acceptance criterion remains unmet for agy.

Security

None. Scoped security review (guidance mode, one session, no delegated auditors) of the issue-1013 diff against pinned merge base c63c2a46: read src/proxy.rs, the src/sandbox.rs proxy/relay/namespace wiring, the trial_session overrides in src/harness/ plus the default in src/harness/adapter.rs, the Trial gating in src/session.rs, and the void-DNS strings in src/trial.rs, with the local-IPC and resource-exhaustion attack-class guidance (the repo has no SECURITY.md or threat model). Temporary adversarial CONNECT probes (userinfo, paths, queries, bracketed names, IP literals, wrong methods, mixed clean/loopback/host/unspecified/mapped DNS sets) all stayed refused while clean allowlisted names tunneled; the probes were reverted afterward. proxy::tests (11 passed), harness:: (177 passed), sandbox:: (23 passed), --test trial (17 passed). No introduced finding had a failing proof-of-concept, so nothing was fixed or left unaddressed here.

Security review outcome

No unaddressed introduced findings.

Review coverage

Independent Standards and Spec reviews used issue-977 (c63c2a4622bddc3f9bc8484c8054547069ae62e2) as the fixed point. Each axis read all 17 changed files, including supporting standards/domain/ADR context, with one follow-up on the fixes. No generated-output exclusions and no unread changed files. The final EOF shutdown fix was made and validated by the implementer after the Standards follow-up raised it.

Final reports, including initial/follow-up findings, dispositions and each axis's combined files-read list:

  • .thirdshift-review-Xuk0v5/standards.md
  • .thirdshift-review-Xuk0v5/spec.md

Standards: 4 addressed, worst found P1 idle forwarding (fixed). Spec: 2 addressed, 1 deferred, worst remaining P1 agy web-search control (#1215).

Built with codex · gpt-6.1-sol · xhigh

Each sandbox now unshares its network, holding only its own loopback. An allowlisting CONNECT proxy runs outside it on a Unix socket bound into the sandbox; a socat relay inside turns 127.0.0.1:3128 into that socket, and the Harness reaches its provider through HTTPS_PROXY. The proxy allows only that Harness's provider hosts from #1009's lists and refuses names resolving to loopback or to the host's own addresses. Trial sessions switch each Harness's web search off by its own flag (agy has none); OpenCode's Trial config denies webfetch and websearch.
Void a Trial whose Harness ignores the proxy at the DNS step too: DNS and unreachable-network wordings now map to session_unavailable, with trial-level cases. Share one provider-host table and one provider-name parse between the proxy and the sandbox. Correct the hand-written site pages that said Trials run without a sandbox.
@JacobStephens2 JacobStephens2 changed the title Trial sandboxes get their own network behind an allowlisting proxy TITLE-TEST-MARKER Oct 24, 2026
@JacobStephens2 JacobStephens2 changed the title TITLE-TEST-MARKER Trial sandboxes get their own network behind an allowlisting proxy Oct 24, 2026
…id=6178ceda-3074-4c42-a5f0-d7f9fdc9a360]: Subscription quota exhausted. Your usage window resets at 2026-10-24T03:28:16Z. (rate_limit_error))

2026-10-24T02:17:33Z, host stephens. Uncommitted work at the time of failure is included in this commit.
@JacobStephens2
JacobStephens2 marked this pull request as draft October 24, 2026 02:17
@JacobStephens2
JacobStephens2 marked this pull request as ready for review October 24, 2026 03:36
@JacobStephens2
JacobStephens2 merged commit 5536630 into issue-977 Oct 24, 2026
18 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-1013 branch October 24, 2026 03:42
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