GHSA-8444-4FHQ-FXPQ

Vulnerability from github – Published: 2026-05-29 22:29 – Updated: 2026-05-29 22:29
VLAI
Summary
PraisonAI `deploy --type api` emits a Flask server with authentication disabled by default
Details

Summary

CVE-2026-44338 (GHSA-6rmh-7xcm-cpxj) documents that PraisonAI ships a code-generator (praisonai.deploy.api.generate_api_server_code) that emits a Flask API server with authentication disabled by default. Users who follow the documented quickstart (praisonai deploy --type api) get a server that:

  • binds to 0.0.0.0 per the recommended sample YAML
  • exposes /chat and /agents endpoints
  • runs praisonai.run() on user-supplied JSON input — LLM orchestration with the API key materials present in the process environment
  • does not require any authentication

The PyPI wheel praisonai==4.6.33 (current @latest) still ships the generator with auth_enabled defaulting to False. The fix shape is opt-in via APIConfig(auth_enabled=True, auth_token=...).

Details

Anchor (file:line:symbol)

  • Vulnerable artifact: praisonai==4.6.33 on PyPI.
  • Defaults: praisonai/deploy/models.py:29auth_enabled: bool = Field(default=False, ...); praisonai/deploy/models.py:30auth_token: Optional[str] = Field(default=None, ...).
  • Generator: praisonai/deploy/api.py:40AUTH_ENABLED = {config.auth_enabled}; api.py:41AUTH_TOKEN = {repr(config.auth_token)}; api.py:43-49def check_auth(): if not AUTH_ENABLED: return True.
  • CLI entry: documented as praisonai deploy --type api (vendor README); produces the generator output above with no flag required to suppress the warning, because no warning is emitted.

Vulnerable code (verbatim from installed wheel)

# praisonai/deploy/models.py (praisonai==4.6.33)
class APIConfig(BaseModel):
    host: str = Field(default="127.0.0.1", description="Server host")
    port: int = Field(default=8005, description="Server port")
    cors_enabled: bool = Field(default=True, description="Enable CORS")
    auth_enabled: bool = Field(default=False, description="Enable authentication")     # line 29
    auth_token: Optional[str] = Field(default=None, description="Authentication token") # line 30
# praisonai/deploy/api.py (praisonai==4.6.33)
code = f\'\'\'...
# Authentication
AUTH_ENABLED = {config.auth_enabled}      # False by default
AUTH_TOKEN   = {repr(config.auth_token)}  # None by default

def check_auth():
    if not AUTH_ENABLED:
        return True                       # short-circuit, accept all
    token = request.headers.get(\'Authorization\', \'\').replace(\'Bearer \', \'\')
    return token == AUTH_TOKEN
...
\'\'\'

A default invocation of the deploy command emits a server whose check_auth() short-circuits to True and accepts unauthenticated /chat, /agents POSTs.

PoC

#!/usr/bin/env python3
"""
legend-c420 PoC - PraisonAI 4.6.33 generates Flask API server with auth
disabled by default. Class H sibling of CVE-2026-44338.

Phase 1: reflect on praisonai.deploy.models.APIConfig defaults.
Phase 2: call generate_api_server_code(default config) and assert the
         emitted source contains AUTH_ENABLED = False and the
         short-circuit return.
Phase 3: re-run with auth_enabled=True, auth_token='s3cret-bearer-value'
         and confirm the emitted source flips to the secure shape.

Exit code 0 = PASS = vulnerable defaults confirmed.
"""
import sys, traceback

def phase1_dataclass_defaults():
    print("PHASE 1 - praisonai.deploy.models.APIConfig default values")
    from praisonai.deploy.models import APIConfig
    cfg = APIConfig()
    checks = [
        ("auth_enabled", cfg.auth_enabled, False),
        ("auth_token",   cfg.auth_token,   None),
    ]
    for name, observed, expected in checks:
        ok = observed == expected
        mark = "VULNERABLE" if name in ("auth_enabled","auth_token") and ok else "ok"
        print(f"  {name:14s} = {observed!r:18s}  (expected {expected!r})  [{mark}]")
        assert ok
    print("  >> APIConfig defaults reproduce the CVE-2026-44338 shape.")

def phase2_default_generator_emits_unauth():
    print("PHASE 2 - generate_api_server_code(default config) emits unauth server")
    from praisonai.deploy.models import APIConfig
    from praisonai.deploy.api import generate_api_server_code
    src = generate_api_server_code("agents.yaml", config=APIConfig())
    for needle in ["AUTH_ENABLED = False","AUTH_TOKEN = None","if not AUTH_ENABLED:","return True"]:
        assert needle in src, f"missing: {needle!r}"
        print(f"  [FOUND] {needle!r}")
    print("  >> Default-config generator emits Flask server with check_auth() short-circuit.")

def phase3_fix_shape_available():
    print("PHASE 3 - auth_enabled=True flips to secure shape")
    from praisonai.deploy.models import APIConfig
    from praisonai.deploy.api import generate_api_server_code
    cfg = APIConfig(auth_enabled=True, auth_token="s3cret-bearer-value")
    src = generate_api_server_code("agents.yaml", config=cfg)
    assert "AUTH_ENABLED = True" in src
    assert "AUTH_ENABLED = False" not in src
    print("  >> Fix shape works when toggled. Class H confirmed: default is insecure.")

def main():
    print("=" * 64)
    print("legend-c420 PoC - PraisonAI default-config AUTH_ENABLED=False")
    print("=" * 64)
    try:
        phase1_dataclass_defaults()
        phase2_default_generator_emits_unauth()
        phase3_fix_shape_available()
    except Exception:
        traceback.print_exc()
        print("FAIL"); sys.exit(2)
    print("PASS 3/3 phases. EXIT 0.")
    sys.exit(0)

if __name__ == "__main__":
    main()

PoC dependencies: praisonai==4.6.33 from PyPI. Tested on Python 3.11.

Run log verdict: PASS 3/3 phases. EXIT 0. — vulnerable-default shape confirmed. auth_enabled=False by default, check_auth() short-circuits to True, fix toggle exists but is opt-in.

Impact

An operator who runs the vendor-documented quickstart (pip install praisonai && praisonai deploy --type api) gets a network-reachable Flask server that invokes praisonai.run() on attacker-supplied JSON with the user's LLM API keys in the process environment. The attacker reaches arbitrary LLM-orchestration (including any tool-use the agents define, which in PraisonAI commonly includes python_repl, bash, file I/O, and HTTP calls), with the host's API-key credit billed to the operator.

  • Belief: CVE-2026-44338 was filed and triaged.
  • Reality: praisonai==4.6.33 is current @latest on PyPI (2026-05-16). The generator still defaults to auth_enabled=False.
  • Gap: The CVE acknowledges the fix shape exists. The fix is opt-in. The default-config consumer remains vulnerable.

Parent CVE: CVE-2026-44338 / GHSA-6rmh-7xcm-cpxj

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.6.39"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "PraisonAI"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.6.40"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-47393"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-306"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-29T22:29:20Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "### Summary\n\nCVE-2026-44338 (GHSA-6rmh-7xcm-cpxj) documents that PraisonAI ships a code-generator (`praisonai.deploy.api.generate_api_server_code`) that emits a Flask API server with authentication disabled by default. Users who follow the documented quickstart (`praisonai deploy --type api`) get a server that:\n\n- binds to `0.0.0.0` per the recommended sample YAML\n- exposes `/chat` and `/agents` endpoints\n- runs `praisonai.run()` on user-supplied JSON input \u2014 LLM orchestration with the API key materials present in the process environment\n- does not require any authentication\n\nThe PyPI wheel `praisonai==4.6.33` (current `@latest`) still ships the generator with `auth_enabled` defaulting to `False`. The fix shape is opt-in via `APIConfig(auth_enabled=True, auth_token=...)`.\n\n### Details\n\n**Anchor (file:line:symbol)**\n\n- Vulnerable artifact: `praisonai==4.6.33` on PyPI.\n- Defaults: `praisonai/deploy/models.py:29` \u2014 `auth_enabled: bool = Field(default=False, ...)`; `praisonai/deploy/models.py:30` \u2014 `auth_token: Optional[str] = Field(default=None, ...)`.\n- Generator: `praisonai/deploy/api.py:40` \u2014 `AUTH_ENABLED = {config.auth_enabled}`; `api.py:41` \u2014 `AUTH_TOKEN = {repr(config.auth_token)}`; `api.py:43-49` \u2014 `def check_auth(): if not AUTH_ENABLED: return True`.\n- CLI entry: documented as `praisonai deploy --type api` (vendor README); produces the generator output above with no flag required to suppress the warning, because no warning is emitted.\n\n**Vulnerable code (verbatim from installed wheel)**\n\n```python\n# praisonai/deploy/models.py (praisonai==4.6.33)\nclass APIConfig(BaseModel):\n    host: str = Field(default=\"127.0.0.1\", description=\"Server host\")\n    port: int = Field(default=8005, description=\"Server port\")\n    cors_enabled: bool = Field(default=True, description=\"Enable CORS\")\n    auth_enabled: bool = Field(default=False, description=\"Enable authentication\")     # line 29\n    auth_token: Optional[str] = Field(default=None, description=\"Authentication token\") # line 30\n```\n\n```python\n# praisonai/deploy/api.py (praisonai==4.6.33)\ncode = f\\\u0027\\\u0027\\\u0027...\n# Authentication\nAUTH_ENABLED = {config.auth_enabled}      # False by default\nAUTH_TOKEN   = {repr(config.auth_token)}  # None by default\n\ndef check_auth():\n    if not AUTH_ENABLED:\n        return True                       # short-circuit, accept all\n    token = request.headers.get(\\\u0027Authorization\\\u0027, \\\u0027\\\u0027).replace(\\\u0027Bearer \\\u0027, \\\u0027\\\u0027)\n    return token == AUTH_TOKEN\n...\n\\\u0027\\\u0027\\\u0027\n```\n\nA default invocation of the deploy command emits a server whose `check_auth()` short-circuits to `True` and accepts unauthenticated `/chat`, `/agents` POSTs.\n\n### PoC\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nlegend-c420 PoC - PraisonAI 4.6.33 generates Flask API server with auth\ndisabled by default. Class H sibling of CVE-2026-44338.\n\nPhase 1: reflect on praisonai.deploy.models.APIConfig defaults.\nPhase 2: call generate_api_server_code(default config) and assert the\n         emitted source contains AUTH_ENABLED = False and the\n         short-circuit return.\nPhase 3: re-run with auth_enabled=True, auth_token=\u0027s3cret-bearer-value\u0027\n         and confirm the emitted source flips to the secure shape.\n\nExit code 0 = PASS = vulnerable defaults confirmed.\n\"\"\"\nimport sys, traceback\n\ndef phase1_dataclass_defaults():\n    print(\"PHASE 1 - praisonai.deploy.models.APIConfig default values\")\n    from praisonai.deploy.models import APIConfig\n    cfg = APIConfig()\n    checks = [\n        (\"auth_enabled\", cfg.auth_enabled, False),\n        (\"auth_token\",   cfg.auth_token,   None),\n    ]\n    for name, observed, expected in checks:\n        ok = observed == expected\n        mark = \"VULNERABLE\" if name in (\"auth_enabled\",\"auth_token\") and ok else \"ok\"\n        print(f\"  {name:14s} = {observed!r:18s}  (expected {expected!r})  [{mark}]\")\n        assert ok\n    print(\"  \u003e\u003e APIConfig defaults reproduce the CVE-2026-44338 shape.\")\n\ndef phase2_default_generator_emits_unauth():\n    print(\"PHASE 2 - generate_api_server_code(default config) emits unauth server\")\n    from praisonai.deploy.models import APIConfig\n    from praisonai.deploy.api import generate_api_server_code\n    src = generate_api_server_code(\"agents.yaml\", config=APIConfig())\n    for needle in [\"AUTH_ENABLED = False\",\"AUTH_TOKEN = None\",\"if not AUTH_ENABLED:\",\"return True\"]:\n        assert needle in src, f\"missing: {needle!r}\"\n        print(f\"  [FOUND] {needle!r}\")\n    print(\"  \u003e\u003e Default-config generator emits Flask server with check_auth() short-circuit.\")\n\ndef phase3_fix_shape_available():\n    print(\"PHASE 3 - auth_enabled=True flips to secure shape\")\n    from praisonai.deploy.models import APIConfig\n    from praisonai.deploy.api import generate_api_server_code\n    cfg = APIConfig(auth_enabled=True, auth_token=\"s3cret-bearer-value\")\n    src = generate_api_server_code(\"agents.yaml\", config=cfg)\n    assert \"AUTH_ENABLED = True\" in src\n    assert \"AUTH_ENABLED = False\" not in src\n    print(\"  \u003e\u003e Fix shape works when toggled. Class H confirmed: default is insecure.\")\n\ndef main():\n    print(\"=\" * 64)\n    print(\"legend-c420 PoC - PraisonAI default-config AUTH_ENABLED=False\")\n    print(\"=\" * 64)\n    try:\n        phase1_dataclass_defaults()\n        phase2_default_generator_emits_unauth()\n        phase3_fix_shape_available()\n    except Exception:\n        traceback.print_exc()\n        print(\"FAIL\"); sys.exit(2)\n    print(\"PASS 3/3 phases. EXIT 0.\")\n    sys.exit(0)\n\nif __name__ == \"__main__\":\n    main()\n```\n\n**PoC dependencies:** `praisonai==4.6.33` from PyPI. Tested on Python 3.11.\n\n**Run log verdict:** `PASS 3/3 phases. EXIT 0.` \u2014 vulnerable-default shape confirmed. `auth_enabled=False` by default, `check_auth()` short-circuits to `True`, fix toggle exists but is opt-in.\n\n### Impact\n\nAn operator who runs the vendor-documented quickstart (`pip install praisonai \u0026\u0026 praisonai deploy --type api`) gets a network-reachable Flask server that invokes `praisonai.run()` on attacker-supplied JSON with the user\u0027s LLM API keys in the process environment. The attacker reaches arbitrary LLM-orchestration (including any tool-use the agents define, which in PraisonAI commonly includes `python_repl`, `bash`, file I/O, and HTTP calls), with the host\u0027s API-key credit billed to the operator.\n\n- **Belief:** CVE-2026-44338 was filed and triaged.\n- **Reality:** `praisonai==4.6.33` is current `@latest` on PyPI (2026-05-16). The generator still defaults to `auth_enabled=False`.\n- **Gap:** The CVE acknowledges the fix shape exists. The fix is opt-in. The default-config consumer remains vulnerable.\n\n**Parent CVE:** CVE-2026-44338 / GHSA-6rmh-7xcm-cpxj",
  "id": "GHSA-8444-4fhq-fxpq",
  "modified": "2026-05-29T22:29:20Z",
  "published": "2026-05-29T22:29:20Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-8444-4fhq-fxpq"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-6rmh-7xcm-cpxj"
    }
  ],
  "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:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI `deploy --type api` emits a Flask server with authentication disabled by default"
}



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…