CVE-2026-40289
PraisonAI Browser Server allows unauthenticated WebSocket clients to hijack connected extension sessions
PraisonAI is a multi-agent teams system. In versions below 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents, the browser bridge (praisonai browser start) is vulnerable to unauthenticated remote session hijacking due to missing authentication and a bypassable origin check on its /ws WebSocket endpoint. The server binds to 0.0.0.0 by default and only validates the Origin header when one is present, meaning any non-browser client that omits the header is accepted without restriction. An unauthenticated network attacker can connect, send a start_session message, and the server will route it to the first idle browser-extension WebSocket (effectively hijacking that session) and then broadcast all resulting automation actions and outputs back to the attacker. This enables unauthorized remote control of connected browser automation sessions, leakage of sensitive page context and automation results, and misuse of model-backed browser actions in any environment where the bridge is network-reachable. This issue has been fixed in versions 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
9.1
PraisonAI Browser Server allows unauthenticated WebSocket clients to hijack connected extension sessions
PraisonAI is a multi-agent teams system. In versions below 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents, the browser bridge (praisonai browser start) is vulnerable to unauthenticated remote session hijacking due to missing authentication and a bypassable origin check on its /ws WebSocket endpoint. The server binds to 0.0.0.0 by default and only validates the Origin header when one is present, meaning any non-browser client that omits the header is accepted without restriction. An unauthenticated network attacker can connect, send a start_session message, and the server will route it to the first idle browser-extension WebSocket (effectively hijacking that session) and then broadcast all resulting automation actions and outputs back to the attacker. This enables unauthorized remote control of connected browser automation sessions, leakage of sensitive page context and automation results, and misuse of model-backed browser actions in any environment where the bridge is network-reachable. This issue has been fixed in versions 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents.
- Record
CVE-2026-40289- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-306
- Updated
- 2026-11-02
EUVDEUVD-2026-22211
9.1
PraisonAI is a multi-agent teams system. In versions below 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents, the browser bridge (praisonai browser start) is vulnerable to unauthenticated remote session hijacking due to missing authentication and a bypassable origin check on its /ws WebSocket endpoint. The server binds to 0.0.0.0 by default and only validates the Origin header when one is present, meaning any non-browser client that omits the header is accepted without restriction. An unauthenticated network attacker can connect, send a start_session message, and the server will route it to the first idle browser-extension WebSocket (effectively hijacking that session) and then broadcast all resulting automation actions and outputs back to the attacker. This enables unauthorized remote control of connected browser automation sessions, leakage of sensitive page context and automation results, and misuse of model-backed browser actions in any environment where the bridge is network-reachable. This issue has been fixed in versions 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents.
- Record
EUVD-2026-22211- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8x8f-54wf-vv92
9.1
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.
Details
The 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-44andsrc/praisonai/praisonai/browser/cli.py:25-30 - Optional-only origin validation:
src/praisonai/praisonai/browser/server.py:156-173 - Unauthenticated
start_sessionrouting:src/praisonai/praisonai/browser/server.py:237-240andsrc/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-423andsrc/praisonai/praisonai/browser/server.py:462-476
The handshake logic only checks origin when an Origin header is present:
origin = websocket.headers.get("origin")
if origin:
...
if not is_allowed:
await websocket.close(code=1008)
return
await websocket.accept()
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:
if client_conn != conn and client_conn.websocket and not client_conn.session_id:
await client_conn.websocket.send_text(json_mod.dumps(start_msg))
client_conn.session_id = session_id
sent_to_extension = True
break
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:
action_response = {
"type": "action",
"session_id": session_id,
**action,
}
for client_id, client_conn in self._connections.items():
if client_conn.session_id == session_id and client_conn != conn:
await client_conn.websocket.send_json(action_response)
I verified this on the latest local checkout: praisonai version 4.5.134 at commit 365f75040f4e279736160f4b6bdb2bdb7a3968d4.
PoC
I used tmp/pocs/poc.sh to reproduce the issue from a clean local checkout.
Run:
cd "/Users/r1zzg0d/Documents/CVE hunting/targets/PraisonAI"
./tmp/pocs/poc.sh
Expected vulnerable output:
[+] No-Origin client accepted: True
[+] Session forwarded to extension: True
[+] Action broadcast to attacker: True
[+] RESULT: VULNERABLE - unauthenticated client can hijack browser sessions.
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
Originheader. - Send
start_sessionfrom the unauthenticated websocket. - Observe that the server forwards
start_automationto the extension websocket. - Send an
observationfrom the extension websocket using the assignedsession_id. - Observe that the resulting
actionand completionstatusare delivered back to the unauthenticated initiating websocket.
tmp/pocs/poc.sh:
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$SCRIPT_DIR/../.."
exec uv run --no-project \
--with fastapi…
- Record
GHSA-8x8f-54wf-vv92- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-306
- Updated
- 2026-11-02
NVD
9.1
PraisonAI is a multi-agent teams system. In versions below 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents, the browser bridge (praisonai browser start) is vulnerable to unauthenticated remote session hijacking due to missing authentication and a bypassable origin check on its /ws WebSocket endpoint. The server binds to 0.0.0.0 by default and only validates the Origin header when one is present, meaning any non-browser client that omits the header is accepted without restriction. An unauthenticated network attacker can connect, send a start_session message, and the server will route it to the first idle browser-extension WebSocket (effectively hijacking that session) and then broadcast all resulting automation actions and outputs back to the attacker. This enables unauthorized remote control of connected browser automation sessions, leakage of sensitive page context and automation results, and misuse of model-backed browser actions in any environment where the bridge is network-reachable. This issue has been fixed in versions 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents.
- Record
CVE-2026-40289- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-306
- Updated
- 2026-11-02
OSV
9.1
PraisonAI Browser Server allows unauthenticated WebSocket clients to hijack connected extension sessions
PraisonAI is a multi-agent teams system. In versions below 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents, the browser bridge (praisonai browser start) is vulnerable to unauthenticated remote session hijacking due to missing authentication and a bypassable origin check on its /ws WebSocket endpoint. The server binds to 0.0.0.0 by default and only validates the Origin header when one is present, meaning any non-browser client that omits the header is accepted without restriction. An unauthenticated network attacker can connect, send a start_session message, and the server will route it to the first idle browser-extension WebSocket (effectively hijacking that session) and then broadcast all resulting automation actions and outputs back to the attacker. This enables unauthorized remote control of connected browser automation sessions, leakage of sensitive page context and automation results, and misuse of model-backed browser actions in any environment where the bridge is network-reachable. This issue has been fixed in versions 4.5.139 of PraisonAI and 1.5.140 of praisonaiagents.
- Record
CVE-2026-40289- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-306
- Updated
- 2026-11-02
OSVGHSA-8x8f-54wf-vv92
9.1
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.
Details
The 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-44andsrc/praisonai/praisonai/browser/cli.py:25-30 - Optional-only origin validation:
src/praisonai/praisonai/browser/server.py:156-173 - Unauthenticated
start_sessionrouting:src/praisonai/praisonai/browser/server.py:237-240andsrc/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-423andsrc/praisonai/praisonai/browser/server.py:462-476
The handshake logic only checks origin when an Origin header is present:
origin = websocket.headers.get("origin")
if origin:
...
if not is_allowed:
await websocket.close(code=1008)
return
await websocket.accept()
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:
if client_conn != conn and client_conn.websocket and not client_conn.session_id:
await client_conn.websocket.send_text(json_mod.dumps(start_msg))
client_conn.session_id = session_id
sent_to_extension = True
break
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:
action_response = {
"type": "action",
"session_id": session_id,
**action,
}
for client_id, client_conn in self._connections.items():
if client_conn.session_id == session_id and client_conn != conn:
await client_conn.websocket.send_json(action_response)
I verified this on the latest local checkout: praisonai version 4.5.134 at commit 365f75040f4e279736160f4b6bdb2bdb7a3968d4.
PoC
I used tmp/pocs/poc.sh to reproduce the issue from a clean local checkout.
Run:
cd "/Users/r1zzg0d/Documents/CVE hunting/targets/PraisonAI"
./tmp/pocs/poc.sh
Expected vulnerable output:
[+] No-Origin client accepted: True
[+] Session forwarded to extension: True
[+] Action broadcast to attacker: True
[+] RESULT: VULNERABLE - unauthenticated client can hijack browser sessions.
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
Originheader. - Send
start_sessionfrom the unauthenticated websocket. - Observe that the server forwards
start_automationto the extension websocket. - Send an
observationfrom the extension websocket using the assignedsession_id. - Observe that the resulting
actionand completionstatusare delivered back to the unauthenticated initiating websocket.
tmp/pocs/poc.sh:
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$SCRIPT_DIR/../.."
exec uv run --no-project \
--with fastapi…
- Record
GHSA-8x8f-54wf-vv92- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-306
- Updated
- 2026-11-02
OSVPYSEC-2026-467
9.1
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.
Details
The 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-44andsrc/praisonai/praisonai/browser/cli.py:25-30 - Optional-only origin validation:
src/praisonai/praisonai/browser/server.py:156-173 - Unauthenticated
start_sessionrouting:src/praisonai/praisonai/browser/server.py:237-240andsrc/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-423andsrc/praisonai/praisonai/browser/server.py:462-476
The handshake logic only checks origin when an Origin header is present:
origin = websocket.headers.get("origin")
if origin:
...
if not is_allowed:
await websocket.close(code=1008)
return
await websocket.accept()
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:
if client_conn != conn and client_conn.websocket and not client_conn.session_id:
await client_conn.websocket.send_text(json_mod.dumps(start_msg))
client_conn.session_id = session_id
sent_to_extension = True
break
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:
action_response = {
"type": "action",
"session_id" : session_id,
**action,
}
for client_id, client_conn in self._connections.items():
if client_conn.session_id == session_id and client_conn != conn:
await client_conn.websocket.send_json(action_response)
I verified this on the latest local checkout: praisonai version 4.5.134 at commit 365f75040f4e279736160f4b6bdb2bdb7a3968d4.
PoC
I used tmp/pocs/poc.sh to reproduce the issue from a clean local checkout.
Run:
cd "/Users/r1zzg0d/Documents/CVE hunting/targets/PraisonAI"
./tmp/pocs/poc.sh
Expected vulnerable output:
[+] No-Origin client accepted: True
[+] Session forwarded to extension: True
[+] Action broadcast to attacker: True
[+] RESULT: VULNERABLE - unauthenticated client can hijack browser sessions.
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
Originheader. - Send
start_sessionfrom the unauthenticated websocket. - Observe that the server forwards
start_automationto the extension websocket. - Send an
observationfrom the extension websocket using the assignedsession_id. - Observe that the resulting
actionand completionstatusare delivered back to the unauthenticated initiating websocket.
tmp/pocs/poc.sh:
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$SCRIPT_DIR/../.."
exec uv run --no-project \
…
- Record
PYSEC-2026-467- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
OSVPYSEC-2026-485
9.1
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.
Details
The 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-44andsrc/praisonai/praisonai/browser/cli.py:25-30 - Optional-only origin validation:
src/praisonai/praisonai/browser/server.py:156-173 - Unauthenticated
start_sessionrouting:src/praisonai/praisonai/browser/server.py:237-240andsrc/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-423andsrc/praisonai/praisonai/browser/server.py:462-476
The handshake logic only checks origin when an Origin header is present:
origin = websocket.headers.get("origin")
if origin:
...
if not is_allowed:
await websocket.close(code=1008)
return
await websocket.accept()
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:
if client_conn != conn and client_conn.websocket and not client_conn.session_id:
await client_conn.websocket.send_text(json_mod.dumps(start_msg))
client_conn.session_id = session_id
sent_to_extension = True
break
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:
action_response = {
"type": "action",
"session_id" : session_id,
**action,
}
for client_id, client_conn in self._connections.items():
if client_conn.session_id == session_id and client_conn != conn:
await client_conn.websocket.send_json(action_response)
I verified this on the latest local checkout: praisonai version 4.5.134 at commit 365f75040f4e279736160f4b6bdb2bdb7a3968d4.
PoC
I used tmp/pocs/poc.sh to reproduce the issue from a clean local checkout.
Run:
cd "/Users/r1zzg0d/Documents/CVE hunting/targets/PraisonAI"
./tmp/pocs/poc.sh
Expected vulnerable output:
[+] No-Origin client accepted: True
[+] Session forwarded to extension: True
[+] Action broadcast to attacker: True
[+] RESULT: VULNERABLE - unauthenticated client can hijack browser sessions.
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
Originheader. - Send
start_sessionfrom the unauthenticated websocket. - Observe that the server forwards
start_automationto the extension websocket. - Send an
observationfrom the extension websocket using the assignedsession_id. - Observe that the resulting
actionand completionstatusare delivered back to the unauthenticated initiating websocket.
tmp/pocs/poc.sh:
#!/bin/sh
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
cd "$SCRIPT_DIR/../.."
exec uv run --no-project \
…
- Record
PYSEC-2026-485- CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiagentsPyPI |
through 1.5.139 | 1.5.140 |
praisonaiPyPI |
through 4.5.138 | 4.5.139 |
praisonaiagentsPyPI |
before 1.5.140, plus listed versions 0.0.10.0.100.0.1000.0.1010.0.102 Show 45 moreShow less0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143 |
1.5.140 |
praisonaiPyPI |
before 4.5.139, plus listed versions 0.0.10.0.100.0.110.0.120.0.13 Show 45 moreShow less0.0.140.0.150.0.160.0.170.0.180.0.190.0.20.0.200.0.210.0.220.0.230.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.320.0.330.0.340.0.350.0.360.0.370.0.380.0.390.0.40.0.400.0.410.0.420.0.430.0.440.0.450.0.460.0.470.0.480.0.490.0.50.0.500.0.520.0.530.0.540.0.55 |
4.5.139 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-40289 is a critical vulnerability (CVSS 9.1) in Mervinpraison Praisonai, published April 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.42%, the 36th percentile. Fixed in praisonaiagents 1.5.140.
References
github.com/MervinPraison/PraisonAI/security/advisories/GHSA-8x8f-54wf-vv92
github.com/MervinPraison/PraisonAI/releases/tag/v4.5.139
nvd.nist.gov/vuln/detail/CVE-2026-40289
github.com/advisories/GHSA-8x8f-54wf-vv92
github.com/CVEProject/cvelistV5/tree/main/cves/2026/40xxx/CVE-2026-40289.json
github.com/MervinPraison/PraisonAI
pypi.org/project/praisonai
pypi.org/project/praisonaiagents
Left out here: 1,114 versions and the rest of 4 source descriptions. The JSON record has them.