Conversation
There was a problem hiding this comment.
5 issues found across 16 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/communication_protocols/websocket/src/utcp_websocket/websocket_communication_protocol.py">
<violation number="1" location="plugins/communication_protocols/websocket/src/utcp_websocket/websocket_communication_protocol.py:239">
P1: `allow_redirects` is not a valid `ws_connect` argument, so connection setup now fails at runtime. This breaks all WebSocket manual registration/tool calls that reach this code path.</violation>
</file>
<file name="plugins/communication_protocols/http/src/utcp_http/sse_communication_protocol.py">
<violation number="1" location="plugins/communication_protocols/http/src/utcp_http/sse_communication_protocol.py:225">
P2: `ClientSession` is not closed on successful SSE streams; only error paths call `await session.close()`. This can accumulate unclosed connections during normal streaming usage.</violation>
</file>
<file name="plugins/communication_protocols/http/src/utcp_http/openapi_converter.py">
<violation number="1" location="plugins/communication_protocols/http/src/utcp_http/openapi_converter.py:379">
P2: This rejects valid OpenAPI 3.0 OAuth2 specs that use relative `tokenUrl`. Guard security validation to absolute URLs (or resolve first) instead of failing conversion on relative URLs.</violation>
</file>
<file name="plugins/communication_protocols/http/src/utcp_http/_security.py">
<violation number="1" location="plugins/communication_protocols/http/src/utcp_http/_security.py:212">
P1: Redirect handling forwards credentials to new origins because auth-related kwargs are never scrubbed between hops. This can leak API keys/bearer/basic credentials and cookies to attacker-controlled HTTPS redirect targets.</violation>
</file>
<file name="plugins/communication_protocols/gql/src/utcp_gql/gql_communication_protocol.py">
<violation number="1" location="plugins/communication_protocols/gql/src/utcp_gql/gql_communication_protocol.py:141">
P1: Redirect protection is applied too late: it runs after entering the GqlClient context, but schema fetch can already happen during `__aenter__`. This leaves an SSRF/credential-leak path on the initial GraphQL request.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| # aiohttp's ws_connect defaults to following HTTP | ||
| # redirects on the upgrade handshake; refuse so a | ||
| # 3xx response cannot land us on a different host. | ||
| allow_redirects=False, |
There was a problem hiding this comment.
P1: allow_redirects is not a valid ws_connect argument, so connection setup now fails at runtime. This breaks all WebSocket manual registration/tool calls that reach this code path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/communication_protocols/websocket/src/utcp_websocket/websocket_communication_protocol.py, line 239:
<comment>`allow_redirects` is not a valid `ws_connect` argument, so connection setup now fails at runtime. This breaks all WebSocket manual registration/tool calls that reach this code path.</comment>
<file context>
@@ -198,7 +232,11 @@ async def _get_connection(self, call_template: WebSocketCallTemplate) -> ClientW
+ # aiohttp's ws_connect defaults to following HTTP
+ # redirects on the upgrade handshake; refuse so a
+ # 3xx response cannot land us on a different host.
+ allow_redirects=False,
)
self._connections[provider_key] = ws
</file context>
| current_method = "GET" | ||
| kwargs.pop("json", None) | ||
| kwargs.pop("data", None) | ||
| current_url = next_url |
There was a problem hiding this comment.
P1: Redirect handling forwards credentials to new origins because auth-related kwargs are never scrubbed between hops. This can leak API keys/bearer/basic credentials and cookies to attacker-controlled HTTPS redirect targets.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/communication_protocols/http/src/utcp_http/_security.py, line 212:
<comment>Redirect handling forwards credentials to new origins because auth-related kwargs are never scrubbed between hops. This can leak API keys/bearer/basic credentials and cookies to attacker-controlled HTTPS redirect targets.</comment>
<file context>
@@ -110,3 +111,108 @@ def ensure_secure_url(url: str, *, context: Optional[str] = None) -> None:
+ current_method = "GET"
+ kwargs.pop("json", None)
+ kwargs.pop("data", None)
+ current_url = next_url
+ hops += 1
+
</file context>
| headers = await self._prepare_headers(manual_call_template) | ||
| transport = AIOHTTPTransport(url=manual_call_template.url, headers=headers) | ||
| async with GqlClient(transport=transport, fetch_schema_from_transport=True) as session: | ||
| self._disable_transport_redirects(transport) |
There was a problem hiding this comment.
P1: Redirect protection is applied too late: it runs after entering the GqlClient context, but schema fetch can already happen during __aenter__. This leaves an SSRF/credential-leak path on the initial GraphQL request.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/communication_protocols/gql/src/utcp_gql/gql_communication_protocol.py, line 141:
<comment>Redirect protection is applied too late: it runs after entering the GqlClient context, but schema fetch can already happen during `__aenter__`. This leaves an SSRF/credential-leak path on the initial GraphQL request.</comment>
<file context>
@@ -94,17 +100,45 @@ async def _prepare_headers(
headers = await self._prepare_headers(manual_call_template)
transport = AIOHTTPTransport(url=manual_call_template.url, headers=headers)
async with GqlClient(transport=transport, fetch_schema_from_transport=True) as session:
+ self._disable_transport_redirects(transport)
schema = session.client.schema
tools: List[Tool] = []
</file context>
| method, url, params=query_params, headers=request_headers, | ||
| auth=auth, cookies=cookies, json=json_data, data=data, timeout=None | ||
| auth=auth, cookies=cookies, json=json_data, data=data, | ||
| timeout=None, allow_redirects=False, |
There was a problem hiding this comment.
P2: ClientSession is not closed on successful SSE streams; only error paths call await session.close(). This can accumulate unclosed connections during normal streaming usage.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/communication_protocols/http/src/utcp_http/sse_communication_protocol.py, line 225:
<comment>`ClientSession` is not closed on successful SSE streams; only error paths call `await session.close()`. This can accumulate unclosed connections during normal streaming usage.</comment>
<file context>
@@ -208,10 +212,26 @@ async def call_tool_streaming(self, caller, tool_name: str, tool_args: Dict[str,
method, url, params=query_params, headers=request_headers,
- auth=auth, cookies=cookies, json=json_data, data=data, timeout=None
+ auth=auth, cookies=cookies, json=json_data, data=data,
+ timeout=None, allow_redirects=False,
)
+ if 300 <= response.status < 400:
</file context>
| ensure_secure_url( | ||
| token_url, context="OAuth2 tokenUrl in OpenAPI spec" | ||
| ) |
There was a problem hiding this comment.
P2: This rejects valid OpenAPI 3.0 OAuth2 specs that use relative tokenUrl. Guard security validation to absolute URLs (or resolve first) instead of failing conversion on relative URLs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At plugins/communication_protocols/http/src/utcp_http/openapi_converter.py, line 379:
<comment>This rejects valid OpenAPI 3.0 OAuth2 specs that use relative `tokenUrl`. Guard security validation to absolute URLs (or resolve first) instead of failing conversion on relative URLs.</comment>
<file context>
@@ -368,6 +368,17 @@ def _create_auth_from_scheme(self, scheme: Dict[str, Any], scheme_name: str) ->
+ # runtime check in ``_handle_oauth2`` also
+ # enforces this -- see
+ # GHSA-8cp3-qxj6-px34.
+ ensure_secure_url(
+ token_url, context="OAuth2 tokenUrl in OpenAPI spec"
+ )
</file context>
| ensure_secure_url( | |
| token_url, context="OAuth2 tokenUrl in OpenAPI spec" | |
| ) | |
| parsed_token_url = urlparse(token_url) | |
| if parsed_token_url.scheme and parsed_token_url.netloc: | |
| ensure_secure_url( | |
| token_url, context="OAuth2 tokenUrl in OpenAPI spec" | |
| ) |
Summary by cubic
Hardened network security across HTTP, GraphQL, and WebSocket protocols to block SSRF and MITM. Enforces secure URL schemes, validates each redirect hop, and checks OAuth2 token URLs; adds tests and minor version bumps.
Bug Fixes
https:///wss://, and only loopback forhttp:///ws://.safe_request_with_redirectsfor manual discovery, tool calls, and OAuth2; disable redirects for SSE/streaming andgqltransport.token_urlat runtime and during OpenAPI conversion; reject internal or plain-HTTP non-loopback URLs.Dependencies
utcp-httpto 1.1.4,utcp-gqlto 1.1.1,utcp-websocketto 1.1.1.aiohttp>=3.8toutcp-gql.Written for commit fc3268e. Summary will update on new commits.