Files
zed/crates/http_proxy/src/http_proxy.rs
T
Cameron Mcloughlin fbe484282d agent: Sandbox DNS rebinding and redirect cleanup (#60941)
Tidies up the host resolution code a bit and closes some small gaps

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-14 15:29:30 +00:00

58 lines
2.9 KiB
Rust

//! In-process HTTP/HTTPS proxy that enforces a hostname allowlist.
//!
//! Spawned per terminal command from the parent process. The sandbox is
//! configured to permit network only to this proxy's port; everything the
//! sandboxed command tries to reach the network for has to come through here.
//!
//! The proxy:
//!
//! - Speaks HTTP CONNECT for HTTPS tunnels and HTTP forward proxying for
//! plain HTTP. Other protocols cannot reach it (the seatbelt rule limits
//! the sandboxed process to this one TCP destination, and this proxy only
//! speaks HTTP).
//! - Checks the destination hostname against an allowlist of exact hostnames
//! and leading-`*.` subdomain wildcards. Unless the allowlist allows any
//! host, IP-literal targets are denied, and hostnames whose DNS resolves
//! only into loopback / private / link-local space are denied too
//! (DNS-rebinding protection — the proxy runs outside the sandbox, so it
//! must not reopen the local network the Seatbelt rule closed off).
//! - Pins each TCP connection to the destination approved for its first
//! request: directly (to the vetted resolved addresses) or via a CONNECT
//! tunnel through an optional upstream HTTP proxy from the parent's
//! environment (`HTTPS_PROXY` / `HTTP_PROXY`), honoring `NO_PROXY`. Plain
//! HTTP is also tunneled when chaining, so keep-alive requests after the
//! first can never be routed to a different host by the upstream.
//! - Reports per-connection events (allowed, denied, completed) over an
//! mpsc supplied by the caller.
//!
//! ## Trust assumptions
//!
//! The proxy's sole client is model-driven code running inside the sandbox —
//! exactly the party the sandbox distrusts — and the proxy itself runs inside
//! the editor process. It therefore caps request header sizes and concurrent
//! connections, and bounds connect/handshake waits with timeouts, so a
//! malicious command can't exhaust the editor's memory, threads, or file
//! descriptors through it. Bandwidth is deliberately not capped; the
//! command's lifetime bounds it.
//!
//! ## "No proxy here" principle
//!
//! The agent and tools running inside the sandbox should not need to know
//! that a proxy is in front of them. The only response code the proxy
//! synthesizes itself is `511 Network Authentication Required`, used solely
//! for policy denials (with `Via:` and `Proxy-Status:` headers and a
//! plain-text body explaining the policy decision). Other failure modes
//! (upstream connection failure, malformed input from the client, etc.) are
//! handled by silently closing the connection — same behavior the client
//! would see from a direct network failure, no proxy fingerprint.
mod allowlist;
mod pinned_host;
mod proxy;
pub use allowlist::{Allowlist, HostPattern, HostPatternError};
pub use pinned_host::{PinnedHost, PinnedHostError, is_forbidden_ip};
pub use proxy::{
DenyReason, ProxyConfig, ProxyEvent, ProxyHandle, RequestMethod, RequestOutcome, UpstreamProxy,
};