Every CVE Search/ Get API key

CVE-2026-57120

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

6.5 MediumFix available

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.56%Up 0.2 points since Sep 15, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.5

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

Record
CVE-2026-57120
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-693
Updated
2026-11-02
EUVDEUVD-2026-77541 6.5

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

Record
EUVD-2026-77541
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-pv2j-rghr-v5r9 6.5

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

Summary

The 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:

  1. 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.
  2. 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).

Combined, this yields an arbitrary read primitive over every blocklisted attribute (__class__, __qualname__, __bases__, __base__, function __globals__, __dict__, …).

Affected code

| File | Lines | Symbol | Role | |---|---|---|---| | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 39–54 | _SANDBOX_BLOCKED_ATTRS | The blocklist the bypass renders unreachable | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 56–60 | _SANDBOX_BLOCKED_CALLS | Missing entries: format, format_map | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 75–102 | _validate_code_ast | Static check, blind to runtime string assembly | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 221–226 | _safe_getattr | Wraps Python-level getattr only; C-level access bypasses | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 352 | execute_code | Entry point; gated by @require_approval(risk_level="critical") |

Reproducer

import os
os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
from praisonaiagents.tools.python_tools import execute_code

payload = '''
und = "_" * 2                         # "__" assembled at runtime
key1 = und + "class" + und            # "__class__"
key2 = und + "qualname" + und         # "__qualname__"
fmt_class = "{0." + key1 + "}"
fmt_qual2 = "{0." + key1 + "." + key2 + "}"
print("LEAK_CLASS=" + fmt_class.format(()))
print("LEAK_QUAL2=" + fmt_qual2.format(()))
'''
print(execute_code(payload, sandbox_mode="sandbox", timeout=15))

Observed: 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.

Trust boundary

The @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.

Relationship to GHSA-4mr5-g6f9-cfrh

The 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…

Record
GHSA-pv2j-rghr-v5r9
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-693
Updated
2026-11-02
NVD 6.5

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

Record
CVE-2026-57120
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-693
Updated
2026-11-02
OSV 6.5

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

Record
CVE-2026-57120
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-693
Updated
2026-11-02
OSVGHSA-pv2j-rghr-v5r9 6.5

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

Summary

The 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:

  1. 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.
  2. 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).

Combined, this yields an arbitrary read primitive over every blocklisted attribute (__class__, __qualname__, __bases__, __base__, function __globals__, __dict__, …).

Affected code

| File | Lines | Symbol | Role | |---|---|---|---| | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 39–54 | _SANDBOX_BLOCKED_ATTRS | The blocklist the bypass renders unreachable | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 56–60 | _SANDBOX_BLOCKED_CALLS | Missing entries: format, format_map | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 75–102 | _validate_code_ast | Static check, blind to runtime string assembly | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 221–226 | _safe_getattr | Wraps Python-level getattr only; C-level access bypasses | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 352 | execute_code | Entry point; gated by @require_approval(risk_level="critical") |

Reproducer

import os
os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
from praisonaiagents.tools.python_tools import execute_code

payload = '''
und = "_" * 2                         # "__" assembled at runtime
key1 = und + "class" + und            # "__class__"
key2 = und + "qualname" + und         # "__qualname__"
fmt_class = "{0." + key1 + "}"
fmt_qual2 = "{0." + key1 + "." + key2 + "}"
print("LEAK_CLASS=" + fmt_class.format(()))
print("LEAK_QUAL2=" + fmt_qual2.format(()))
'''
print(execute_code(payload, sandbox_mode="sandbox", timeout=15))

Observed: 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.

Trust boundary

The @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.

Relationship to GHSA-4mr5-g6f9-cfrh

The 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…

Record
GHSA-pv2j-rghr-v5r9
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Weakness
CWE-693
Updated
2026-11-02
OSVPYSEC-2026-3533 6.5

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

Summary

The 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:

  1. 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.
  2. 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).

Combined, this yields an arbitrary read primitive over every blocklisted attribute (__class__, __qualname__, __bases__, __base__, function __globals__, __dict__, …).

Affected code

| File | Lines | Symbol | Role | |---|---|---|---| | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 39–54 | _SANDBOX_BLOCKED_ATTRS | The blocklist the bypass renders unreachable | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 56–60 | _SANDBOX_BLOCKED_CALLS | Missing entries: format, format_map | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 75–102 | _validate_code_ast | Static check, blind to runtime string assembly | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 221–226 | _safe_getattr | Wraps Python-level getattr only; C-level access bypasses | | src/praisonai-agents/praisonaiagents/tools/python_tools.py | 352 | execute_code | Entry point; gated by @require_approval(risk_level="critical") |

Reproducer

import os
os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
from praisonaiagents.tools.python_tools import execute_code

payload = '''
und = "_" * 2                         # "__" assembled at runtime
key1 = und + "class" + und            # "__class__"
key2 = und + "qualname" + und         # "__qualname__"
fmt_class = "{0." + key1 + "}"
fmt_qual2 = "{0." + key1 + "." + key2 + "}"
print("LEAK_CLASS=" + fmt_class.format(()))
print("LEAK_QUAL2=" + fmt_qual2.format(()))
'''
print(execute_code(payload, sandbox_mode="sandbox", timeout=15))

Observed: 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.

Trust boundary

The @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.

Relationship to GHSA-4mr5-g6f9-cfrh

The 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…

Record
PYSEC-2026-3533
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonaiagents
PyPI
before 1.6.59
1.6.59
praisonaiagents
PyPI
before 1.6.59, plus listed versions
0.0.10.0.100.0.1000.0.1010.0.102
Show 45 moreShow less
0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143
1.6.59

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-57120 is a medium vulnerability (CVSS 6.5) in Mervinpraison Praisonaiagents, published June 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.56%, the 46th percentile. Fixed in praisonaiagents 1.6.59.

References

This page doesn't list everything

Left out here: 536 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open