Every CVE Search/ Get API key

CVE-2026-40111

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

9.3 CriticalFix available

PraisonAIAgents is a multi-agent teams system. Prior to 1.5.128, he 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. No sanitization is performed and shell metacharacters 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 vulnerability is fixed in 1.5.128.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
High

Exploit likelihood

0.24%Up 0.2 points since Apr 10, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.3

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

PraisonAIAgents is a multi-agent teams system. Prior to 1.5.128, he 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. No sanitization is performed and shell metacharacters 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 vulnerability is fixed in 1.5.128.

Record
CVE-2026-40111
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-78
Updated
2026-11-02
EUVDEUVD-2026-21152 9.3

PraisonAIAgents is a multi-agent teams system. Prior to 1.5.128, he 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. No sanitization is performed and shell metacharacters 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 vulnerability is fixed in 1.5.128.

Record
EUVD-2026-21152
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-v7px-3835-7gjx 9.3

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

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,
            capture_output=True,
            text=True,
            timeout=hook.timeout
        )

Step 4. Confirm zero sanitization in this file

grep -n "shlex\|quote\|sanitize\|allowlist\|banned_chars\|strip\|validate" \
  src/praisonai-agents/praisonaiagents/memory/hooks.py

Confirmed output:
(no output)

Step 5. Confirm hooks.json load and lifecycle dispatch

grep -rn "hooks\.json\|BEFORE_TOOL\|AFTER_TOOL\|hook.*execut\|execut.*hook" \
  src/praisonai-agents/praisonaiagents/ \
  --include="*.py"

Confirmed output (key lines):
memory/hooks.py:105:   CONFIG_FILE = f"{_DIR_NAME}/hooks.json"
memory/hooks.py:396:   config_path = config_dir / "hooks.json"
agent/tool_execution.py:183:   self._hook_runner.execute_sync(HookEvent.BEFORE_TOOL, ...)
agent/chat_mixin.py:2052:      await self._hook_runner.execute(HookEvent.BEFORE_TOOL, ...)
hooks/runner.py:210:           return await self._execute_command_hook(...)

Step 6. Confirm…

Record
GHSA-v7px-3835-7gjx
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-78
Updated
2026-11-02
NVD 9.3

PraisonAIAgents is a multi-agent teams system. Prior to 1.5.128, he 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. No sanitization is performed and shell metacharacters 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 vulnerability is fixed in 1.5.128.

Record
CVE-2026-40111
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1
8.8 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Weakness
CWE-78
Updated
2026-11-02
OSVGHSA-v7px-3835-7gjx 9.3

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

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,
            capture_output=True,
            text=True,
            timeout=hook.timeout
        )

Step 4. Confirm zero sanitization in this file

grep -n "shlex\|quote\|sanitize\|allowlist\|banned_chars\|strip\|validate" \
  src/praisonai-agents/praisonaiagents/memory/hooks.py

Confirmed output:
(no output)

Step 5. Confirm hooks.json load and lifecycle dispatch

grep -rn "hooks\.json\|BEFORE_TOOL\|AFTER_TOOL\|hook.*execut\|execut.*hook" \
  src/praisonai-agents/praisonaiagents/ \
  --include="*.py"

Confirmed output (key lines):
memory/hooks.py:105:   CONFIG_FILE = f"{_DIR_NAME}/hooks.json"
memory/hooks.py:396:   config_path = config_dir / "hooks.json"
agent/tool_execution.py:183:   self._hook_runner.execute_sync(HookEvent.BEFORE_TOOL, ...)
agent/chat_mixin.py:2052:      await self._hook_runner.execute(HookEvent.BEFORE_TOOL, ...)
hooks/runner.py:210:           return await self._execute_command_hook(...)

Step 6. Confirm…

Record
GHSA-v7px-3835-7gjx
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Weakness
CWE-78
Updated
2026-11-02
OSVPYSEC-2026-487 9.3

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

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,
            capture_output=True,
            text=True,
            timeout=hook.timeout
        )

Step 4. Confirm zero sanitization in this file

grep -n "shlex\|quote\|sanitize\|allowlist\|banned_chars\|strip\|validate" \
  src/praisonai-agents/praisonaiagents/memory/hooks.py

Confirmed output:
(no output)

Step 5. Confirm hooks.json load and lifecycle dispatch

grep -rn "hooks\.json\|BEFORE_TOOL\|AFTER_TOOL\|hook.*execut\|execut.*hook" \
  src/praisonai-agents/praisonaiagents/ \
  --include="*.py"

Confirmed output (key lines):
memory/hooks.py:105:   CONFIG_FILE = f"{_DIR_NAME}/hooks.json"
memory/hooks.py:396:   config_path = config_dir / "hooks.json"
agent/tool_execution.py:183:   self._hook_runner.execute_sync(HookEvent.BEFORE_TOOL, ...)
agent/chat_mixin.py:2052:      await self._hook_runner.execute(HookEvent.BEFORE_TOOL, ...)
hooks/runner.py:210:           return await self._execute_command_hook(...)

Step 6. Confirm…

Record
PYSEC-2026-487
CVSS 4.0
9.3 CriticalCVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonaiagents
PyPI
through 1.5.126
1.5.128
praisonaiagents
PyPI
before 1.5.128, plus listed versions
0.0.10.0.100.0.1000.0.1010.0.102
Show 45 moreShow less
0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143
1.5.128

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-40111 is a critical vulnerability (CVSS 9.3) in Mervinpraison Praisonaiagents, published April 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.24%, the 14th percentile. Fixed in praisonaiagents 1.5.128.

References

This page doesn't list everything

Left out here: 457 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open