GHSA-v847-hxxw-3pxg: PraisonAI: streaming bypass enables dangerous tool execution

GHSA-v847-hxxw-3pxg HIGH
Published June 18, 2026
CISO Take

PraisonAI's recipe streaming path — invoked via the --stream CLI flag or HTTP POST /v1/recipes/stream — skips the dangerous-tool policy check that the standard run() path enforces, allowing any recipe declaring execute_command (or other blocked tools) to reach OS-level execution even when allow_dangerous_tools is explicitly false. A public proof-of-concept is embedded in the advisory and the bypass requires no special knowledge beyond substituting the streaming entrypoint for the normal one, placing this squarely in script-kiddie territory; the CVSS base score is 7.8 (C:H/I:H/A:H), and while the local attack vector limits default remote exposure, the documented HTTP sidecar makes this effectively remote-exploitable in any deployment that accepts user-supplied recipe names over the streaming endpoint. Upgrade to praisonai 4.6.59 immediately; if patching is delayed, firewall-block /v1/recipes/stream and strip --stream from approved CLI invocations until the fix is in place.

Sources: GitHub Advisory ATLAS NVD

What is the risk?

HIGH. The bypass is mechanically trivial — the attacker substitutes run_stream() for run() and the dangerous-tool gate is silently skipped. No privilege escalation or AI/ML domain expertise is required. A working PoC with command output (PRAI-CAND-012-CANARY) is published in the advisory itself. The CVSS 7.8 with C:H/I:H/A:H signals full triad impact on successful exploitation. While the base vector is local (AV:L), the recipe HTTP sidecar — documented with optional API-key/JWT auth — exposes /v1/recipes/stream as a REST endpoint, extending blast radius to authenticated network attackers in any non-default sidecar deployment. The package carries 106 prior CVEs, indicating a history of insecure defaults that suggests this class of finding is not isolated.

How does the attack unfold?

Streaming Endpoint Access
Attacker invokes the recipe streaming path via CLI (praisonai recipe run <recipe> --stream) or HTTP (POST /v1/recipes/stream) using existing legitimate or compromised access to the deployment.
AML.T0049
Policy Gate Bypass
run_stream() loads the recipe and checks dependencies but calls _execute_recipe() without invoking _check_tool_policy(), silently bypassing the dangerous-tool enforcement that run() applies.
AML.T0107
Dangerous Tool Resolution
The workflow resolves execute_command from the recipe's TEMPLATE.yaml requires.tools and installs the approval context, enabling tool invocation despite allow_dangerous_tools=False being set.
AML.T0053
OS Command Execution
Attacker-controlled commands execute with PraisonAI process privileges, enabling credential theft, model weight exfiltration, persistence establishment, or lateral movement to adjacent AI infrastructure.
AML.T0050

What systems are affected?

Package Ecosystem Vulnerable Range Patched
PraisonAI pip >= 4.5.87, <= 4.6.58 4.6.59
1 dependents 68% patched ~14d to patch Full package profile →
PraisonAI Agents pip No patch
20 dependents 65% patched ~6d to patch Full package profile →

How severe is it?

CVSS 3.1
7.8 / 10
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Trivial

What is the attack surface?

AV AC PR UI S C I A
AV Local
AC Low
PR None
UI Required
S Unchanged
C High
I High
A High

What should I do?

5 steps
  1. PATCH

    Upgrade to praisonai >= 4.6.59 immediately — this is the confirmed patched release.

  2. WORKAROUND (if patching is delayed): Block /v1/recipes/stream at the network/firewall layer; remove --stream from approved CLI invocations in runbooks and automation; apply AppArmor or seccomp profiles to restrict subprocess spawning from the PraisonAI process.

  3. AUDIT

    Review all TEMPLATE.yaml recipe files for execute_command or other dangerous-tool declarations; identify any CI/CD pipelines or automation that invokes run_stream() or the streaming endpoint.

  4. DETECT

    Alert on unexpected child-process spawning from the PraisonAI parent process; monitor HTTP access logs for POST /v1/recipes/stream requests, especially from non-operator source IPs.

  5. VERIFY POST-PATCH: Confirm that _check_tool_policy() is called in run_stream() before _execute_recipe() in the deployed 4.6.59 source.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 9 - Risk management system
ISO 42001
8.4 - AI system operation
NIST AI RMF
GOVERN-1.2 - Risk tolerance and risk response
OWASP LLM Top 10
LLM06 - Excessive Agency LLM08 - Excessive Permissions

Frequently Asked Questions

What is GHSA-v847-hxxw-3pxg?

PraisonAI's recipe streaming path — invoked via the --stream CLI flag or HTTP POST /v1/recipes/stream — skips the dangerous-tool policy check that the standard run() path enforces, allowing any recipe declaring execute_command (or other blocked tools) to reach OS-level execution even when allow_dangerous_tools is explicitly false. A public proof-of-concept is embedded in the advisory and the bypass requires no special knowledge beyond substituting the streaming entrypoint for the normal one, placing this squarely in script-kiddie territory; the CVSS base score is 7.8 (C:H/I:H/A:H), and while the local attack vector limits default remote exposure, the documented HTTP sidecar makes this effectively remote-exploitable in any deployment that accepts user-supplied recipe names over the streaming endpoint. Upgrade to praisonai 4.6.59 immediately; if patching is delayed, firewall-block /v1/recipes/stream and strip --stream from approved CLI invocations until the fix is in place.

Is GHSA-v847-hxxw-3pxg actively exploited?

No confirmed active exploitation of GHSA-v847-hxxw-3pxg has been reported, but organizations should still patch proactively.

How to fix GHSA-v847-hxxw-3pxg?

1. PATCH: Upgrade to praisonai >= 4.6.59 immediately — this is the confirmed patched release. 2. WORKAROUND (if patching is delayed): Block /v1/recipes/stream at the network/firewall layer; remove --stream from approved CLI invocations in runbooks and automation; apply AppArmor or seccomp profiles to restrict subprocess spawning from the PraisonAI process. 3. AUDIT: Review all TEMPLATE.yaml recipe files for execute_command or other dangerous-tool declarations; identify any CI/CD pipelines or automation that invokes run_stream() or the streaming endpoint. 4. DETECT: Alert on unexpected child-process spawning from the PraisonAI parent process; monitor HTTP access logs for POST /v1/recipes/stream requests, especially from non-operator source IPs. 5. VERIFY POST-PATCH: Confirm that _check_tool_policy() is called in run_stream() before _execute_recipe() in the deployed 4.6.59 source.

What systems are affected by GHSA-v847-hxxw-3pxg?

This vulnerability affects the following AI/ML architecture patterns: agent frameworks, agentic pipelines, AI recipe orchestration, AI workflow automation.

What is the CVSS score for GHSA-v847-hxxw-3pxg?

GHSA-v847-hxxw-3pxg has a CVSS v3.1 base score of 7.8 (HIGH).

What is the AI security impact?

Affected AI Architectures

agent frameworksagentic pipelinesAI recipe orchestrationAI workflow automation

MITRE ATLAS Techniques

AML.T0049 Exploit Public-Facing Application
AML.T0050 Command and Scripting Interpreter
AML.T0053 AI Agent Tool Invocation
AML.T0107 Exploitation for Defense Evasion

Compliance Controls Affected

EU AI Act: Article 9
ISO 42001: 8.4
NIST AI RMF: GOVERN-1.2
OWASP LLM Top 10: LLM06, LLM08

What are the technical details?

Original Advisory

# PraisonAI `recipe.run_stream()` skips dangerous-tool policy enforcement ## Summary PraisonAI recipe execution blocks default-denied dangerous tools unless the caller explicitly passes `allow_dangerous_tools=True`. The normal `recipe.run()` path enforces this with `_check_tool_policy()`. The streaming path, `recipe.run_stream()`, loads the same recipe, checks dependencies, and then calls `_execute_recipe()` without running the dangerous-tool policy check. As a result, a recipe that honestly declares `execute_command` in `TEMPLATE.yaml requires.tools` is denied by `recipe.run()`, but reaches the execution engine through `recipe.run_stream()` with `allow_dangerous_tools=False`. The local PoV uses a harmless `printf` canary, explicitly unsets `PRAISONAI_AUTO_APPROVE`, and avoids network access. ## Affected Product - Repository: `MervinPraison/PraisonAI` - Package: `praisonai` - Components: - `src/praisonai/praisonai/recipe/core.py` - `src/praisonai/praisonai/recipe/serve.py` - `src/praisonai/praisonai/cli/features/recipe.py` - `src/praisonai-agents/praisonaiagents/workflows/yaml_parser.py` - `src/praisonai-agents/praisonaiagents/workflows/workflows.py` Validated affected: - current main `2f9677abb2ea68eab864ee8b6a828fd0141612e1` (`v4.6.57-4-g2f9677ab`) - `v4.6.57` - `v4.6.56` - `v4.6.10` - `v4.6.9` - `v4.5.128` - `v4.5.120` - `v4.5.96` - `v4.5.87` Suggested affected range: `>= 4.5.87, <= 4.6.57`. PyPI lists `PraisonAI 4.6.57` as the latest release on 2026-06-13. Earlier tested tags through `v4.5.85` failed in this source checkout before the tested workflow path due an unrelated `praisonaiagents.output.models` import error. They are not claimed fixed or unaffected. ## Root Cause `recipe.run()` enforces the dangerous-tool gate: ```python if not options.get("allow_dangerous_tools", False): policy_error = _check_tool_policy(recipe_config) if policy_error: return RecipeResult(..., status=RecipeStatus.POLICY_DENIED, ...) ``` `recipe.run_stream()` has a sibling execution path. It loads the recipe and checks dependencies, but then goes directly to execution: ```python recipe_config = _load_recipe(name, offline=options.get("offline", False)) ... output = _execute_recipe(recipe_config, merged_config, session_id, options) ``` There is no equivalent `_check_tool_policy()` call in `run_stream()` before execution or before the dry-run shortcut. The CLI exposes this path via `praisonai recipe run <recipe> --stream`, and the recipe HTTP server exposes it as `POST /v1/recipes/stream`. ## Why This Is Not Intended Behavior The normal recipe path clearly treats declared dangerous tools as denied by default. A control recipe with `TEMPLATE.yaml requires.tools: [execute_command]` returns: ```text Tool 'execute_command' is denied by default. Use allow_dangerous_tools=True to override. ``` That operator-facing override should not depend on whether the caller requests streaming output. PraisonAI's own docs describe approval as requiring a human or configured channel before risky tools run, describe security environment variables as opt-in access for dangerous operations with secure defaults, and describe policy controls as blocking dangerous operations. This is distinct from the prior report `PRAI-CAND-011`: - `PRAI-CAND-011` covers workflow tool declarations that are omitted from `TEMPLATE.yaml requires.tools`. - This report covers a sibling entrypoint that skips the policy check even when `TEMPLATE.yaml` correctly declares the dangerous tool. It is also distinct from the published Recipe-server authentication fail-open advisory. That advisory covers missing authentication secrets. This report assumes the attacker has whatever access is already needed to invoke recipe streaming and focuses on the missing dangerous-tool policy guard. ## Local PoV Run: ```bash python3 poc/pov_prai_cand_012_stream_policy_bypass.py ``` Expected output includes: ```json { "ok": true, "policy_error": "Tool 'execute_command' is denied by default. Use allow_dangerous_tools=True to override.", "control_recipe_status": "policy_denied", "execution_reached": [ { "recipe": "declared-dangerous-stream", "declared_required_tools": ["execute_command"], "allow_dangerous_tools": false } ], "workflow_approve_tools": ["execute_command"], "runner_tool_names": ["execute_command"], "command_stdout": "PRAI-CAND-012-CANARY", "operator_env_auto_approve": null } ``` The PoV creates a temporary recipe that declares `execute_command` in `TEMPLATE.yaml requires.tools`. Control: - `recipe.run(..., options={"force": True})` returns `policy_denied`. Bypass: - `recipe.run_stream(..., options={"force": True})` emits the `executing` event and reaches `_execute_recipe()` while `allow_dangerous_tools` remains false. - The same recipe workflow resolves `execute_command` and preserves `approve: [execute_command]`. - With the workflow approval context installed, the resolved tool runs the harmless local command `printf PRAI-CAND-012-CANARY`. The PoV monkey-patches `_execute_recipe()` only to prove that `run_stream()` crosses the policy boundary without invoking an LLM. The command canary is executed directly through the same resolved workflow tool and approval context to keep the proof deterministic and local-only. ## Impact If an operator runs an untrusted recipe through streaming mode, or exposes the recipe streaming API to users who can choose recipe names or URIs, the recipe can reach execution with default-denied tools even though the caller did not set `allow_dangerous_tools=True`. If the workflow reaches the approved `execute_command` tool call, commands run with the privileges of the PraisonAI process. The exact trigger depends on the workflow and model/tool-call path, but the dangerous-tool policy boundary is already bypassed before execution. The HTTP recipe sidecar is documented as a localhost REST API with SSE streaming and optional API-key/JWT authentication. This report does not claim default unauthenticated network RCE. In authenticated or exposed sidecar deployments where lower-trust users can invoke `/v1/recipes/stream`, the same policy gap can become a remote recipe-execution issue. ## Suggested Fix Centralize recipe preflight enforcement so every execution mode uses the same guard: 1. Run `_check_tool_policy(recipe_config)` in `run_stream()` unless `options["allow_dangerous_tools"]` is true. 2. Perform that check before both dry-run and real execution, matching `recipe.run()`. 3. Prefer a shared helper for dependency checks, dangerous-tool policy checks, and dry-run handling so future entrypoints cannot drift. 4. Add regression tests: - declared dangerous tool is denied by `recipe.run()`; - the same declared dangerous tool is denied by `recipe.run_stream()`; - `allow_dangerous_tools=True` preserves the intended opt-in behavior; - `/v1/recipes/stream` maps a policy denial to a non-success SSE event or equivalent HTTP failure.

Exploitation Scenario

An attacker with access to a PraisonAI deployment — a developer on the same host, an authenticated sidecar API user, or a lower-trust user in a shared environment — references a recipe whose TEMPLATE.yaml declares execute_command in requires.tools. Invoking recipe.run() returns 'Tool execute_command is denied by default' and stops. The attacker instead calls recipe.run_stream() via --stream or POST /v1/recipes/stream with the same recipe name. The streaming path loads the recipe, validates dependencies, and calls _execute_recipe() without ever invoking _check_tool_policy(), so allow_dangerous_tools=False is never evaluated. The workflow resolves execute_command, preserves its approval context, and executes attacker-controlled OS commands — reading API keys or model weights from disk, establishing a reverse shell, or exfiltrating training data — all under the privileges of the PraisonAI process.

Weaknesses (CWE)

CWE-693 — Protection Mechanism Failure: The product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks against the product.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Timeline

Published
June 18, 2026
Last Modified
June 18, 2026
First Seen
June 18, 2026

Related Vulnerabilities