CVE-2026-55538: PraisonAI: [Auth Bypass] `praisonai serve agents --api-key` is silently ignored — agent-invocation routes (`POST /agents`, `POST /agents/{agent_name}`) run unauthenticated

Published Aug 25, 2026
·
Updated

Summary praisonai serve agents exposes HTTP routes that invoke registered agents. The CLI advertises --api-key with help text "API key for authentication", parses it, and forwards it into ServeHandler. But createagentsapp() never reads config["apikey"] again and installs no auth dependency or middleware on its direct routes. The configured key is a no-op flag. As a result, an unauthenticated network caller can invoke exposed agents (POST /agents and POST /agents/{agentname}) even when the operator passed --api-key. Requests with no credentials, a wrong Authorization: Bearer, a wrong X-API-Key, or an empty bearer all reach agent.start(). The failure is made sharper by the fact that a working auth dependency already exists in the same module — praisonai.api.agentinvoke.verifytoken guards every /api/v1/... route with Depends(verifytoken) and is mounted into the very same app. The direct n8n-compat routes simply do not use it.

Technical Detail Source-to-sink trace 1. CLI advertises and forwards --api-key: python cli/commands/serve.py @app.command("agents") def serveagents(..., apikey: Optional[str] = typer.Option(None, "--api-key", help="API key for authentication")): ... if apikey: args.extend(["--api-key", apikey]) exitcode = handleservecommand(args) 2. cmdagents() parses apikey into the spec — and that is the last time it is touched: python cli/features/serve.py — cmdagents() spec = { ..., "apikey": {"default": None} } parsed = self.parseargs(args, spec) app = self.createagentsapp(parsed) A grep of the entire cli/features/serve.py for apikey returns only the two spec entries (cmdagents line ~199 and cmdunified line ~847). config["apikey"] is never read inside createagentsapp() / createunifiedapp(); it is never compared, and no dependency is attached. 3. createagentsapp() imports FastAPI, HTTPException, Request — no Depends, no Header, no auth middleware. Every HTTPException raised in the agents routes is 400/404/500 (validation / not-found / execution error); none is 401. 4. Sink — unauthenticated request reaches agent.start(): python cli/features/serve.py @app.post("/agents/{agentname}") # n8n compatibility route async def invokesingleagent(agentname: str, request: Request): body = await request.json() query = body.get("query", "") or body.get("message", "") ... agent = agentinvoke.getagent(agentname) result = await loop.runinexecutor(None, agent.start, query) # no auth anywhere above return {"response": str(result)} @app.post(path) # default path "/agents" async def invokeagents(request: Request, querydata: AgentQuery = None): ... agent.start(query) ...

The auth dependency exists — it just isn't applied here createagentsapp() mounts the agentinvoke router into the same app: python cli/features/serve.py if getattr(agentinvoke, 'FASTAPIAVAILABLE', False) and hasattr(agentinvoke, 'router'): app.includerouter(agentinvoke.router) That router properly authenticates every sensitive route: python api/agentinvoke.py CALLSERVERTOKEN = os.getenv('CALLSERVERTOKEN') async def verifytoken(request, authorization=Header(None)) -> None: ... if token != CALLSERVERTOKEN: raise HTTPException(statuscode=401, detail="Unauthorized") @router.get("/api/v1/agents") async def listagents(: None = Depends(verifytoken)): ... # and register/unregister/info all use it So in the same process GET /api/v1/agents returns 401 without a token, while POST /agents/{agentname} returns 200. Note also that verifytoken reads the CALLSERVERTOKEN env var — not the CLI --api-key — so the CLI option feeds no auth path at all. Trigger conditions praisonai serve agents --file agents.yaml --host 0.0.0.0 --port 8765 --api-key expected-secret POST /agents/{agentname} body {"query":"..."} with no / wrong / empty credentials Proof of Concept Built the real createagentsapp() and exercised it over HTTP via FastAPI TestClient. Only praisonaiagents.Agent is stubbed (.start() returns EXEC:<query>), so no real LLM/credentials. CALLSERVERTOKEN=expected-secret was set so the sibling /api/v1 router is genuinely armed — making the contrast explicit. Operator started with: --api-key expected-secret (CALLSERVERTOKEN also set) == Sibling /api/v1 route WITH Depends(verifytoken) == GET /api/v1/agents [no creds ] -> HTTP 401 GET /api/v1/agents [wrong bearer] -> HTTP 401 == Direct agent-invocation route (the bug) == POST /agents/owned [no creds ] -> HTTP 200 {'response': 'EXEC:hello'} POST /agents/owned [wrong bearer ] -> HTTP 200 {'response': 'EXEC:hello'} POST /agents/owned [wrong x-api-key] -> HTTP 200 {'response': 'EXEC:hello'} POST /agents/owned [empty bearer ] -> HTTP 200 {'response': 'EXEC:hello'} POST /agents [no creds ] -> HTTP 200 {'response': 'EXEC:hi'} The auth mechanism works for /api/v1 (401) and is entirely absent on the direct /agents routes (200), despite --api-key being configured. Equivalent HTTP trigger in a fully installed environment bash praisonai serve agents --file agents.yaml --host 0.0.0.0 --port 8765 --api-key expected-secret curl -sS -X POST http://TARGET:8765/agents/owned \ -H 'Content-Type: application/json' --data-binary '{"query":"hello"}' -> 200 {"response":"..."} (expected: 401 Unauthorized)

Impact - Direct primitive: unauthenticated agent invocation despite a configured API key. - Misleading control (aggravating): because the CLI advertises --api-key as authentication, operators may deliberately expose the service (e.g. --host 0.0.0.0, reverse proxy, n8n integration) believing it is protected, increasing the real-world likelihood of exposure. - Downstream: exposed agents commonly hold LLM provider credentials, RAG/memory, browser/search, MCP, or shell/file tools; the bypass lets an attacker drive those capabilities. Baseline impact is unauthorized LLM cost + access to agent responses.

Suggested Mitigation - When config["apikey"] is set, build a shared auth dependency and attach it to every agent-invocation / state-changing route in createagentsapp() and createunifiedapp() (dependencies=[Depends(verify)]). - Reuse / unify with the existing verifytoken so the direct /agents routes and the /api/v1 routes share one mechanism, and wire the CLI --api-key into that mechanism (today it feeds nothing; verifytoken reads CALLSERVERTOKEN). - Use constant-time comparison (hmac.comparedigest); verifytoken currently uses !=. - Update discovery metadata from authmodes=["none"] to ["api-key","bearer"] for protected endpoints. - Regression tests next to tests/unit/testserveunified.py: createagentsapp({"apikey":"secret",...}) → POST /agents/{name} with no creds / wrong Authorization / wrong X-API-Key returns 401; correct key succeeds. python import hmac from fastapi import Header, HTTPException, Depends def authdependency(expectedkey: str): async def verify(authorization: str | None = Header(None), xapikey: str | None = Header(None, alias="X-API-Key")): token = xapikey if authorization and authorization.startswith("Bearer "): token = authorization[7:] if not token or not hmac.comparedigest(token, expectedkey): raise HTTPException(statuscode=401, detail="Unauthorized") return Depends(verify)

Other sources

PraisonAI is a multi-agent teams system. Prior to praisonai 4.6.51, praisonai serve agents parses config["apikey"] but createagentsapp() does not authenticate POST /agents or POST /agents/{agentname}. Missing or incorrect bearer and X-API-Key values still reach agent execution. This issue is fixed in version 4.6.58.

MITRE

Affected Software

1 affected componentFixes available
pip/PraisonAI<4.6.58
4.6.58

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/PraisonAI to a version that resolves this vulnerability.

    Fixed in 4.6.58
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 4.6.58
  3. Configuration

    In verify_token, change the token equality check from an inequality operator (currently uses !=) to constant-time comparison using hmac.compare_digest, per the described issue.

    praisonai.api.agent_invoke.verify_token token comparison method = hmac.compare_digest
  4. Configuration

    Update discovery metadata from auth_modes=['none'] to auth_modes=['api-key','bearer'] for the protected endpoints so they are treated as authenticated.

    praisonai serve agents route auth metadata/discovery auth_modes = ['api-key','bearer']
  5. Configuration

    When config['api_key'] is set, build a shared auth dependency and attach it (dependencies=[Depends(verify)]) to every agent-invocation / state-changing route in _create_agents_app() and _create_unified_app() so POST /agents and POST /agents/{agent_name} require authentication.

    praisonai serve agents app wiring (_create_agents_app and _create_unified_app) FastAPI dependencies on /agents routes = dependencies=[Depends(verify)]
  6. Configuration

    Ensure the CLI --api-key passed into ServeHandler is actually used by the authentication dependency (verify/verify_token) for the direct /agents routes; verify_token currently reads CALL_SERVER_TOKEN env var instead of the CLI option.

    praisonai CLI ServeHandler / serve_agents --api-key wiring source for verification = use CLI api_key (config['api_key']) instead of CALL_SERVER_TOKEN

Event History

Aug 25, 2026
CVE Published
via MITRE·02:56 PM
Data Sourced
via MITRE·02:56 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·02:56 PM
Data Sourced
via GitHub·02:56 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are exposed to unauthenticated agent invocation?

Deployments running `praisonai serve agents` are exposed when their direct agent-invocation routes are reachable by an attacker. Passing `--api-key` does not protect `POST /agents` or `POST /agents/{agent_name}`.

2

Does an attacker need credentials or user interaction to invoke an exposed agent?

No. A network caller can invoke exposed agents without credentials or user interaction; requests with no credentials, incorrect bearer or API-key values, and an empty bearer all reach `agent.start()`.

3

Are the API v1 routes affected in the same way?

The provided information identifies the missing authentication on the direct n8n-compatible routes only. It states that `/api/v1/...` routes use the existing `verify_token` dependency.

4

What version should be used to remediate this issue?

The provided references include the v4.6.58 release and a remediation commit. Verify the changes from that release or commit are present before relying on `--api-key` to protect direct agent routes.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203