# Vulnerability Verification Report

## 1. Summary
- **Vulnerability Type**: business_logic
- **Flagged Location**: src/praisonai/praisonai/ui/chat.py:159 and src/praisonai/praisonai/ui/code.py:158
- **Vulnerability Description**: 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 and enabling unapproved shell command execution.

## 2. Analysis Logic

### Step 1: Examining the flagged sink at `src/praisonai/praisonai/ui/chat.py:156-159`

```python
            from praisonai.cli.features.interactive_tools import get_interactive_tools, ToolConfig
            config = ToolConfig.from_env()
            config.workspace = os.getcwd()
            config.approval_mode = "auto"  # Trust mode - auto-approve all tool executions
```

The `chat.py` module loads `ToolConfig` from environment variables via `from_env()`, then unconditionally overwrites `approval_mode` to `"auto"`. This means even if an administrator sets `PRAISON_APPROVAL_MODE=manual`, the value is discarded. The same pattern exists in `code.py:155-158`.

**Analysis**: This is the core of the vulnerability — a business logic flaw where an explicitly configurable security setting is silently overridden.

**Next**: Investigate what `ToolConfig.from_env()` does and what approval modes it supports.

### Step 2: Investigating `ToolConfig.from_env()` at `src/praisonai/praisonai/cli/features/interactive_tools.py:80-105`

```python
    @classmethod
    def from_env(cls) -> "ToolConfig":
        """Create config from environment variables."""
        config = cls()
        # ...
        # Check approval mode
        approval = os.environ.get("PRAISON_APPROVAL_MODE", "")
        if approval in ("auto", "manual", "scoped"):
            config.approval_mode = approval
        return config
```

`ToolConfig.from_env()` correctly reads the `PRAISON_APPROVAL_MODE` environment variable and sets `config.approval_mode` to one of `"auto"`, `"manual"`, or `"scoped"`. This is the intended administrative control point.

**Analysis**: The `from_env()` method is correctly designed to allow administrators to configure the approval mode. However, this carefully loaded value is immediately discarded by the UI modules.

**Next**: Trace how the overridden `approval_mode` affects shell command execution.

### Step 3: Tracing the approval check in `acp_execute_command` at `src/praisonai/praisonai/cli/features/agent_tools.py:225-277`

```python
    def acp_execute_command(command: str, cwd: str = None) -> str:
        async def _execute():
            prompt = f"run command: {command}"
            result = await orchestrator.create_plan(prompt)
            # ...
            auto_approve = runtime.config.approval_mode == "auto"
            approved = await orchestrator.approve_plan(plan, auto=auto_approve)
            # ...
```

At line 249, `auto_approve` is set to `True` whenever `runtime.config.approval_mode == "auto"`. Because the UI modules force `approval_mode = "auto"`, `auto_approve` is always `True`.

**Next**: Investigate `approve_plan` in the `ActionOrchestrator` to see what happens when `auto=True`.

### Step 4: Examining `approve_plan` at `src/praisonai/praisonai/cli/features/action_orchestrator.py:273-315`

```python
    async def approve_plan(self, plan: ActionPlan, auto: bool = False) -> bool:
        if self.runtime.read_only:
            logger.warning("Cannot approve plan in read-only mode")
            return False

        approval_mode = self.runtime.config.approval_mode

        if approval_mode == "auto" or auto:
            plan.status = ActionStatus.APPROVED
            for step in plan.steps:
                step.status = ActionStatus.APPROVED
            return True

        elif approval_mode == "scoped":
            # Auto-approve safe actions, require manual for dangerous ones
            # ...

        else:  # manual
            # In manual mode, we return False to indicate approval is needed
            return False
```

At lines 290-294, when `approval_mode == "auto"` (or `auto=True`), the plan is immediately approved without any user confirmation. All steps in the plan are set to `ActionStatus.APPROVED`. The `"manual"` and `"scoped"` modes are never reached because the UI modules override the value.

**Next**: Verify the actual command execution in `_apply_step`.

### Step 5: Examining command execution in `_apply_step` at `src/praisonai/praisonai/cli/features/action_orchestrator.py:429-438`

```python
        elif step.action_type == ActionType.SHELL_COMMAND:
            import subprocess
            result = subprocess.run(
                step.target,
                shell=True,
                capture_output=True,
                text=True,
                cwd=str(workspace),
                timeout=30
            )
```

At lines 431-438, when the step type is `SHELL_COMMAND`, the target command string is executed directly via `subprocess.run(..., shell=True)`. There is no additional command-line sanitization here (the `_sanitize_command` referenced in the advisory does not exist in the current codebase, or is not called before this point). The only thing standing between an arbitrary command and execution was the approval gate, which is now bypassed.

**Next**: Check if there are any other callers or code paths that might preserve the admin setting.

### Step 6: Checking for other callers of `get_interactive_tools`

```
$ grep -r "get_interactive_tools" --include="*.py" .
```

Only `chat.py` and `code.py` invoke `get_interactive_tools` with a custom `config` parameter. No other UI entry points exist to provide an alternative path that respects `PRAISON_APPROVAL_MODE`.

### Step 7: Decision Tree Walk-through

From the `business_logic` template:

- **Q1: Is there a business constraint that could be violated?**
  → YES. The intended business constraint is: "Tool executions (especially shell commands) must respect the administrator-configured approval mode (`auto`/`manual`/`scoped`)."

- **Q2: Is the constraint enforced server-side (validation, domain logic, DB constraint)?**
  → NO. While `ToolConfig.from_env()` correctly reads the environment variable, the server-side enforcement is immediately nullified by the hardcoded override in `chat.py:159` and `code.py:158`.

- **Q3: Can the user manipulate the relevant parameter in the request?**
  → Not applicable in the traditional sense, but the admin's security-relevant parameter (`PRAISON_APPROVAL_MODE`) is effectively ignored by the application.

- **Q4: Is the code in test/demo/dead/generated context?**
  → NO. Both `chat.py` and `code.py` are the main Chainlit UI entry points for the PraisonAI application. They are actively used in production.

- **Q5: Does the override lead to actual unapproved shell execution?**
  → YES. The `acp_execute_command` tool routes through `ActionOrchestrator`, which auto-approves all plans because `approval_mode` is forced to `"auto"`. This leads directly to `subprocess.run(step.target, shell=True)` being executed without user confirmation.

→ Reached leaf: **TRUE_POSITIVE**

## 3. Verdict
**TRUE_POSITIVE**

**Key Evidence:**
- `src/praisonai/praisonai/ui/chat.py:156-159` — `ToolConfig.from_env()` loads the admin-configured `PRAISON_APPROVAL_MODE`, then immediately overwrites `config.approval_mode = "auto"`.
- `src/praisonai/praisonai/ui/code.py:155-158` — Identical hardcoded override.
- `src/praisonai/praisonai/cli/features/action_orchestrator.py:290-294` — When `approval_mode == "auto"`, all plans (including shell commands) are auto-approved without user interaction.
- `src/praisonai/praisonai/cli/features/action_orchestrator.py:431-438` — Auto-approved shell commands execute via `subprocess.run(step.target, shell=True)`.

## 4. Remediation Suggestions

1. **Remove the hardcoded override** in both UI modules (`chat.py` and `code.py`). Simply delete the line `config.approval_mode = "auto"` so that `ToolConfig.from_env()`'s value is respected.

2. **If a default is needed**, ensure it is applied at the `ToolConfig` dataclass level (already `approval_mode: str = "auto"`) and not overwritten after `from_env()` reads the environment variable.

3. **Strengthen command sanitization** if shell execution is to remain exposed. Consider an allowlist approach for commands rather than a blocklist, as described in the advisory.

4. **Add unit tests** that verify:
   - `PRAISON_APPROVAL_MODE=manual` results in `config.approval_mode == "manual"`
   - Shell commands are not auto-approved when `approval_mode` is set to `manual`
