← Back to PRs

#16060: fix(browser): require relay auth token for /extension WebSocket and /extension/status

by coygeek open 2026-02-14 07:18 View on GitHub →
stale size: XS trusted-contributor
## Fix Summary Require the existing `relayAuthToken` for `/extension` WebSocket connections and `/extension/status` HTTP endpoint, matching the security model already enforced for `/cdp`. ## Issue Linkage Fixes #16059 ## Security Snapshot - CVSS v3.1: 8.1 (High) - CVSS v4.0: 8.8 (High) ## Implementation Details ### Files Changed - `src/browser/extension-relay.ts` (+11/-0) — Add token auth checks to `/extension` WS upgrade and `/extension/status` HTTP handler - `src/browser/extension-relay.test.ts` (+22/-3) — Update existing tests to pass auth headers; add regression test for unauthenticated rejection ### Technical Analysis The `/extension` WebSocket endpoint accepted connections authenticated only by a loopback address check and a bypassable Origin header check. The `/cdp` endpoint on the same server already required a cryptographic `relayAuthToken` via the `x-openclaw-relay-token` header. This fix extends the same token check to both the `/extension` WebSocket upgrade path and the `/extension/status` HTTP endpoint, closing the authentication gap that allowed local processes to impersonate the Chrome extension. ## Validation Evidence - Command: `pnpm build && pnpm check && npx vitest run src/browser/extension-relay.test.ts` - Status: all passed (5/5 tests, 0 lint warnings, 0 errors) ## Risk and Compatibility Non-breaking for the Chrome extension — it already receives the relay auth token via `getChromeExtensionRelayAuthHeaders()` and can pass it for `/extension` connections. Only unauthenticated local processes are blocked. ## AI-Assisted Disclosure - AI-assisted: yes - Model: Claude Opus 4.6 <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR extends relay auth token checks to the `/extension` WebSocket upgrade and `/extension/status` HTTP endpoints, matching the existing pattern enforced for `/cdp`. While the security intent is sound, there is a critical compatibility issue: the Chrome extension (`assets/chrome-extension/background.js`) connects to `/extension` using the browser's native `WebSocket` API, which **does not support custom HTTP headers**. The extension currently uses `new WebSocket(wsUrl)` with no auth token. This means the new auth check will reject all real Chrome extension connections. - The `/cdp` endpoint's auth works because it is connected to by server-side Node.js code (via `cdp.helpers.ts` / `ws` library), which does support custom headers - The `/extension` endpoint is connected to by a Chrome extension service worker running in the browser, where `WebSocket` only accepts a URL and optional sub-protocols - The PR description's claim that "the extension already receives the relay auth token via `getChromeExtensionRelayAuthHeaders()`" is incorrect — that function is server-side Node.js code, not accessible from the browser extension - The tests pass because they use the Node.js `ws` library (which supports headers), not the browser `WebSocket` API - An alternative auth mechanism is needed for the extension path (e.g., query parameter token, sub-protocol, or HTTP-based token exchange) <h3>Confidence Score: 1/5</h3> - This PR will break the Chrome extension's ability to connect to the relay server in production. - The auth mechanism (custom HTTP header on WebSocket upgrade) is incompatible with the browser WebSocket API used by the Chrome extension. While the Node.js-based tests pass, real extension connections will be rejected with 401. The security goal is valid but the implementation needs an auth mechanism that works from browser contexts. - Pay close attention to `src/browser/extension-relay.ts` (lines 476-481) and `assets/chrome-extension/background.js` (line 68) — the auth mechanism is incompatible with the browser WebSocket API. <sub>Last reviewed commit: 4eab139</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs