User MCP servers are now on by default in OSS. An agent author controls the mcpServers[].url in their config, and the runner sends the run's Agenta-resolved named secrets as request headers to that URL. We want a self-hoster to leave user MCP on without an author-controlled config being able to reach an internal host and exfiltrate a credential.
Today the SSRF guard checks only the URL scheme and the host literal. validateUserMcpUrl in services/agent/src/engines/sandbox_agent/mcp.ts requires https and rejects loopback, link-local, cloud-metadata (169.254.169.254), and private-IP host literals, with a AGENTA_AGENT_MCP_HOST_ALLOWLIST opt-out. That is scheme + host only. It does not:
- pin the connection to the host it validated, so a hostname that resolves to a public IP at check time can resolve to
127.0.0.1, 169.254.169.254, or a private IP when the harness actually connects (DNS rebinding); and
- re-validate redirects, so a validated public
https URL can 3xx-redirect the credentialed request to an internal host.
The blast radius is bounded: secrets ride headers only to https hosts, and the capability is config-trust. But with OSS default-on this is a real residual.
Proposed hardening:
- Resolve the URL host once, validate the resolved IP(s) against the same internal-range rules, and pin the connection to that IP so no second DNS lookup happens at connect time.
- Set
redirect: "manual" (or the equivalent) and re-run the origin/IP validation on each redirect hop before following it.
Reference the guard and its docstring at services/agent/src/engines/sandbox_agent/mcp.ts (validateUserMcpUrl and isInternalHost).
Decision for the default-on PR: harden first, or ship default-on with this residual tracked here.
User MCP servers are now on by default in OSS. An agent author controls the
mcpServers[].urlin their config, and the runner sends the run's Agenta-resolved named secrets as request headers to that URL. We want a self-hoster to leave user MCP on without an author-controlled config being able to reach an internal host and exfiltrate a credential.Today the SSRF guard checks only the URL scheme and the host literal.
validateUserMcpUrlinservices/agent/src/engines/sandbox_agent/mcp.tsrequireshttpsand rejects loopback, link-local, cloud-metadata (169.254.169.254), and private-IP host literals, with aAGENTA_AGENT_MCP_HOST_ALLOWLISTopt-out. That is scheme + host only. It does not:127.0.0.1,169.254.169.254, or a private IP when the harness actually connects (DNS rebinding); andhttpsURL can3xx-redirect the credentialed request to an internal host.The blast radius is bounded: secrets ride headers only to
httpshosts, and the capability is config-trust. But with OSS default-on this is a real residual.Proposed hardening:
redirect: "manual"(or the equivalent) and re-run the origin/IP validation on each redirect hop before following it.Reference the guard and its docstring at
services/agent/src/engines/sandbox_agent/mcp.ts(validateUserMcpUrlandisInternalHost).Decision for the default-on PR: harden first, or ship default-on with this residual tracked here.