GHSA-26mh-57q7-jfvr: PraisonAI vulnerable to Remote Code Execution via Broken AST Sandbox

Published Oct 29, 2026
·
Updated

Summary JobWorkflowExecutor.execinlinepython() is designed to safely execute inline Python expressions from YAML workflow script: steps using AST validation. The function claims to block dangerous operations by (a) checking all AST nodes against an allowlist, and (b) providing a builtins whitelist that omits import, exec, eval, and open.

The sandbox is broken. The module import allowlist (allowedmodules) explicitly includes 'os', and the AST allowlist permits ast.Import, ast.ImportFrom, ast.Call, and ast.Attribute nodes. This means the following malicious workflow script: step passes all AST validation checks and executes without error.

Attack Scenario

Attacker creates or contributes a YAML workflow file (type: job) with a script: step containing import os; os.system(...). A user or CI pipeline with PRAISONAIALLOWJOBWORKFLOWS=true executes the workflow via praisonai workflow run malicious.yaml. Arbitrary OS commands execute with the privileges of the calling process.

This is particularly dangerous in multi-tenant or CI environments where YAML workflows may originate from untrusted sources (e.g., downloaded templates, shared repositories, AI-generated workflows). PoC Complete instructions, including specific configuration details, to reproduce the vulnerability.

Impact

Root Cause — Lines 364–372:

pythonallowedmodules = {'os', 'datetime', 'time', 'json', 'math', 're', 'collections', 'itertools'} if isinstance(node, ast.Import): for alias in node.names: if alias.name.split('.')[0] not in allowedmodules: return {"ok": False, "error": f"Import of '{alias.name}' not allowed..."}

os is trusted because it is commonly used for path operations (os.path, os.getcwd). However, os.system(), os.popen(), os.execvp(), os.spawn(), and os.exit() are all accessible once the module is imported, none of which are individually blocked.

The builtins restriction (blocking import) is irrelevant because the code uses a real import statement processed by the Python bytecode compiler — not a import() call.

Proof-of-Concept validation (static — no remote network call made):

pythonimport ast

ALLOWEDNODES = ( ast.Expression, ast.BoolOp, ast.BinOp, ast.UnaryOp, ast.Compare, ast.IfExp, ast.Constant, ast.Name, ast.Load, ast.Store, ast.Subscript, ast.List, ast.Tuple, ast.Dict, ast.Set, ast.And, ast.Or, ast.Not, ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.In, ast.NotIn, ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod, ast.FloorDiv, ast.USub, ast.UAdd, ast.Call, ast.Attribute, ast.keyword, ast.Starred, ast.Module, ast.Assign, ast.Import, ast.ImportFrom, ast.alias, ) allowedmodules = {'os', 'datetime', 'time', 'json', 'math', 're', 'collections', 'itertools'}

malicious = "import os\nresult = os.system('id')" tree = ast.parse(malicious, mode="exec")

allpass = True for node in ast.walk(tree): if not isinstance(node, ALLOWEDNODES): print(f"BLOCKED: {type(node).name}") allpass = False if isinstance(node, ast.Import): for alias in node.names: if alias.name.split('.')[0] not in allowedmodules: print(f"IMPORT BLOCKED: {alias.name}") allpass = False

print("SANDBOX BYPASSED" if allpass else "sandbox holds") Output: SANDBOX BYPASSED

Recommended Fix

Remove 'os' from allowedmodules and replace it with only the safe submodule: 'os.path'. Then validate that the imported name is specifically os.path, not os:

python# BEFORE (vulnerable) allowedmodules = {'os', 'datetime', 'time', 'json', 'math', 're', 'collections', 'itertools'}

AFTER (safe) allowedmodules = {'datetime', 'time', 'json', 'math', 're', 'collections', 'itertools', 'pathlib'}

If os.path is needed, handle it explicitly: if isinstance(node, ast.ImportFrom): if node.module == 'os.path': pass # allow 'from os.path import join, exists' only elif node.module and node.module.split('.')[0] not in allowedmodules: return {"ok": False, "error": f"Import of '{node.module}' not allowed."}

Additionally, block ast.Import of os entirely, and consider also removing re (which can be used for ReDoS/CPU exhaustion) and collections (which can be used to reach subclasses-style gadgets).

Affected Software

1 affected componentFixes available
pip/praisonai<4.6.78
4.6.78

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.78
  2. Configuration

    Remove 'os' from allowed_modules, permit only the safe submodule 'os.path', block ast.Import of os, and validate that imported names are specifically os.path rather than os. Optionally remove 're' and 'collections' from allowed_modules to reduce ReDoS/CPU-exhaustion and gadget risks.

    JobWorkflowExecutor._exec_inline_python() allowed_modules and import validation = Allow os.path only; do not allow os

Event History

Oct 29, 2026
Advisory Published
via GitHub·04:34 PM
Data Sourced
via GitHub·04:34 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

When is an environment exposed to this issue?

Exposure requires job workflows to be enabled with PRAISONAI_ALLOW_JOB_WORKFLOWS=true and for a user or CI process to run a YAML workflow containing a job script. The workflow executes with the privileges of the calling process.

2

What access does an attacker need to exploit it?

An attacker needs a way to cause a malicious YAML job workflow to be executed, such as contributing a workflow file, supplying a downloaded template, or placing a workflow in a shared repository. No separate operating-system access is required once the workflow is run.

3

What can be done if patching is not immediately possible?

Disable job workflow execution by ensuring PRAISONAI_ALLOW_JOB_WORKFLOWS is not enabled, and do not run untrusted YAML workflows. Treat workflows from downloaded templates, shared repositories, and AI-generated sources as untrusted until reviewed.

4

How can teams identify potentially dangerous workflows?

Review YAML workflows with type: job and script steps, especially those importing os or invoking operating-system commands through os.system. Also identify CI jobs or user processes that run praisonai workflow run while job workflows are enabled.

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