1. Executive summary
PraisonAI’s FastAPI async Jobs API contains no authentication or job-ownership control, allowing any caller who can reach /api/v1/runs to submit agent jobs, enumerate jobs, retrieve other jobs’ results, cancel running jobs and delete completed jobs. Remote exploitation requires deployment beyond the default 127.0.0.1 bind through a public bind, published container port, reverse proxy or tunnel; exposed jobs execute attacker-controlled prompts using the operator’s configured LLM credentials.
The supplied authoritative data does not provide a CVSS score or CISA KEV state for this issue, identified by GitHub as CVE-2026-55539. It must not be conflated with legacy Flask vulnerability CVE-2026-44338, which is CVSS 7.3 HIGH, NOT in CISA KEV, has EPSS 29%, and was fixed in PraisonAI 4.6.34.
For EMEA financial services operators, an exposed instance creates direct risks of unauthorised LLM expenditure, resource exhaustion, disclosure of stored agent output and disruption of legitimate workloads. The supplied material contains no evidence of exploitation in the wild, malware deployment or threat-actor attribution.
2. Regulatory framing
No specific DORA/NIS2 article is directly engaged by this item.
3. Technical analysis & attack chain
The following chain is supported by the source-code trace and local HTTP proof of concept in the primary GitHub advisory:
- The Jobs API becomes reachable. The default bind is
127.0.0.1. Remote access requires an operator to bind a public interface, publish the service from a container, place it behind a reachable reverse proxy or expose it through a tunnel. The source gives the following exposed configuration:
bash python -m uvicorn praisonai.jobs.server:create_app --host 0.0.0.0 --port 8005 --factory
- A caller reaches routes with no authentication check.
src/praisonai/praisonai/jobs/server.pycreates the FastAPI application and attaches the jobs router usingapp.include_router(jobs_router). It configures CORS but supplies no authentication middleware orDepends(...)requirement. Allowing theAuthorizationheader through CORS does not validate it.
src/praisonai/praisonai/jobs/router.py registers these routes under /api/v1/runs:
| Method and route | Capability |
|---|---|
POST /api/v1/runs |
Submit a job |
GET /api/v1/runs |
List jobs |
GET /api/v1/runs/{job_id} |
Retrieve job status |
GET /api/v1/runs/{job_id}/result |
Retrieve job output |
POST /api/v1/runs/{job_id}/cancel |
Cancel a job |
DELETE /api/v1/runs/{job_id} |
Delete a terminal job |
GET /api/v1/runs/{job_id}/stream |
Stream job activity |
The sole declared header is Idempotency-Key, which controls deduplication and is not an authorisation mechanism.
- An unauthenticated caller supplies an agent prompt.
submit_job()builds aJobfrom caller-controlled JSON. The executor saves and schedules it, then the defaultpraisonaipath invokes:
python agent = Agent(instructions="You are a helpful AI assistant.", output="minimal") result = await asyncio.to_thread(agent.start, job.prompt)
The source demonstrates an unauthenticated POST /api/v1/runs returning HTTP 202. Its dynamic test substituted a canned implementation for Agent.start(), so the routing and execution call path were demonstrated without making a real LLM request. Actual provider billing impact is supported by the code path but was not dynamically validated by that test.
- The caller enumerates and reads cross-job data. The default in-memory store contains no owner, user or principal field.
list_jobs()begins with the globalself._jobs.values()collection;statusand caller-suppliedsession_idare filters, not access controls. The proof of concept received HTTP200when listing jobs and when retrieving another job’s stored result. - The caller interferes with workloads. The proof of concept cancelled a running job with an unauthenticated
POST, receiving HTTP200, and deleted a terminal job with an unauthenticatedDELETE, receiving HTTP204. The unauthenticated stream route is confirmed by the route definition but was not explicitly exercised in the supplied proof of concept.
Defensive interpretation
| Area | Supported finding |
|---|---|
| Initial access | Direct HTTP access to a reachable Jobs API |
| Vulnerability mechanism | Complete absence of route-level authentication and per-job ownership |
| Payload | Caller-controlled prompt and job parameters, including timeout; the source states a default of 3,600 seconds |
| Execution context | The application’s existing agent runtime and configured LLM credentials |
| Persistence | No host persistence mechanism described; the default job store is in memory |
| Privilege escalation | None demonstrated; abuse occurs with the application’s existing permissions |
| Command and control | No C2 infrastructure or malware identified |
| Lateral movement | None described |
| Data access | Global job listing and retrieval of stored cross-job output |
| Impact | Potential provider charges, CPU/memory and queue consumption, output disclosure, cancellation and deletion |
As of commit 9fcac3a, identified as version 4.6.51 by the source, this FastAPI Jobs API remained unpatched. PraisonAI 4.6.34 fixed only the legacy Flask file src/praisonai/api_server.py for CVE-2026-44338 by adding AUTH_ENABLED, AUTH_TOKEN and check_auth().
The authoritative metrics for that distinct sibling are CVSS 7.3 HIGH, NOT in CISA KEV, EPSS 29%, CWE-306 (Missing Authentication for Critical Function) and CWE-668 (Exposure of Resource to Wrong Sphere). Those metrics and classifications apply to CVE-2026-44338, not CVE-2026-55539; no verified CVSS, CWE or KEV state was supplied for the present Jobs API issue.
Related advisory records describe separate PraisonAI exposure paths: an ignored --api-key on direct /agents invocation routes and unrestricted MCP file-read handlers. These are not confirmed stages of this attack chain and do not independently corroborate exploitation of /api/v1/runs.
No threat actor is named, and no attribution or in-the-wild exploitation is confirmed. The core evidence is single-sourced to the GitHub advisory and its submitted proof of concept.
4. Mitigation & containment
P1 — within 24 hours
- Inventory every deployment running
praisonai.jobs.server:create_app. Record the listening address, actual port, container publication, reverse-proxy route and tunnel exposure. - Test reachability from each untrusted network zone using an approved account and host:
bash curl -sS http://TARGET:8005/api/v1/runs
An unauthenticated HTTP 200 response indicates exposure. Do not exercise submit, cancel or delete operations against production data.
- Disable the Jobs API where it is not required. Otherwise remove public port publication and bind it to loopback:
bash python -m uvicorn praisonai.jobs.server:create_app --host 127.0.0.1 --port 8005 --factory
Restrict the actual service port to explicitly authorised systems. Port 8005 is the source’s example and may differ locally.
- Do not treat an upgrade to 4.6.34 or 4.6.51 as remediation for this issue. Version 4.6.34 fixed only CVE-2026-44338’s legacy Flask path; the source reports the Jobs API as vulnerable at 4.6.51.
- If the service was reachable, preserve reverse-proxy, application and volatile job-state evidence before restarting it. Review successful requests to the affected routes and correlate them with LLM-provider usage and billing. Rotate or revoke provider credentials if evidence shows unauthorised use; credential rotation alone does not close the endpoint.
P2 — within 72 hours
- Until an official fix is available, enforce authentication before every
/api/v1/runsroute. Prefer a router-level dependency so future child routes inherit the control. The source proposesPRAISONAI_JOBS_API_TOKEN, bearer orX-API-Keyextraction and constant-time comparison:
```python import hmac, os from fastapi import Depends, Header, HTTPException
def verify_jobs_token( authorization: str | None = Header(None), x_api_key: str | None = Header(None, alias="X-API-Key"), ): expected = os.getenv("PRAISONAI_JOBS_API_TOKEN") if not expected: raise HTTPException(401, "Jobs API auth is not configured")
token = x_api_key
if authorization and authorization.startswith("Bearer "):
token = authorization[7:]
if not token or not hmac.compare_digest(token, expected):
raise HTTPException(401, "Unauthorized")
```
Apply it to the router:
python APIRouter( prefix="/api/v1/runs", dependencies=[Depends(verify_jobs_token)], )
This is a proposed compensating change, not a vendor-released patch; test it before production deployment.
- Introduce owner or principal binding for every job. Authentication alone does not prevent one authenticated caller from listing, reading, cancelling or deleting another caller’s jobs.
- Add regression tests confirming unauthenticated submit, list, status, result, stream, cancel and delete requests return HTTP
401. Test incorrect bearer tokens, incorrectX-API-Keyvalues and empty bearer values.
P3 — within seven days
- Monitor the primary advisory for a confirmed fixed release. Before deployment, verify that the change modifies
praisonai/jobs/, not onlysrc/praisonai/api_server.py. - Conduct a retrospective review of
/api/v1/runsaccess, source addresses, response codes, job identifiers, cancellation/deletion events and provider consumption. The supplied sources contain no known-malicious addresses or hashes against which to pivot. - Review the separate
praisonai serve agents --api-keyauthentication-bypass and MCP file-read advisories as distinct exposure-management workstreams.
5. Indicators of compromise
No indicators of compromise available in the source material.
The following behaviours are exploitation-compatible but do not independently establish malicious activity. The evidence is single-sourced; verify before enforcement.
Behavioural indicators
| Behaviour | Where to observe | Confidence |
|---|---|---|
POST /api/v1/runs succeeds with HTTP 202 without an Authorization header, cookie or token |
Reverse-proxy/API access logs containing headers and status codes; FastAPI access logs | High for vulnerability validation; low specificity for compromise |
GET /api/v1/runs or GET /api/v1/runs/{job_id}/result returns HTTP 200 without credentials |
Reverse-proxy and application logs | High for vulnerability validation; low specificity for compromise |
POST /api/v1/runs/{job_id}/cancel returns HTTP 200, or DELETE /api/v1/runs/{job_id} returns HTTP 204, without credentials |
Reverse-proxy and application logs; job-state records | High for the supplied proof of concept; caller intent requires investigation |
| Unexpected LLM-provider activity corresponding to externally submitted jobs | PraisonAI executor telemetry and LLM-provider usage or billing records | Medium; the source traced this path but used a stubbed agent during dynamic testing |
6. Detection
The YARA rule below is suitable only for raw, decrypted HTTP request data or exported request content. It cannot establish that authentication was absent and may match legitimate clients or reporting that contains the same request strings.
rule PraisonAI_Jobs_API_Sensitive_HTTP_Operations
{
meta:
author = "Adverse Trace"
date = "2026-08-25"
reference = "https://github.com/advisories/GHSA-2jgc-f764-c5r2"
description = "Detects result, cancel, or delete request text under /api/v1/runs"
strings:
$get_base = "GET /api/v1/runs/" ascii
$post_base = "POST /api/v1/runs/" ascii
$delete_base = "DELETE /api/v1/runs/" ascii
$result = "/result" ascii
$cancel = "/cancel" ascii
condition:
($get_base and $result) or
($post_base and $cancel) or
$delete_base
}
The following generic Sigma rule requires local field mapping. It intentionally detects successful sensitive operations regardless of whether an Authorization header was supplied because the vulnerable implementation ignores all credentials, including incorrect or empty values.
title: Successful PraisonAI Jobs API Operations
status: experimental
description: Detects successful submit, list, result, cancel, and delete operations against /api/v1/runs
author: Adverse Trace
date: 2026-08-25
references:
- https://github.com/advisories/GHSA-2jgc-f764-c5r2
logsource:
category: webserver
detection:
selection_submit:
http_method: POST
url_path: /api/v1/runs
status: 202
selection_list:
http_method: GET
url_path: /api/v1/runs
status: 200
selection_result:
http_method: GET
url_path|startswith: /api/v1/runs/
url_path|endswith: /result
status: 200
selection_cancel:
http_method: POST
url_path|startswith: /api/v1/runs/
url_path|endswith: /cancel
status: 200
selection_delete:
http_method: DELETE
url_path|startswith: /api/v1/runs/
status: 204
condition: 1 of selection_*
falsepositives:
- Legitimate internal Jobs API clients
Scope this detection to PraisonAI service addresses and investigate the caller, exposure path, associated job identifier and provider activity before enforcement.
CVE assessment
1 referenced CVE
| CVE | CVSS | Exploited | EPSS | Summary |
|---|---|---|---|---|
| CVE-2026-44338 | 7.3 High | — | 29% | PraisonAI is a multi-agent teams system. From version 2.5.6 to before version 4.6.34, PraisonAI ships a legacy Flask API server… |
7. Sources
- GitHub Security Advisories, “PraisonAI: [Auth Bypass] PraisonAI async Jobs API (
/api/v1/runs) has no authentication — unauthenticated job execution, result theft, cancel and delete,” https://github.com/advisories/GHSA-2jgc-f764-c5r2, 2026-08-25. - GitHub Security Advisories, “PraisonAI serve agents
--api-keyis ignored, allowing unauthenticated remote agent execution,” https://github.com/advisories/GHSA-7ww9-85pg-cv4x, date not provided in supplied material. - GitHub Security Advisories, “PraisonAI: [Auth Bypass]
praisonai serve agents --api-keyis silently ignored,” https://github.com/advisories/GHSA-r7v3-x45f-g7hp, date not provided in supplied material. - GitHub Security Advisories, “PraisonAI vulnerable to unauthenticated arbitrary file read via MCP
workflow.show,workflow.validate,deploy.validate,” https://github.com/advisories/GHSA-9cr9-25q5-8prj, date not provided in supplied material.
8. Adverse Trace position
Adverse Trace does not assign a severity or CISA KEV state to CVE-2026-55539 because neither is present in the supplied authoritative data. Client impact is exposure-dependent: a loopback-only instance is not remotely reachable, while an instance published beyond the host permits pre-authentication job execution, cross-job result access and workload interference. CVE-2026-44338 is a distinct legacy sibling rated CVSS 7.3 HIGH, NOT in CISA KEV, with EPSS 29% and classifications CWE-306 and CWE-668; its 4.6.34 remediation does not protect the Jobs API. No live exploitation or actor attribution is confirmed, and the primary evidence is single-sourced; verify before enforcement. Adverse Trace will monitor the advisory for a fixed version, verified scoring and evidence of exploitation.
Published via PulseTrace — Adverse Trace threat intelligence.