Skip to content

Security updates#87

Merged
h3xxit merged 3 commits into
mainfrom
dev
Jun 17, 2026
Merged

Security updates#87
h3xxit merged 3 commits into
mainfrom
dev

Conversation

@h3xxit

@h3xxit h3xxit commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary by cubic

Post‑audit hardening for HTTP, GraphQL, and WebSocket to block SSRF and credential leaks. Tightens URL/loopback checks, secures redirects and OAuth2 token handling (incl. relative token URLs), patches GraphQL introspection, and hardens WebSocket message templating; adds tests and bumps versions.

  • Bug Fixes

    • Broaden loopback detection and replace prefix checks with hostname/IP validators: https:///wss:// allowed; http:///ws:// loopback only (covers 0.0.0.0, ::, full 127.0.0.0/8, IPv4‑mapped IPv6).
    • Harden redirects via safe_request_with_redirects: strip auth‑like headers, request body, and proxy creds on cross‑origin; normalize default ports for same‑origin; used for manual discovery, tool calls, OAuth2 across HTTP/SSE/WS.
    • Validate OAuth2 token_url at runtime and during OpenAPI conversion; resolve relative tokenUrl against spec_url and validate.
    • GraphQL: disable redirects before schema introspection so the first request is protected.
    • WebSocket: enforce “WSS or loopback” with hostname validators, remove invalid redirect flag, and escape JSON‑shaped string templates to prevent injection.
    • SSE: ensure ClientSession closes on success to prevent leaks.
  • Dependencies

    • Bump utcp-http to 1.1.6, utcp-gql to 1.1.3, utcp-websocket to 1.1.3.
    • Add aiohttp>=3.8 to utcp-gql.

Written for commit 2cc003b. Summary will update on new commits.

Review in cubic

@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.

5 issues found across 16 files

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

Re-trigger cubic

Comment thread plugins/communication_protocols/http/src/utcp_http/_security.py
Comment thread plugins/communication_protocols/gql/src/utcp_gql/gql_communication_protocol.py Outdated
Comment thread plugins/communication_protocols/http/src/utcp_http/openapi_converter.py Outdated
…gins

Follow-up to the GHSA-9qhg-99ww-9mqc / GHSA-8cp3-qxj6-px34 /
GHSA-ppx3-28rw-8fpf fixes shipped in 1.1.4 / 1.1.1 / 1.1.1. A
post-fix audit surfaced six bugs ranging from real exploits to
availability papercuts; this commit lands all of them.

Loopback detection now covers wildcard binds and the IPv4-mapped
IPv6 forms that bypass Python's stdlib `is_loopback`.

  * `is_loopback_url` returns True for `0.0.0.0`, `::`, the entire
    `127.0.0.0/8` range, and `::ffff:127.x.x.x` (IPv4-mapped IPv6
    loopback). On Linux a connect to any of these lands on local
    loopback; the OpenAPI converter's "remote spec declares a
    loopback server" SSRF defense now actually catches them.

Cross-origin redirect helper now scrubs everything the OAuth / API-
key threat model requires:

  * Auth header set extended beyond the canonical four to include
    `X-Api-Key`, `Api-Key`, `X-Auth-Token`, `X-Access-Token`,
    `X-Csrf-Token`, `X-Xsrf-Token`, `X-Amz-Security-Token`,
    `X-Goog-Api-Key`, plus a regex catch-all matching ad-hoc
    auth-looking names (`X-MyApp-Token`, `Custom-Bearer`, etc.).
    Without this, `ApiKeyAuth` headers under arbitrary names rode
    through cross-origin 302s.
  * Request body (`json` / `data`) is now dropped on cross-origin
    redirect. 307 / 308 preserve method + body per RFC 7231, which
    would otherwise resend an OAuth2 token POST (containing
    `client_secret`) to an attacker-controlled `Location`. Browsers
    prompt the user; we have no user, so we strip.
  * `proxy_auth` is now also dropped on cross-origin so a proxy's
    Basic credentials are not forwarded to a new origin's proxy.
  * Same-origin check normalises default ports so `https://x` and
    `https://x:443` are recognised as the same origin -- the
    previous comparison treated them as different origins and
    silently stripped `Authorization` on legitimate same-origin
    redirects.

WebSocket protocol:

  * `ws_connect` no longer receives the invalid `allow_redirects`
    kwarg the previous fix added -- aiohttp's `ws_connect` does not
    accept it. The pre-flight URL validation via
    `ensure_secure_ws_url` already constrains the handshake; the
    one-shot upgrade naturally rejects 3xx.
  * `_substitute_placeholders` gained a `json_string_context` mode.
    When the `message` field of `WebSocketCallTemplate` is a string
    that looks like JSON (`{...}` / `[...]`), string substitutions
    are run through `json.dumps(...)[1:-1]` so a tool_arg
    containing `"` cannot break out of the surrounding string
    literal and inject extra JSON fields. Dict templates were
    already safe via the outer `json.dumps`.

SSE protocol:

  * `ClientSession` is now closed in a `finally` block on the
    success path of `call_tool_streaming`. The previous version
    closed it only in `except`, leaking the session on every
    normal-completion stream.

OpenAPI converter:

  * `tokenUrl` validation handles relative references per the
    OpenAPI 3.0 / 3.1 spec. Absolute URLs still go through
    `ensure_secure_url` eagerly; relative URLs are resolved
    against `spec_url` if available (and the resolved URL is
    validated). Without a `spec_url` the eager check defers to
    the runtime `_handleOAuth2` validation. Fixes the regression
    where `"tokenUrl": "/oauth/token"` was rejected as
    non-loopback / non-HTTPS.

GraphQL protocol:

  * `_SecureAIOHTTPTransport` subclass patches `session.post` with
    `allow_redirects=False` from inside `connect()`, before the
    `GqlClient.__aenter__` schema-introspection request fires.
    The previous fix patched the session AFTER entering the
    context, leaving the very first GraphQL request unprotected.

Regression tests cover:
  * loopback detection of `0.0.0.0`, `::`, `127.0.0.2`,
    `::ffff:127.0.0.1`, plus negatives;
  * OpenAPI converter rejecting wildcard / 127.0.0.x / IPv4-mapped
    server URLs from a remote spec;
  * cross-origin redirect stripping custom `X-Api-Key` /
    `X-MyApp-Token` while preserving a benign `X-Trace-Id`;
  * cross-origin 307 dropping the OAuth POST body so
    `client_secret` cannot reach the attacker;
  * same-origin redirect with explicit default port keeping
    `Authorization`;
  * WebSocket JSON-shaped string template escaping tool_arg `"`
    characters, dict template remaining safe, and non-JSON string
    templates substituting raw (back-compat).

Version bumps:
  * utcp-http 1.1.4 -> 1.1.5
  * utcp-gql 1.1.1 -> 1.1.2
  * utcp-websocket 1.1.1 -> 1.1.2

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

6 issues found across 13 files (changes from recent commits).

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

Re-trigger cubic

Comment thread plugins/communication_protocols/websocket/src/utcp_websocket/_security.py Outdated
Comment thread plugins/communication_protocols/http/src/utcp_http/_security.py Outdated
Comment thread plugins/communication_protocols/gql/src/utcp_gql/_security.py Outdated
Comment thread plugins/communication_protocols/http/tests/test_redirect_security.py Outdated
Comment thread plugins/communication_protocols/http/src/utcp_http/_security.py Outdated
Comment thread plugins/communication_protocols/http/tests/test_redirect_security.py Outdated
@h3xxit h3xxit merged commit 61df48c into main Jun 17, 2026
19 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