CVE-2026-57120: PraisonAI Execute_code Sandbox Bypass: Str.format C-level Vulnerability
MODERATECVE-2026-57120 is a MODERATE (CVSS 6.5) execute_code sandbox bypass: str.format c-level vulnerability (CWE-693: Protection Mechanism Failure) affecting PraisonAI libraries in the PyPI ecosystem. The issue involves execute_code sandbox bypass: str.format c-level attribute access reads every blocklisted dunder. Remediation is available by upgrading affected packages to version 1.6.59.
CVE-2026-57120 at a Glance
Security Impact
CVSS: 6.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N).
How to Fix CVE-2026-57120
To remediate this vulnerability, upgrade all affected PraisonAI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install praisonaiagents>=1.6.59
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show praisonaiagents
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-57120, inspect your installed packages using pip:
pip show praisonaiagents
Analyze the terminal output to locate any version of PraisonAI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.6.59), your application is affected.
Note: The pip show praisonaiagents command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-693: Protection Mechanism Failure, where the product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks against the product. In PraisonAI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-57120 Affected PraisonAI Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
praisonaiagents
|
PyPI |
< 1.6.59
|
1.6.59
|
References
Frequently Asked Questions
What is CVE-2026-57120?
Which PraisonAI versions are affected?
What version fixes CVE-2026-57120?
Is praisonaiagents affected?
What is the CVSS score of CVE-2026-57120?
What CWE is associated with CVE-2026-57120?
How can I check PyPI dependencies for CVE-2026-57120?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-pv2j-rghr-v5r9",
"modified" : "2026-07-20T21:24:25Z",
"published" : "2026-06-18T13:55:29Z",
"aliases" : [ "CVE-2026-57120" ],
"summary" : "PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder",
"details" : "## Summary\n\nThe `execute_code` tool's subprocess sandbox advertises a three-layer defense (AST validation, text-pattern blocklist, restricted `__builtins__`). In **sandbox mode** (the default) only two layers are active — the text-pattern blocklist is skipped — and both remaining layers are bypassed by combining two CPython semantics:\n\n1. **Runtime string assembly.** The AST validator (`src/praisonai-agents/praisonaiagents/tools/python_tools.py:75`) enumerates blocked dunder names against `ast.Attribute.attr`, `ast.Call.func.id`, and `ast.Constant` string-substring. Names assembled at runtime (e.g. `\"_\"*2 + \"class\" + \"_\"*2`) appear in the AST as multiple short `ast.Constant` nodes, none containing a blocked substring, so the static check passes.\n2. **C-level attribute access via format-spec.** `str.format` / `str.format_map` resolve dotted field references through CPython's internal `PyObject_GetAttr` (`do_string_format` → `get_field`). This C path never consults the Python-level `getattr` binding. The sandbox's `_safe_getattr` wrapper (`python_tools.py:221`) is installed only as the `getattr` name in `safe_builtins`, so any C-level attribute access — including format-spec field resolution — sidesteps it. `format`/`format_map` are also absent from `_SANDBOX_BLOCKED_CALLS` (`python_tools.py:56`).\n\nCombined, this yields an arbitrary read primitive over every blocklisted attribute (`__class__`, `__qualname__`, `__bases__`, `__base__`, function `__globals__`, `__dict__`, …).\n\n## Affected code\n\n| File | Lines | Symbol | Role |\n|---|---|---|---|\n| `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 39–54 | `_SANDBOX_BLOCKED_ATTRS` | The blocklist the bypass renders unreachable |\n| `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 56–60 | `_SANDBOX_BLOCKED_CALLS` | Missing entries: `format`, `format_map` |\n| `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 75–102 | `_validate_code_ast` | Static check, blind to runtime string assembly |\n| `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 221–226 | `_safe_getattr` | Wraps Python-level `getattr` only; C-level access bypasses |\n| `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 352 | `execute_code` | Entry point; gated by `@require_approval(risk_level=\"critical\")` |\n\n## Reproducer\n\n```python\nimport os\nos.environ[\"PRAISONAI_AUTO_APPROVE\"] = \"true\"\nfrom praisonaiagents.tools.python_tools import execute_code\n\npayload = '''\nund = \"_\" * 2 # \"__\" assembled at runtime\nkey1 = und + \"class\" + und # \"__class__\"\nkey2 = und + \"qualname\" + und # \"__qualname__\"\nfmt_class = \"{0.\" + key1 + \"}\"\nfmt_qual2 = \"{0.\" + key1 + \".\" + key2 + \"}\"\nprint(\"LEAK_CLASS=\" + fmt_class.format(()))\nprint(\"LEAK_QUAL2=\" + fmt_qual2.format(()))\n'''\nprint(execute_code(payload, sandbox_mode=\"sandbox\", timeout=15))\n```\n\nObserved: `stdout` = `LEAK_CLASS=<class 'tuple'>` / `LEAK_QUAL2=tuple`, `success: true`, no security error. Both `__class__` (one hop) and `__class__.__qualname__` (two hops) — all blocklisted — are read.\n\n## Trust boundary\n\nThe `@require_approval(risk_level=\"critical\")` gate is bypassed when `PRAISONAI_AUTO_APPROVE` is set (verified: `require_approval` short-circuits on `is_env_auto_approve()`). That variable is set by the project's FULL_AUTO autonomy mode, the bots-CLI launchers, and the project's own issue-triage CI workflow — postures where the agent reaches `execute_code` with no human approval. The payload then arrives via any LLM-visible surface (user message, retrieved document, tool/web/MCP output) and the tool-call machinery passes it as the `code` argument.\n\n## Relationship to GHSA-4mr5-g6f9-cfrh\n\nThe code's own comment at `python_tools.py:46` cites GHSA-4mr5-g6f9-cfrh, which added `__self__` to the blocklist to stop C-builtins leaking `builtins` via `func.__self__`. This finding does not bypass that single entry — it bypasses the **entire** blocklist, because format-spec attribute resolution never consults the blocklist or `_safe_getattr`. `\"{0.__self__}\".format(print)` would leak `__self__` regardless of the blocklist. Same defense surface, different mechanism; the GHSA-4mr5 fix does not mitigate this.\n\n## Scope (read primitive only)\n\nThis reports the **read primitive**. Turning the read into in-process execution requires a callable bridge; the obvious one (`string.Formatter().get_field()` returning the live object) is not directly reachable because `import string` is blocked at the AST layer (no `ast.Import`). Other bridges may exist; a full execution chain is **not** claimed here. If one is found, severity rises to ~8.8 (the subprocess has no seccomp/`setrlimit`/syscall filtering).\n\n## Suggested fix\n\n1. Add `format`, `format_map` to `_SANDBOX_BLOCKED_CALLS` (blocks the calls at the AST layer; cost: also blocks benign `str.format`).\n2. Or replace `str` in `safe_builtins` with a subclass whose `format`/`format_map` reject dotted fields resolving to leading-underscore attributes (preserves benign formatting).\n3. Or drop sandbox-mode's in-process security claim and document that real isolation requires external sandboxing (gVisor/firejail/container/microVM) — which matches what the subprocess provides today.\n\nThe text-pattern blocklist present in the `direct` path (`python_tools.py:487-502`) is absent from the sandbox path; even if added, the runtime-assembly trick defeats it, so (1) or (2) is required.\n\nReporter: Kai Aizen / SnailSploit — kai@snailsploit.com — PGP on request. Coordinated disclosure; no public posting.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "praisonaiagents"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.6.59"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-pv2j-rghr-v5r9"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/MervinPraison/PraisonAI"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-693" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-06-18T13:55:29Z",
"nvd_published_at" : null
}
}