Skip to content

fix(gateway): require a pairing token on WebSocket upgrade - #139

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-identification-8d88
Draft

cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-identification-8d88

Conversation

@cursor

@cursor cursor Bot commented Oct 25, 2026

Copy link
Copy Markdown

WebSocket upgrade accepted any client that named the registered ws subprotocol, with no pairing token. The same handshake path also rejected the dashboard, which offered only bearer.<token>.

Bug and impact

libwebsockets 4.3 delivers LWS_CALLBACK_HTTP_CONFIRM_UPGRADE to the HTTP protocol callback (protocols[0]), before it binds the ws protocol. The token check lived only in ws_callback, so it never ran. Returning 0 from http_callback allowed the upgrade.

A client that sent Sec-WebSocket-Protocol: ws and no Authorization header received 101 Switching Protocols and could push {"type":"message"} into the agent (shell and other tools). Reproduced on libwebsockets 4.3.3: http_cb got CONFIRM_UPGRADE, then ws_cb got FILTER_PROTOCOL_CONNECTION and ESTABLISHED, and ws_cb never saw CONFIRM_UPGRADE.

The dashboard opened the socket with only bearer.<token>. That name is not a registered protocol, so libwebsockets closed the handshake (No supported protocol) and WebChat never connected.

Fix

  • Reject the upgrade in http_callback on LWS_CALLBACK_HTTP_CONFIRM_UPGRADE unless the request carries a stored pairing token.
  • Apply the same check in ws_callback on LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION (this one does run on the ws protocol, before the 101) and keep the old confirm case as a backstop.
  • Read bearer.<token> from anywhere in the Sec-WebSocket-Protocol list, not only as a prefix. Authorization: Bearer still works.
  • The dashboard now offers ws and bearer.<token>. libwebsockets selects ws and echoes that name.

Validation

tests/test_gateway_http.c covers the handshake against a live server: ws alone, no subprotocol, bearer.<token> alone, both orders of ws plus a valid token, a wrong subprotocol token, and Authorization: Bearer with ws (valid and invalid). The unauthenticated ws case returned 101 before the fix and is rejected after it.

CC=gcc CI=true make test passed, including test_gateway_http.

Notes

Clients that followed the old README and offered only bearer.<token> still cannot connect. They need ws in the offered list (or Authorization: Bearer plus subprotocol ws).

Open in Web View Automation 

libwebsockets 4.3 delivers HTTP_CONFIRM_UPGRADE on the HTTP
protocol, so the check in ws_callback never ran. A client that
offered the registered "ws" subprotocol was accepted with no
token and could inject WebChat messages. The dashboard offered
only bearer.<token>, which is not a registered protocol, so its
handshake never completed.

Check the token on the HTTP confirm callback and on
FILTER_PROTOCOL_CONNECTION. Browsers offer both "ws" and
bearer.<token>.

This branch has not been deployed

No deployments
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