{"schema":"threatline.record/2","record":{"id":"GHSA-6vww-5m28-f26v","aliases":["GHSA-6vww-5m28-f26v"],"summary":"WebSocket sessions do not verify TLS certificates, exposing API keys and OAuth tokens to on-path attackers","details":"## Summary\n\nReqLLM versions 1.9.0 through 1.28.0 do not verify TLS certificates for secure WebSocket connections. Every affected `wss://` connection accepts any certificate.\n\nAn attacker on the network path can terminate the connection with a certificate that the client does not trust. The attacker can then read the `authorization: Bearer ...` header and read or change the full WebSocket session.\n\n## Affected versions\n\n- Affected: `>= 1.9.0, <= 1.28.0`\n- Fixed: `>= 1.29.0`\n\nThe issue was introduced in [v1.9.0](https://github.com/agentjido/req_llm/releases/tag/v1.9.0) with OpenAI WebSocket streaming and Realtime support in [PR #559](https://github.com/agentjido/req_llm/pull/559).\n\n## Details\n\n`ReqLLM.Streaming.WebSocketSession.Client.start/3` in `lib/req_llm/streaming/web_socket_session/client.ex` called `WebSockex.start/4` without `:insecure`, `:cacerts`, or `:ssl_options`.\n\nWebSockex 0.5.1 defaults to `insecure: true`. This setting becomes `:ssl.connect(..., verify: :verify_none)`. The ReqLLM option allowlist also did not pass TLS options, so callers could not enable certificate verification.\n\nThe affected paths are:\n\n- OpenAI Responses WebSocket sessions.\n- OpenAI Realtime sessions.\n- OpenAI Codex WebSocket sessions.\n\nThese paths can send an OpenAI API key or an OAuth access token in the `authorization` header.\n\nHTTP streaming through Finch and the HTTP/2 duplex session through Mint are not affected. These clients verify certificates by default.\n\n## Impact\n\nAn attacker must have an on-path position. Examples include a compromised network, hostile Wi-Fi, or DNS or BGP interference.\n\nSuccessful exploitation can disclose:\n\n- The API key or OAuth access token.\n- Prompts, responses, tool calls, and tool results.\n- Other data sent through the session.\n\nThe attacker can also change model output and tool calls before the application receives them.\n\n## Reproduction\n\n1. Start a TLS WebSocket endpoint with a self-signed certificate.\n2. Point an affected ReqLLM WebSocket session at that endpoint.\n3. Start the session with an `authorization: Bearer <token>` header.\n4. In an affected version, the TLS handshake succeeds and the endpoint receives the authorization header.\n5. A request to the same endpoint through an unaffected HTTP client fails because the certificate authority is not trusted.\n\n## Resolution\n\nUpgrade to [ReqLLM 1.29.0](https://github.com/agentjido/req_llm/releases/tag/v1.29.0) or later.\n\nVersion 1.29.0:\n\n- Enables peer verification for secure WebSocket connections.\n- Uses the system certificate authorities by default.\n- Verifies the certificate hostname.\n- Fails closed if the system trust store is not available.\n- Keeps support for an explicit custom certificate authority or custom SSL options.\n- Applies the secure settings to `wss://` URLs and WebSocket URLs built from an `https://` base URL.\n- Adds tests for an untrusted certificate, an incorrect hostname, and explicit custom trust settings.\n\nThe primary fix is in [commit c7264fc0](https://github.com/agentjido/req_llm/commit/c7264fc0217fbacfb5562234ff790b8e4462de76). The `https://` base URL follow-up is in [PR #1140](https://github.com/agentjido/req_llm/pull/1140) and [commit edfe1108](https://github.com/agentjido/req_llm/commit/edfe11085710e67134d66e158f94aabfdcb41c1d).\n\n## Workarounds\n\nUpgrade to version 1.29.0 or later. If an upgrade is not possible, do not use the affected OpenAI Responses, Realtime, or Codex WebSocket paths. HTTP streaming is not affected.\n\n## Upstream note\n\nThe underlying WebSockex behavior is tracked in [WebSockex issue #1](https://github.com/dominicletz/websockex/issues/1). This advisory covers ReqLLM only.\n\n## Credit\n\nReported by [Neil Berkman](https://github.com/neilberkman). Found with Claude Mythos.\n","categories":["Unclassified"],"product":"req_llm","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/agentjido/req_llm/security/advisories/GHSA-6vww-5m28-f26v","source_modified_at":"2026-10-22T20:50:53Z","fetched_at":"2026-10-24T05:41:33.294140Z","package":{"ecosystem":"erlang","name":"req_llm"},"range":">= 1.9.0, <= 1.28.0"}],"fixed_versions":[],"cvss":7.4,"severity":"High","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-22T20:50:53Z","modified_at":"2026-10-22T20:50:53Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/agentjido/req_llm/security/advisories/GHSA-6vww-5m28-f26v","fetched_at":"2026-10-24T05:41:33.294140Z","source_modified_at":"2026-10-22T20:50:53Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-22T20:50:53Z","content_hash":"8bfa7521776e4f795c0cd229b5561c3e799e246d58607a9a38e1841322edae20"}}
