GitHub Security Advisory: GHSA-v7px-3835-7gjx
Package: praisonaiagents (PIP)
Severity: CRITICAL (CVSS 0.0)
CWE: CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Vulnerable versions: <= 1.5.126
Patched in: 1.5.128

Summary: PraisonAIAgents has an OS Command Injection via shell=True in Memory Hooks Executor (memory/hooks.py)

Details:
Summary

The memory hooks executor in praisonaiagents passes a user-controlled command string
directly to subprocess.run() with shell=True at
src/praisonai-agents/praisonaiagents/memory/hooks.py lines 303 to 305.
No sanitization, no shlex.quote(), no character filter, and no allowlist check
exists anywhere in this file. Shell metacharacters including semicolons, pipes,
ampersands, backticks, dollar-sign substitutions, and newlines are interpreted by
/bin/sh before the intended command executes.

Two independent attack surfaces exist. The first is via pre_run_command and
post_run_command hook event types registered through the hooks configuration.
The second and more severe surface is the .praisonai/hooks.json lifecycle
configuration, where hooks registered for events such as BEFORE_TOOL and
AFTER_TOOL fire automatically during agent operation. An agent that gains
file-write access through prompt injection can overwrite .praisonai/hooks.json
and have its payload execute silently at every subsequent lifecycle event without
further user interaction.

This file and these surfaces are not covered by any existing published advisory.


Vulnerability Description

File    : src/praisonai-agents/praisonaiagents/memory/hooks.py
Lines   : 303 to 305

Vulnerable code:

    result = subprocess.run(
        command,
        shell=True,
        cwd=str(self.workspace_path),
        env=env,
        capture_output=True,
        text=True,
        timeout=hook.timeout
    )

The variable command originates from hook.command, which is loaded directly
from .praisonai/hooks.json at line 396 of the same file.

The hooks system registers pre_run_command and post_run_command as event types
at lines 54 and 55 and dispatches them through _execute_script() at line 261,
which calls the subprocess.run() block above.

HookRunner at hooks/runner.py line 210 routes command-type hooks through
_execute_command_hook(), which feeds into this executor.

BEFORE_TOOL and AFTER_TOOL events are fired automatically at every tool call
from agent/tool_execution.py line 183 and agent/chat_mixin.py line 2052.

No fix exists. shell=False does not appear anywhere in memory/hooks.py.


Grep Commands and Confirmed Output

Step 1. Confirm shell=True at exact line

    grep -n "shell=True" \
      src/praisonai-agents/praisonaiagents/memory/hooks.py

    Confirmed output:
    305:                shell=True,

Step 2. Confirm subprocess imported and called

    grep -n "import subprocess\|subprocess\.run\|subprocess\.Popen" \
      src/praisonai-agents/praisonaiagents/memory/hooks.py

    Confirmed output:
    41:import subprocess
    303:            result = subprocess.run(

Step 3. View full vulnerable call with context

    sed -n '295,320p' \
      src/praisonai-agents/praisonaiagents/memory/hooks.py

    Confirmed output:
            result = subprocess.run(
                command,
                shell=True,
                cwd=str(self.workspace_path),
                env=env,