CVE-2026-56074

mediumpriority · track · 0OT risk · elevatedEPSS 0.002 · 7 %ileEUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.18% (7th) EPSS
CVSS 5.5 · medium
Is it mine?
2 products
Check your exposure
Who says so?
3 sources
cnnvd · enisa_euvd · osv

EPSS · scored 2026-10-27

PraisonAI before 1.5.128 caches tool approval decisions by tool name only, not by invocation arguments, allowing subsequent execute_command calls to bypass approval prompts. Attackers can exploit this by obtaining initial approval for a benign command, then silently exfiltrate API keys and credentials via subsequent shell commands without user consent.

01

cvss

Technical severity, 0–10. One row per scoring source.

6.8
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.15.5mediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:Nsecondary · disclosure@vulncheck.comexploitability 1.8 · impact 3.6
v3.15.5mediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:Nsecondary · OSV
v4.06.8mediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:Nsecondary · ENISA EU
v4.06.8mediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · disclosure@vulncheck.com
v4.04.3mediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (2)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-ffp3-3562-8cv3PraisonAI: Coarse-Grained Tool Approval Cache Bypasses Per-Invocation Consent for Shell Commands2026-04-10ghsa · osvUS

    ## Summary The approval system in PraisonAI Agents caches tool approval decisions by tool name only, not by invocation arguments. Once a user approves `execute_command` for any command (e.g., `ls -la`), all subsequent `execute_command` calls in that execution context bypass the approval prompt entirely. Combined with `os.environ.copy()` passing all process environment variables to subprocesses, this allows an LLM agent (potentially via prompt injection) to silently exfiltrate API keys and credentials without further user consent. ## Details The `require_approval` decorator in `src/praisonai-agents/praisonaiagents/approval/__init__.py:176-178` checks approval status by tool name only: ```python @wraps(func) def wrapper(*args, **kwargs): if is_already_approved(tool_name): # line 177 — checks only tool_name return func(*args, **kwargs) # line 178 — bypasses ALL approval ``` The `mark_approved` function in `registry.py:144-147` stores only the tool name string: ```python def mark_approved(self, tool_name: str) -> None: approved = self._approved_context.get(set()) approved.add(tool_name) # stores "execute_command", not args self._approved_context.set(approved) ``` The approval context is never cleared during agent execution — `clear_approved()` exists (`registry.py:152`) but is never called in the agent's tool execution path (`agent/tool_execution.py`). Meanwhile, the `ConsoleBackend` UI at `backends.py:95-96` misleads the user: ```python return Confirm.ask( f"Do you want to execute this {request.risk_level} risk tool?", # "this" implies per-invocation approval ) ``` The UI displays the specific command arguments (lines 81-85), creating a reasonable expectation that the user is approving only that specific invocation. Additionally, `shell_tools.py:77` passes the full process environment to every subprocess: ```python process_env = os.environ.copy() # includes OPENAI_API_KEY, etc. ``` There is no command filtering, blocklist, or environment variable sanitization in the shell tools module. ## PoC ```python from praisonaiagents import Agent from praisonaiagents.tools.shell_tools import execute_command # Step 1: Create agent with shell tool agent = Agent( name="worker", instructions="You are a helpful assistant.", tools=[execute_command] ) # Step 2: Agent requests benign command — user sees Rich panel: # Function: execute_command # Risk Level: CRITICAL # Arguments: # command: ls -la # "Do you want to execute this critical risk tool?" [y/N] # User approves → mark_approved("execute_command") is called # Step 3: All subsequent execute_command calls bypass approval silently: # execute_command(command="env") # → returns ALL environment variables (OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, etc.) # → NO approval prompt shown # Step 4: Targeted extraction also bypasses approval: # execute_command(command="printenv OPENAI_API_KEY") # → returns the specific API key # → NO approval prompt shown # Verification: check the approval cache from praisonaiagents.approval import is_already_approved # After approving "ls -la": # is_already_approved("execute_command") → True # Any execute_command call now returns immediately at __init__.py:177-178 ``` ## Impact - **Secret exfiltration**: An LLM agent (or one subjected to prompt injection) can dump all process environment variables after a single benign command approval. Common secrets include `OPENAI_API_KEY`, `AWS_SECRET_ACCESS_KEY`, `DATABASE_URL`, and any other credentials passed via environment. - **Misleading consent UI**: The console prompt displays specific arguments and uses language ("this tool") that implies per-invocation consent, but the system grants session-wide blanket approval. - **No expiration or scope**: The approval cache uses a `ContextVar` that persists for the entire agent execution context with no timeout, no command-count limit, and no clearing between tool calls. - **No environment filtering**: `os.environ.copy()` passes every environment variable to subprocesses without filtering sensitive patterns. ## Recommended Fix 1. **Per-invocation approval for critical tools** — store a hash of `(tool_name, arguments)` instead of just `tool_name`, or require re-approval for each invocation of critical-risk tools: ```python # In registry.py — change mark_approved/is_already_approved: import hashlib, json def mark_approved(self, tool_name: str, arguments: dict = None) -> None: approved = self._approved_context.get(set()) risk = self._risk_levels.get(tool_name) if risk == "critical" and arguments: key = f"{tool_name}:{hashlib.sha256(json.dumps(arguments, sort_keys=True).encode()).hexdigest()}" else: key = tool_name approved.add(key) self._approved_context.set(approved) def is_already_approved(self, tool_name: str, arguments: dict = None) -> bool: approved = self._approved_context.get(set()) risk = self._risk_levels.get(tool_name) if risk == "critical" and arguments: key = f"{tool_name}:{hashlib.sha256(json.dumps(arguments, sort_keys=True).encode()).hexdigest()}" return key in approved return tool_name in approved ``` 2. **Filter environment variables** in `shell_tools.py`: ```python SENSITIVE_PATTERNS = ('_KEY', '_SECRET', '_TOKEN', '_PASSWORD', '_CREDENTIAL') process_env = { k: v for k, v in os.environ.items() if not any(p in k.upper() for p in SENSITIVE_PATTERNS) } if env: process_env.update(env) ```

  • PYSEC-2026-2946PraisonAI: Coarse-Grained Tool Approval Cache Bypasses Per-Invocation Consent for Shell Commands2026-07-13vendor · osvUS

    ## Summary The approval system in PraisonAI Agents caches tool approval decisions by tool name only, not by invocation arguments. Once a user approves `execute_command` for any command (e.g., `ls -la`), all subsequent `execute_command` calls in that execution context bypass the approval prompt entirely. Combined with `os.environ.copy()` passing all process environment variables to subprocesses, this allows an LLM agent (potentially via prompt injection) to silently exfiltrate API keys and credentials without further user consent. ## Details The `require_approval` decorator in `src/praisonai-agents/praisonaiagents/approval/__init__.py:176-178` checks approval status by tool name only: ```python @wraps(func) def wrapper(*args, **kwargs): if is_already_approved(tool_name): # line 177 — checks only tool_name return func(*args, **kwargs) # line 178 — bypasses ALL approval ``` The `mark_approved` function in `registry.py:144-147` stores only the tool name string: ```python def mark_approved(self, tool_name: str) -> None: approved = self._approved_context.get(set()) approved.add(tool_name) # stores "execute_command", not args self._approved_context.set(approved) ``` The approval context is never cleared during agent execution — `clear_approved()` exists (`registry.py:152`) but is never called in the agent's tool execution path (`agent/tool_execution.py`). Meanwhile, the `ConsoleBackend` UI at `backends.py:95-96` misleads the user: ```python return Confirm.ask( f"Do you want to execute this {request.risk_level} risk tool?", # "this" implies per-invocation approval ) ``` The UI displays the specific command arguments (lines 81-85), creating a reasonable expectation that the user is approving only that specific invocation. Additionally, `shell_tools.py:77` passes the full process environment to every subprocess: ```python process_env = os.environ.copy() # includes OPENAI_API_KEY, etc. ``` There is no command filtering, blocklist, or environment variable sanitization in the shell tools module. ## PoC ```python from praisonaiagents import Agent from praisonaiagents.tools.shell_tools import execute_command # Step 1: Create agent with shell tool agent = Agent( name="worker", instructions="You are a helpful assistant.", tools=[execute_command] ) # Step 2: Agent requests benign command — user sees Rich panel: # Function: execute_command # Risk Level: CRITICAL # Arguments: # command: ls -la # "Do you want to execute this critical risk tool?" [y/N] # User approves → mark_approved("execute_command") is called # Step 3: All subsequent execute_command calls bypass approval silently: # execute_command(command="env") # → returns ALL environment variables (OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, etc.) # → NO approval prompt shown # Step 4: Targeted extraction also bypasses approval: # execute_command(command="printenv OPENAI_API_KEY") # → returns the specific API key # → NO approval prompt shown # Verification: check the approval cache from praisonaiagents.approval import is_already_approved # After approving "ls -la": # is_already_approved("execute_command") → True # Any execute_command call now returns immediately at __init__.py:177-178 ``` ## Impact - **Secret exfiltration**: An LLM agent (or one subjected to prompt injection) can dump all process environment variables after a single benign command approval. Common secrets include `OPENAI_API_KEY`, `AWS_SECRET_ACCESS_KEY`, `DATABASE_URL`, and any other credentials passed via environment. - **Misleading consent UI**: The console prompt displays specific arguments and uses language ("this tool") that implies per-invocation consent, but the system grants session-wide blanket approval. - **No expiration or scope**: The approval cache uses a `ContextVar` that persists for the entire agent execution context with no timeout, no command-count limit, and no clearing between tool calls. - **No environment filtering**: `os.environ.copy()` passes every environment variable to subprocesses without filtering sensitive patterns. ## Recommended Fix 1. **Per-invocation approval for critical tools** — store a hash of `(tool_name, arguments)` instead of just `tool_name`, or require re-approval for each invocation of critical-risk tools: ```python # In registry.py — change mark_approved/is_already_approved: import hashlib, json def mark_approved(self, tool_name: str, arguments: dict = None) -> None: approved = self._approved_context.get(set()) risk = self._risk_levels.get(tool_name) if risk == "critical" and arguments: key = f"{tool_name}:{hashlib.sha256(json.dumps(arguments, sort_keys=True).encode()).hexdigest()}" else: key = tool_name approved.add(key) self._approved_context.set(approved) def is_already_approved(self, tool_name: str, arguments: dict = None) -> bool: approved = self._approved_context.get(set()) risk = self._risk_levels.get(tool_name) if risk == "critical" and arguments: key = f"{tool_name}:{hashlib.sha256(json.dumps(arguments, sort_keys=True).encode()).hexdigest()}" return key in approved return tool_name in approved ``` 2. **Filter environment variables** in `shell_tools.py`: ```python SENSITIVE_PATTERNS = ('_KEY', '_SECRET', '_TOKEN', '_PASSWORD', '_CREDENTIAL') process_env = { k: v for k, v in os.environ.items() if not any(p in k.upper() for p in SENSITIVE_PATTERNS) } if env: process_env.update(env) ```

06

also known as (6)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-06-18modified 2026-10-26
08

references (7)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan