Skip to content

http/websocket: a host chosen after registration can never be loopback - #116

Merged
h3xxit merged 5 commits into
devfrom
fix/loopback-via-placeholders-and-token-url
Oct 25, 2026
Merged

h3xxit merged 5 commits into
devfrom
fix/loopback-via-placeholders-and-token-url

Conversation

@h3xxit

@h3xxit h3xxit commented Oct 25, 2026

Copy link
Copy Markdown
Member

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/admin called with host=127.0.0.1 is 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_urls now also refuses a loopback OAuth2 token_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 a default are now resolved into servers[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.TraceConfig fails the connection on the first 3xx, before the redirected request is built), so a 3xx answer can no longer steer the handshake and its auth headers at a host ensure_secure_ws_url never 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

h3xxit and others added 2 commits September 16, 2026 18:14
utcp-http 1.1.15: at call time a resolved URL may be loopback only when the
template literally names a loopback host, so a `{host}` placeholder filled
with 127.0.0.1 is refused before any request leaves; a remote manual or
OpenAPI spec may not route the OAuth2 token POST at loopback either; server
variables resolve to their defaults; the converter judges the spec by its
final post-redirect URL.

utcp-websocket 1.2.1: the upgrade never follows a redirect, and a manual
discovered from a remote origin may not point tool calls or a token URL at
loopback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 14 files

Tip: Get a Slack DM when cubic finishes reviewing your PRs. Connect Slack

View guided diff | Turn on auto-fix | Re-trigger cubic

- the remote-manual and converter loopback rules also classify templates
  whose port is a placeholder (``http://localhost:{port}/x``), which do not
  parse as URLs yet name loopback literally
- an explicit ``base_url`` override lifts the OpenAPI token-URL rule, as it
  lifts the server-URL rule
- a failed WebSocket discovery closes the socket and session it opened
- README: name the override that actually exists

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 7 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

h3xxit and others added 2 commits October 25, 2026 14:18
A socket already in the pool may be carrying another registration's or a
tool call's traffic, so the cleanup added for failed discoveries is scoped
to connections the discovery itself opened.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…arse error

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@h3xxit
h3xxit changed the base branch from main to dev October 25, 2026 13:40
@h3xxit
h3xxit merged commit 8b3d695 into dev Oct 25, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant