http/websocket: a host chosen after registration can never be loopback
Merged
H3
7d ago+912+912+012345678901234567890123456789-47-47-01234567890123456789
Summary
Loopback is the trust boundary of the HTTP plugins: plain HTTP to loopback is allowed for local development, and only for requests the user addressed there. This closes the ways a host decided after registration could still land a request on the agent's own interface, and ports the same rules to the WebSocket plugin.
utcp-http 1.1.14 ā 1.1.15
- Call time: after path-parameter substitution, a resolved URL may be loopback only if the call template literally names a loopback host (
ensure_loopback_was_declared).http://localhost:8080/items/{id}keeps working;http://{host}:7777/admincalled withhost=127.0.0.1is refused before any request leaves. Applied to http, SSE and streamable HTTP. Templates may still take their host from an argument for any other destination; nothing is rejected at registration for having a placeholder host. - Remote manuals:
reject_remote_loopback_tool_urlsnow also refuses a loopback OAuth2token_url(the client-credentials POST is a request made on the manual's behalf). Covers GHSA-pg2m-h8f5-6ppq. - Remote OpenAPI specs: a loopback
tokenUrl, absolute or resolved from a relative/scheme-relative value, is refused for a spec fetched from a non-loopback origin. Covers GHSA-32vv-p32r-hxxp; flips the test that documented this gap. Server variables with adefaultare now resolved intoservers[0].url, as the OpenAPI spec intends. - The converter receives the final post-redirect URL as
spec_url, so a loopback discovery URL that redirected to a remote origin does not keep the local-dev exemption (the manual path already did this).
utcp-websocket 1.2.0 ā 1.2.1
- The session that opens the socket refuses to follow a redirect of the WebSocket upgrade (an
aiohttp.TraceConfigfails the connection on the first3xx, before the redirected request is built), so a3xxanswer can no longer steer the handshake and its auth headers at a hostensure_secure_ws_urlnever saw. Covers GHSA-6mfx-q9f2-jwx8. - WebSocket discovery applies the remote-loopback manual rule (tool URLs and
token_url), which it was missing.
GHSA-2r98-cv5v-cj65 (redirect into loopback) was already closed in 1.1.14 (#109).
Test plan
- New
test_remote_loopback_bypass.py(classifier table, call-time rule, remote-manual token_url, converter rules, end-to-end attack and control per protocol against an aiohttp server that records hits) - New
test_websocket_redirect_security.py(upgrade redirect refused with the internal target never contacted and the API key never sent; direct handshake control; remote-manual rule) -
pytest core plugins: 626 passed, 9 skipped before the last revision; http + websocket plugins 364 passed after it
š¤ Generated with Claude Code