Improper resource allocation In praisonaiagents
Description
PraisonAIAgents: Environment Variable Secret Exfiltration via os.path.expandvars() Bypassing shell=False in Shell Tool
Summary
The execute_command function in shell_tools.py calls os.path.expandvars() on every command argument at line 64, manually re-implementing shell-level environment variable expansion despite using shell=False (line 88) for security. This allows exfiltration of secrets stored in environment variables (database credentials, API keys, cloud access keys). The approval system displays the unexpanded $VAR references to human reviewers, creating a deceptive approval where the displayed command differs from what actually executes.
Details
The vulnerable code is in src/praisonai-agents/praisonaiagents/tools/shell_tools.py:
# Line 60: command is split command = shlex.split(command) # (shell=False means the shell won't do this for us) command = [os.path.expanduser(os.path.expandvars(arg)) for arg in command] # Line 88: shell=False is supposed to prevent shell feature access process = subprocess.Popen(...
The security problem is a disconnect between the approval display and actual execution:
The LLM generates a tool call: execute_command(command="cat $DATABASE_URL")
_check_tool_approval_sync in tool_execution.py:558 passes {"command": "cat $DATABASE_URL"} to the approval backend
ConsoleBackend (backends.py:81-85) displays command: cat $DATABASE_URL — the literal dollar-sign form
The user approves, reasoning that shell=False prevents variable expansion
Inside execute_command, os.path.expandvars("$DATABASE_URL") → postgres://user:secretpass@prod-host:5432/mydb
The expanded secret appears in stdout, returned to the LLM
Line 69 has the same issue for the cwd parameter:
cwd = os.path.expandvars(cwd) # Also expand $HOME, $USER, etc.
With PRAISONAI_AUTO_APPROVE=true (registry.py:170-171), AutoApproveBackend, YAML-approved tools, or AgentApproval, no human reviews the command at all. The env var auto-approve check is:
# registry.py:170-171 @staticmethod def is_env_auto_approve() -> bool: return os.environ.get("PRAISONAI_AUTO_APPROVE", "").lower() in ("true", "1", "yes")
PoC
import os # Simulate secrets in environment (common in production/CI) os.environ['DATABASE_URL'] = 'postgres://admin:s3cretP@ss@prod-db.internal:5432/app' os.environ['AWS_SECRET_ACCESS_KEY'] = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' # Enable auto-approve (as used in CI/automated deployments) os.environ['PRAISONAI_AUTO_APPROVE'] = 'true'...
Verification without auto-approve (deceptive approval display):
# Do you want to execute this critical risk tool? [y/N] # # Actual execution expands $DATABASE_URL to the real credential.
Impact
Secret exfiltration: All environment variables accessible to the process are exposed, including database credentials (DATABASE_URL), cloud keys (AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID), API tokens (OPENAI_API_KEY, ANTHROPIC_API_KEY), and any other secrets passed via environment.
Deceptive approval: The approval UI shows $VAR references while the system executes with expanded secrets, undermining the human-in-the-loop security control. Users familiar with shell=False semantics will expect no variable expansion.
Automated environments at highest risk: CI/CD pipelines and production deployments using PRAISONAI_AUTO_APPROVE=true, AutoApproveBackend, or YAML tool pre-approval have no human review gate. These environments typically have the most sensitive secrets in environment variables.
Prompt injection amplifier: In agentic workflows processing untrusted content (documents, emails, web pages), a prompt injection can direct the LLM to call execute_command with $VAR references to exfiltrate specific secrets.
Recommended Fix
Remove os.path.expandvars() from command argument processing. Only keep os.path.expanduser() for tilde expansion (which is safe — it only expands ~ to the home directory path):
# shell_tools.py, line 64 — BEFORE (vulnerable): command = [os.path.expanduser(os.path.expandvars(arg)) for arg in command] # AFTER (fixed): command = [os.path.expanduser(arg) for arg in command]
Similarly for cwd on line 69:
# BEFORE (vulnerable): cwd = os.path.expandvars(cwd) # (delete line 69)
If environment variable expansion is needed for specific use cases, it should:
Be opt-in via an explicit parameter (e.g., expand_env=False default)
Show the expanded command in the approval display so humans can see actual values
Have an allowlist of safe variable names (e.g., HOME, USER, PATH) rather than expanding all variables
Mitigation
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Component | Affected version | Patched versions |
|---|---|---|---|
pypi | 1.5.128 |
Aliases
References