fix(daemon): confine remote HTTP trust boundaries - #2111
Conversation
|
Not ready. [P2] Bound public Maestro HTTP responses before buffering them. The remaining trust routing is sound: host paths are default-denied under auth, explicit roots are realpath-confined, upload IDs remain server-owned, and public URL resolution is validated and pinned per hop. This head is also |
d71e354 to
36f8305
Compare
|
Addressed in 36f8305 and rebased onto current main (04758c9).
Validation: pnpm check:affected --run completed with all runnable checks passing; focused trust/HTTP/Maestro tests pass 53/53. GitHub-authoritative device/provider lanes remain CI-owned. |
Size Report
npm unpacked components
Startup median (7 runs, lower is better):
Top changed chunks:
Top changed packed files
|
|
Re-reviewed exact head |
|
The reviewed head is unchanged, but the newly advanced |
36f8305 to
4964a49
Compare
|
Re-reviewed a44016b. P1: remove AGENT_DEVICE_HTTP_ALLOW_HOST_PATH_INSTALL / root confinement and reject unbacked remote kind:path requests unconditionally. Although realpath confinement is correct in isolation, #2097’s later maintainer triage explicitly superseded the original issue sketch: no host-path opt-in without separate approval; uploaded-artifact IDs remain the supported route. Dependency: #2098 is explicitly unready until open #2095 establishes the shared remote trust posture. The public-only Maestro routing itself looks sound (per-hop validation/pinning, redirect/header controls, response cap), but the PR is not ready while that dependency and P1 remain. Android Smoke failed in an unrelated system-automation wait for 'Alert result: cancelled'; this looks like device/lane flake rather than causal evidence against this diff, but still needs a green rerun. Also add the required size justification for current +7.3 kB npm unpacked. |
a44016b to
c4439c8
Compare
|
Re-reviewed exact head |
|
Exact head c4439c8 has no new trust-code finding and #2095/#2104 is now merged, but it is not ready. Current main conflicts in http-server.ts and cli-help-topics.test.ts; the rebase must preserve #2104's unconditional fail-closed tenant attestation before layering remote network/path trust policy. Coverage is a real owner-action failure: changed-line coverage is 95/140 = 67.86%, with unproved credential/malformed-host rejection and 38 uncovered run-script-http-child lines across URL/redirect/downgrade/parser/child-failure paths. Add focused negative/boundary proof, then rerun exact-head gates. Update the stale body dependency and size text (+5.6 kB latest, not +7.3 kB). No readiness label until rebase and green coverage. |
c4439c8 to
2dd7fb6
Compare
|
Addressed in |
|
Reviewed exact head P1 — the documented remote proxy path still permits SSRF. The new tests cover an auth-hook-configured direct HTTP server and a directly supplied The public-only implementation itself—shared DNS validation, address pinning, redirect revalidation, downgrade rejection, cleanup, and response cap—looks sound. Current Android smoke failure appears unrelated, but exact-head CI is not fully green yet. |
|
Addressed in 5349a5a. agent-device proxy now stamps x-agent-device-network-access: public-only on forwarded /rpc requests. The daemon resolves that marker at the request boundary, so a reused local daemon cannot run proxy-originated Maestro HTTP requests through unrestricted fetch. Missing markers preserve unrestricted no-hook local HTTP behavior; invalid or duplicate markers fail closed. Added a proxy-to-daemon regression using the shared Maestro HTTP policy: loopback is refused without reaching the target, while a public literal is approved and dispatched through the shared transport seam. Removing only the marker reproduced the old 200 response and loopback reachability. Validation: focused trust/HTTP/proxy tests 46/46, pnpm check:quick, and planted-red proof pass. pnpm check:affected --run ran the full fail-open local selection with 2,695 tests passing, but exited on two unhandled existing Apple replay environment-teardown/module-load errors in session-replay-repair-record-exclusion.test.ts; the new proxy tests passed. GitHub exact-head checks are pending. |
|
Reviewed exact head The proxy now supplies the restrictive network marker itself and does not forward a client marker; the daemon accepts only Completed exact-head checks are green. Pending smoke jobs are CI status only, not a code-review blocker. |
|
Exact head |
|
Verified exact head Remote host paths are rejected at both proxy and daemon boundaries; uploaded IDs resolve only to daemon-owned, tenant-checked artifacts. The real proxy-to-daemon regression proves loopback refusal before connection. Exact-head CI is fully green, including coverage, package/wire compatibility, provider-backed integration, live web, and Android/iOS/macOS/Linux smokes. Size is +4.1 kB unpacked (+2.1 kB gzip JS), chiefly the necessary 3.0 kB synchronous child; the final revision removed 1,034 lines from the previous head. Ready for merge. Residual risk: live DNS-rebinding and enterprise-proxy permutations cannot be exhaustively exercised in CI, but the approved address is pinned on each Fetch hop and proxy tunnelling is covered. |
|
Summary
Fix the remote HTTP trust boundaries for #2097 and #2098 with one daemon-owned attenuation bit:
publicNetworkOnly; local loopback and socket behavior stays unchanged.http.postpreserves native Fetch redirects, headers, methods, and decompression while every hop is public-address checked and pinned, including through configured proxies. HTTPS downgrade and decoded responses over 8 MiB are refused.This revision removes the manual redirect engine and generalized install-source transport rewrite. Against the PR base it changes 17 files (+869/-56); against the previous PR head it removes 1,034 lines. npm unpacked size is +4.1 kB, of which the required synchronous Maestro child entry is 3.0 kB.
Closes #2098
Validation
pnpm check:affected --run(all runnable gates passed; GitHub-authoritative native/provider lanes remain CI-owned).