Follow-up from PR #307 (#266) review.
Problem
EgressTransportFromContext(ctx) returns nil when no egress client is installed, and http.Client{Transport: nil} silently falls back to http.DefaultTransport — bypassing the egress allowlist and SSRF/DNS-rebinding protections entirely.
In #307, web_fetch was hardened to refuse when the egress transport is nil (it's a new, default-on, LLM-URL-driven surface where fail-open is worst). But http_request still fails open at the same seam:
client := &http.Client{
Transport: security.EgressTransportFromContext(ctx), // nil → DefaultTransport
...
}
The runtime always installs the egress client via WithEgressClient before tools run, so this isn't reachable in a normal agent run — but it's latent fail-open in a library-level tool, and the two tools now behave inconsistently.
Proposal
Harden at the shared seam so both tools (and any future HTTP tool) get it for free. Options:
- A helper like
security.EgressTransportOrRefuse(ctx) (http.RoundTripper, error) that returns an error when nil, or
EgressTransportFromContext returns a security.SafeTransport (SSRF-protecting, no allowlist) fallback instead of nil — never DefaultTransport.
Then route http_request (and web_fetch) through it, and add a test pinning the no-egress-client path for http_request (mirrors TestWebFetch_RefusesWithoutEgressClient).
Acceptance
Follow-up from PR #307 (#266) review.
Problem
EgressTransportFromContext(ctx)returnsnilwhen no egress client is installed, andhttp.Client{Transport: nil}silently falls back tohttp.DefaultTransport— bypassing the egress allowlist and SSRF/DNS-rebinding protections entirely.In #307,
web_fetchwas hardened to refuse when the egress transport is nil (it's a new, default-on, LLM-URL-driven surface where fail-open is worst). Buthttp_requeststill fails open at the same seam:The runtime always installs the egress client via
WithEgressClientbefore tools run, so this isn't reachable in a normal agent run — but it's latent fail-open in a library-level tool, and the two tools now behave inconsistently.Proposal
Harden at the shared seam so both tools (and any future HTTP tool) get it for free. Options:
security.EgressTransportOrRefuse(ctx) (http.RoundTripper, error)that returns an error when nil, orEgressTransportFromContextreturns asecurity.SafeTransport(SSRF-protecting, no allowlist) fallback instead of nil — neverDefaultTransport.Then route
http_request(andweb_fetch) through it, and add a test pinning the no-egress-client path forhttp_request(mirrorsTestWebFetch_RefusesWithoutEgressClient).Acceptance
http_requestno longer falls back tohttp.DefaultTransportwhen no egress client is present.web_fetchandhttp_requestshare one seam for this decision.http_request.