GHSA-8x8f-54wf-vv92: PraisonAI Browser Server allows unauthenticated WebSocket clients to hijack connected extension sessions
Summary`praisonai browser start` exposes the browser bridge on `0.0.0.0` by default, and its `/ws` endpoint accepts websocket clients that omit the `Origin` header entirely. An unauthenticated network client can connect as a fake controller, send `start_session`, cause the server to forward `start_automation` to another connected browser-extension websocket, and receive the resulting action/status stream back over that hijacked session. This allows unauthorized remote use of a connected browser automation session without any credentials.DetailsThe issue is in the browser bridge trust model. The code assumes that websocket peers are trusted local components, but that assumption is not enforced.Relevant code paths:Default network exposure: `src/praisonai/praisonai/browser/server.py:38-44` and `src/praisonai/praisonai/browser/cli.py:25-30`Optional-only origin validation: `src/praisonai/praisonai/browser/server.py:156-173`Unauthenticated `start_session` routing: `src/praisonai/praisonai/browser/server.py:237-240` and `src/praisonai/praisonai/browser/server.py:289-302`Cross-connection forwarding to any other idle websocket: `src/praisonai/praisonai/browser/server.py:344-356`Broadcast of action output back to the initiating unauthenticated client: `src/praisonai/praisonai/browser/server.py:412-423` and `src/praisonai/praisonai/browser/server.py:462-476`The handshake logic only checks origin when an `Origin` header is present:This means a non-browser client can omit `Origin` completely and still be accepted.After that, any connected client can send `{"type":"start_session", ...}`. The server then looks for the first other websocket without a session and sends it a `start_automation` message:When the extension-side connection responds with an observation, the resulting action is broadcast to every websocket with the same `session_id`, including the unauthenticated initiating client:I verified this on the latest local checkout: `praisonai` version `4.5.134` at commit `365f75040f4e279736160f4b6bdb2bdb7a3968d4`.PoCI used `tmp/pocs/poc.sh` to reproduce the issue from a clean local checkout.Run:Expected vulnerable output:Step-by-step reproduction:Start the local browser bridge from the checked-out source tree.Connect one websocket as a stand-in extension using a valid `chrome-extension://<32-char-id>` origin.Connect a second websocket with no `Origin` header.Send `start_session` from the unauthenticated websocket.Observe that the server forwards `start_automation` to the extension websocket.Send an `observation` from the extension websocket using the assigned `session_id`.Observe that the resulting `action` and completion `status` are delivered back to the unauthenticated initiating websocket.`tmp/pocs/poc.sh`:`tmp/pocs/poc.py`:`tmp/pocs/poc.py` starts a temporary local server, stubs the browser agent, opens both websocket roles, and prints the final vulnerability conditions explicitly.PoC Video:https://github.com/user-attachments/assets/df078542-bbdc-4341-b438-89c86365009eImpactThis is an unauthenticated remote-control vulnerability in the browser automation bridge. Any network client that can reach the exposed bridge can impersonate the controller side of the workflow, hijack an available connected extension session, and receive automation output from that hijacked session. In real deployments, this can allow unauthorized browser actions, misuse of model-backed automation, and leakage of sensitive page context or automation results.Who is impacted:Operators who run `praisonai browser start` with the default host bindingUsers with an active connected browser extension sessionEnvironments where the bridge is reachable from other hosts on the networkRecommended FixSuggested remediations:Require explicit authentication for every websocket client connecting to `/ws`.Reject websocket handshakes that omit `Origin`, unless they are using a separate authenticated localhost-only transport.Bind the browser bridge to `127.0.0.1` by default and require explicit operator opt-in for non-loopback exposure.Do not route `start_session` to “the first other idle connection”; instead, pair authenticated controller and extension clients explicitly.
CVSS v3.1: 9.1