{"id":"CVE-2026-44338","aliases":["GHSA-6rmh-7xcm-cpxj","PYSEC-2026-2904"],"title":"PraisonAI ships and generates a legacy API server with authentication disabled by default, allowing unauthenticated workflow execution","summary":"PraisonAI ships and generates a legacy API server with authentication disabled by default, allowing unauthenticated workflow execution","severity":"high","cvss":7.3,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L","vendor":"praisonai","product":"praisonai","ecosystem":"pip","affected":["praisonai >= 2.5.6, < 4.6.34"],"patched":["praisonai 4.6.34"],"published":"2026-05-11","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-6rmh-7xcm-cpxj","references":[{"url":"https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6rmh-7xcm-cpxj"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44338"},{"url":"https://github.com/MervinPraison/PraisonAI"}],"tags":["osv","pip","exploit-available"],"epss":0.28571,"epssPercentile":0.98096,"ingestedAt":"2026-07-13T18:57:54.005Z","exploits":{"github":2,"githubRepos":["https://github.com/HORKimhab/CVE-2026-44338","https://github.com/rootdirective-sec/CVE-2026-44338-Lab"],"nuclei":["CVE-2026-44338"],"checkedAt":"2026-09-21T15:29:01.939Z"},"exploitAvailable":true,"slug":"CVE-2026-44338","body":"## Overview\n\n### Summary\nPraisonAI ships a legacy Flask API server with authentication disabled by default. When that server is used, any caller that can reach it can access `/agents` and trigger the configured `agents.yaml` workflow through `/chat` without providing a token.\n\n### Details\nThe vulnerable server is the shipped `src/praisonai/api_server.py` entrypoint.\n\n- `AUTH_ENABLED = False` and `AUTH_TOKEN = None` are hard-coded at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:15)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:15).\n- `check_auth()` returns `True` whenever authentication is disabled, so both protected routes fail open by design at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:18)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:18).\n- `POST /chat` only checks that the request JSON contains a `message` key and then runs `PraisonAI(agent_file=\"agents.yaml\").run()` at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:31)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:31).\n- `GET /agents` is guarded by the same no-op authentication check and returns agent metadata at [[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:55)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/[src/praisonai/api_server.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:66):55).\n- When launched directly, the same script binds to `0.0.0.0:8080` at [src/praisonai/api_server.py](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/api_server.py:66).\n\nThe deploy subsystem keeps the same insecure authentication default:\n\n- `APIConfig` defaults `auth_enabled` to `False` in [[src/praisonai/praisonai/deploy/models.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/models.py:23)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/models.py:23).\n- The generated sample API deployment YAML recommends `host: 0.0.0.0` together with `auth_enabled: false` in [[src/praisonai/praisonai/deploy/schema.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/schema.py:108)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/deploy/schema.py:108).\n\nFor scope clarity: the newer `serve agents` command is safer by default, because it binds to `127.0.0.1` and supports `--api-key` in [[src/praisonai/praisonai/cli/commands/serve.py](https://github.com/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/cli/commands/serve.py:155)](/Users/shmulc/Stuff/tmp/first-cve/scans/variant-hunt/PraisonAI/src/praisonai/praisonai/cli/commands/serve.py:155). This report is about the shipped legacy API server and the generated/sample API deployment path above.\n\nVersion scope:\n\n- `v2.5.6` already ships the same `src/praisonai/api_server.py` implementation.\n- The current PyPI release on May 1, 2026 is `4.6.33`, and it still ships the same unauthenticated server logic.\n\n### PoC\nThe following route-level reproduction was verified locally and proves that the shipped `api_server.py` exposes `/agents` and `/chat` without authentication.\n\n1. From the repository root, create a throwaway environment with the server's direct Flask dependencies:\n\n```bash\npython3 -m venv /tmp/praisonai-ghsa-venv\n/tmp/praisonai-ghsa-venv/bin/pip install flask flask-cors\n```\n\n2. Execute the shipped `src/praisonai/api_server.py` under a minimal stub for `praisonai.PraisonAI` so only the server auth logic is exercised:\n\n```bash\n/tmp/praisonai-ghsa-venv/bin/python - <<'PY'\nimport importlib.util\nimport pathlib\nimport sys\nimport types\n\nstub = types.ModuleType(\"praisonai\")\n\nclass DummyPraisonAI:\n    def __init__(self, agent_file=\"agents.yaml\"):\n        self.agent_file = agent_file\n    def run(self):\n        return {\"ran\": True, \"agent_file\": self.agent_file}\n\nstub.PraisonAI = DummyPraisonAI\nsys.modules[\"praisonai\"] = stub\n\npath = pathlib.Path(\"src/praisonai/api_server.py\").resolve()\nspec = importlib.util.spec_from_file_location(\"api_server_local\", path)\nmod = importlib.util.module_from_spec(spec)\nspec.loader.exec_module(mod)\n\nclient = mod.app.test_client()\nprint(client.get(\"/agents\").status_code, client.get(\"/agents\").get_data(as_text=True))\nprint(client.post(\"/chat\", json={\"message\": \"hello\"}).status_code, client.post(\"/chat\", json={\"message\": \"hello\"}).get_data(as_text=True))\nPY\n```\n\n3. Observed result:\n\n```text\n200 {\"agent_file\":\"agents.yaml\",\"agents\":[\"default\"]}\n200 {\"response\":{\"agent_file\":\"agents.yaml\",\"ran\":true},\"status\":\"success\"}\n```\n\nBoth endpoints succeed without any `Authorization` header.\n\n### Impact\nAny reachable caller can invoke the legacy API server's protected functionality without a token.\n\nAt minimum, this allows:\n\n- unauthenticated enumeration of the configured agent file through `/agents`\n- unauthenticated triggering of the locally configured `agents.yaml` workflow through `/chat`\n- repeated consumption of model/API quota and any other side effects performed by that workflow\n- exposure of whatever result `PraisonAI.run()` returns to the unauthenticated caller\n\nThis is not the same as arbitrary prompt injection by itself, because the current `/chat` handler ignores the submitted `message` value and simply runs the configured workflow. The impact therefore depends on what the operator's `agents.yaml` is allowed to do, but the authentication bypass is unconditional in the shipped legacy server.\n\n## Affected packages\n\n- `praisonai >= 2.5.6, < 4.6.34`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `praisonai 4.6.34`","depth":"midnight","depthScore":58,"depthScoreParts":{"impact":40.2,"likelihood":5.7,"exploitation":12,"ransomware":0},"changes":[{"seq":5227,"id":"CVE-2026-44338","ts":1788887254543,"field":"exploit_available","old":"false","new":"true"},{"seq":4110,"id":"CVE-2026-44338","ts":1788886370411,"field":"exploit_available","old":"true","new":"false"},{"seq":2885,"id":"CVE-2026-44338","ts":1788883036760,"field":"exploit_available","old":"false","new":"true"},{"seq":1914,"id":"CVE-2026-44338","ts":1788882439818,"field":"exploit_available","old":"true","new":"false"},{"seq":1003,"id":"CVE-2026-44338","ts":1788881874147,"field":"exploit_available","old":"false","new":"true"}]}