PT-2026-45052 · Pypi · Praisonai

Published

2026-05-29

·

Updated

2026-05-29

·

CVE-2026-47393

CVSS v3.1

9.8

Critical

VectorAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

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

Fix

Missing Authentication

Weakness Enumeration

Related Identifiers

CVE-2026-47393
GHSA-8444-4FHQ-FXPQ

Affected Products

Praisonai