Skip to content

hardening: http_request/web_fetch should never fail open to http.DefaultTransport (shared egress seam) #308

Description

@initializ-mk

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:

  1. A helper like security.EgressTransportOrRefuse(ctx) (http.RoundTripper, error) that returns an error when nil, or
  2. 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

  • http_request no longer falls back to http.DefaultTransport when no egress client is present.
  • web_fetch and http_request share one seam for this decision.
  • Test pinning the no-egress-client path for http_request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    forge-coreAffects the forge-core library (runtime, security, types, llm, mcp, auth)securitySecurity vulnerability fixestoolsAffects builtin or adapter tools (cli_execute, http_request, browser, etc.)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions