Every CVE Search/ Get API key

CVE-2026-60088

PraisonAI before 4.6.78 Path Traversal via Custom Commands

6.8 MediumFix available

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Attack profile

How easy it is to exploit

Attack vector
Local
Complexity
Low
Privileges needed
None
User interaction
Passive

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.22%Up 0.1 points since Jul 12, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.8

PraisonAI before 4.6.78 Path Traversal via Custom Commands

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
CVE-2026-60088
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
CVSS 3.1
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-43174 6.8

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
EUVD-2026-43174
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-wgvq-3jxh-4qg7 6.8

Duplicate Advisory: PraisonAI: Project custom command templates can read outside-workspace files into model prompts

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-xpx6-x8c2-mw5w. This link is maintained to preserve external references.

Original Description

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
GHSA-wgvq-3jxh-4qg7
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/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
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
GitHub AdvisoriesGHSA-xpx6-x8c2-mw5w 5.5

PraisonAI: Project custom command templates can read outside-workspace files into model prompts

Project custom command templates can read outside-workspace files into model prompts

Summary

PraisonAI's new file-based custom command feature auto-discovers project commands from .praisonai/commands/*.md. When a user runs praisonai run --command <name> inside a repository, the command body is interpolated before it is sent as the model prompt.

The interpolation code expands @path references by reading files relative to the current working directory, but it does not canonicalize the target or require it to stay inside the project. A repository-controlled command can therefore include @../outside_secret.txt or an absolute path and cause PraisonAI to copy process-readable files outside the workspace into the prompt.

This is a confidentiality issue in the untrusted-repository workflow: a project can make a normal-looking custom command exfiltrate local files to whichever model/provider receives the generated prompt.

Technical Details

The feature was introduced by commit 88cf0c29 (feat: file-based custom agents and reusable commands with auto-discovery (#2035)) and is present on current main:

current commit: 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab
current describe: v4.6.64-8-g3aa9cbc2

src/praisonai/praisonai/cli/features/custom_definitions.py discovers project-level definitions by walking upward from Path.cwd() to the git root and loading .praisonai/commands/*.md. Project commands override user-global commands.

interpolate_command_template() loads the selected command and passes the command body to the interpolator with Path.cwd() as the working directory:

return interpolator.interpolate(command.template, arguments, Path.cwd())

TemplateInterpolator._interpolate_files() then matches every @([^\s]+) token and reads the referenced file:

if working_dir:
    file_path = working_dir / file_path_str
else:
    file_path = Path(file_path_str)

if file_path.exists() and file_path.is_file():
    with open(file_path, 'r') as f:
        return f.read()

There is no resolve() call and no containment check against the project root. In Python, Path.cwd() / "/absolute/path" returns the absolute path, and parent traversal such as ../outside_secret.txt resolves outside the workspace when opened.

The sink is in src/praisonai/praisonai/cli/commands/run.py: the --command path calls interpolate_command_template(), then passes the fully interpolated prompt to _run_prompt().

PoV

A minimal vulnerable repository only needs a project command template and an outside file:

workspace/
  .git/
  .praisonai/
    commands/
      relative_escape.md   # contains @../outside_secret.txt
      absolute_escape.md   # contains an absolute path outside workspace
  inside.txt
outside_secret.txt

When the operator runs the project command, PraisonAI discovers .praisonai/commands/*.md, interpolates the template with Path.cwd() as the working directory, reads the outside file, and passes the resulting prompt to _run_prompt().

The controls in the PoC below show the expected asymmetry: an in-workspace file expands, a missing file remains literal, shell substitution is escaped, and both parent traversal and absolute outside-file references disclose the outside canary.

PoC

From a fresh PraisonAI checkout, run the following command. The checkout path is passed as the first Python argument, and the script sets up the source import path itself; no hidden PYTHONPATH setup is required.

git clone https://github.com/MervinPraison/PraisonAI.git
cd PraisonAI
git checkout 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab

python3 - "$PWD" <<'PY'
from __future__ import annotations

import importlib.util
import json
import os
import subprocess
import sys
import tempfile
import types
from pathlib import Path


CANARY = "PRAISONAI_CUSTOM_COMMAND_CANARY=outside-workspace"


def install_yaml_fallback_if_needed() -> str:
    if importlib.util.find_spec("yaml") is…
Record
GHSA-xpx6-x8c2-mw5w
CVSS 3.1
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-22, CWE-200
Updated
2026-11-02
NVD 6.8

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
CVE-2026-60088
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/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
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 6.8

PraisonAI before 4.6.78 Path Traversal via Custom Commands

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
CVE-2026-60088
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-wgvq-3jxh-4qg7 6.8

Duplicate Advisory: PraisonAI: Project custom command templates can read outside-workspace files into model prompts

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-xpx6-x8c2-mw5w. This link is maintained to preserve external references.

Original Description

PraisonAI before 4.6.78 fails to validate file path references in custom command templates, allowing attackers to read files outside the workspace. Attackers can include path traversal sequences like @../outside_secret.txt or absolute paths in project command files to exfiltrate process-readable files into model prompts.

Record
GHSA-wgvq-3jxh-4qg7
CVSS 4.0
6.8 MediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/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
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-xpx6-x8c2-mw5w 5.5

PraisonAI: Project custom command templates can read outside-workspace files into model prompts

Project custom command templates can read outside-workspace files into model prompts

Summary

PraisonAI's new file-based custom command feature auto-discovers project commands from .praisonai/commands/*.md. When a user runs praisonai run --command <name> inside a repository, the command body is interpolated before it is sent as the model prompt.

The interpolation code expands @path references by reading files relative to the current working directory, but it does not canonicalize the target or require it to stay inside the project. A repository-controlled command can therefore include @../outside_secret.txt or an absolute path and cause PraisonAI to copy process-readable files outside the workspace into the prompt.

This is a confidentiality issue in the untrusted-repository workflow: a project can make a normal-looking custom command exfiltrate local files to whichever model/provider receives the generated prompt.

Technical Details

The feature was introduced by commit 88cf0c29 (feat: file-based custom agents and reusable commands with auto-discovery (#2035)) and is present on current main:

current commit: 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab
current describe: v4.6.64-8-g3aa9cbc2

src/praisonai/praisonai/cli/features/custom_definitions.py discovers project-level definitions by walking upward from Path.cwd() to the git root and loading .praisonai/commands/*.md. Project commands override user-global commands.

interpolate_command_template() loads the selected command and passes the command body to the interpolator with Path.cwd() as the working directory:

return interpolator.interpolate(command.template, arguments, Path.cwd())

TemplateInterpolator._interpolate_files() then matches every @([^\s]+) token and reads the referenced file:

if working_dir:
    file_path = working_dir / file_path_str
else:
    file_path = Path(file_path_str)

if file_path.exists() and file_path.is_file():
    with open(file_path, 'r') as f:
        return f.read()

There is no resolve() call and no containment check against the project root. In Python, Path.cwd() / "/absolute/path" returns the absolute path, and parent traversal such as ../outside_secret.txt resolves outside the workspace when opened.

The sink is in src/praisonai/praisonai/cli/commands/run.py: the --command path calls interpolate_command_template(), then passes the fully interpolated prompt to _run_prompt().

PoV

A minimal vulnerable repository only needs a project command template and an outside file:

workspace/
  .git/
  .praisonai/
    commands/
      relative_escape.md   # contains @../outside_secret.txt
      absolute_escape.md   # contains an absolute path outside workspace
  inside.txt
outside_secret.txt

When the operator runs the project command, PraisonAI discovers .praisonai/commands/*.md, interpolates the template with Path.cwd() as the working directory, reads the outside file, and passes the resulting prompt to _run_prompt().

The controls in the PoC below show the expected asymmetry: an in-workspace file expands, a missing file remains literal, shell substitution is escaped, and both parent traversal and absolute outside-file references disclose the outside canary.

PoC

From a fresh PraisonAI checkout, run the following command. The checkout path is passed as the first Python argument, and the script sets up the source import path itself; no hidden PYTHONPATH setup is required.

git clone https://github.com/MervinPraison/PraisonAI.git
cd PraisonAI
git checkout 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab

python3 - "$PWD" <<'PY'
from __future__ import annotations

import importlib.util
import json
import os
import subprocess
import sys
import tempfile
import types
from pathlib import Path


CANARY = "PRAISONAI_CUSTOM_COMMAND_CANARY=outside-workspace"


def install_yaml_fallback_if_needed() -> str:
    if importlib.util.find_spec("yaml") is…
Record
GHSA-xpx6-x8c2-mw5w
CVSS 3.1
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Weakness
CWE-200, CWE-22
Updated
2026-11-02
OSVPYSEC-2026-4261 5.5

PraisonAI: Project custom command templates can read outside-workspace files into model prompts

Project custom command templates can read outside-workspace files into model prompts

Summary

PraisonAI's new file-based custom command feature auto-discovers project commands from .praisonai/commands/*.md. When a user runs praisonai run --command <name> inside a repository, the command body is interpolated before it is sent as the model prompt.

The interpolation code expands @path references by reading files relative to the current working directory, but it does not canonicalize the target or require it to stay inside the project. A repository-controlled command can therefore include @../outside_secret.txt or an absolute path and cause PraisonAI to copy process-readable files outside the workspace into the prompt.

This is a confidentiality issue in the untrusted-repository workflow: a project can make a normal-looking custom command exfiltrate local files to whichever model/provider receives the generated prompt.

Technical Details

The feature was introduced by commit 88cf0c29 (feat: file-based custom agents and reusable commands with auto-discovery (#2035)) and is present on current main:

current commit: 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab
current describe: v4.6.64-8-g3aa9cbc2

src/praisonai/praisonai/cli/features/custom_definitions.py discovers project-level definitions by walking upward from Path.cwd() to the git root and loading .praisonai/commands/*.md. Project commands override user-global commands.

interpolate_command_template() loads the selected command and passes the command body to the interpolator with Path.cwd() as the working directory:

return interpolator.interpolate(command.template, arguments, Path.cwd())

TemplateInterpolator._interpolate_files() then matches every @([^\s]+) token and reads the referenced file:

if working_dir:
    file_path = working_dir / file_path_str
else:
    file_path = Path(file_path_str)

if file_path.exists() and file_path.is_file():
    with open(file_path, 'r') as f:
        return f.read()

There is no resolve() call and no containment check against the project root. In Python, Path.cwd() / "/absolute/path" returns the absolute path, and parent traversal such as ../outside_secret.txt resolves outside the workspace when opened.

The sink is in src/praisonai/praisonai/cli/commands/run.py: the --command path calls interpolate_command_template(), then passes the fully interpolated prompt to _run_prompt().

PoV

A minimal vulnerable repository only needs a project command template and an outside file:

workspace/
  .git/
  .praisonai/
    commands/
      relative_escape.md   # contains @../outside_secret.txt
      absolute_escape.md   # contains an absolute path outside workspace
  inside.txt
outside_secret.txt

When the operator runs the project command, PraisonAI discovers .praisonai/commands/*.md, interpolates the template with Path.cwd() as the working directory, reads the outside file, and passes the resulting prompt to _run_prompt().

The controls in the PoC below show the expected asymmetry: an in-workspace file expands, a missing file remains literal, shell substitution is escaped, and both parent traversal and absolute outside-file references disclose the outside canary.

PoC

From a fresh PraisonAI checkout, run the following command. The checkout path is passed as the first Python argument, and the script sets up the source import path itself; no hidden PYTHONPATH setup is required.

git clone https://github.com/MervinPraison/PraisonAI.git
cd PraisonAI
git checkout 3aa9cbc2bd49c23a32be0a89a5e620d13d843eab

python3 - "$PWD" <<'PY'
from __future__ import annotations

import importlib.util
import json
import os
import subprocess
import sys
import tempfile
import types
from pathlib import Path


CANARY = "PRAISONAI_CUSTOM_COMMAND_CANARY=outside-workspace"


def install_yaml_fallback_if_needed() -> str:
    if importlib.util.find_spec("yaml") is…
Record
PYSEC-2026-4261
CVSS 3.1
5.5 MediumCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonai
PyPI
through 4.6.77
4.6.78
praisonai
PyPI
before 4.6.78, plus listed versions
0.0.10.0.100.0.110.0.120.0.13
Show 45 moreShow less
0.0.140.0.150.0.160.0.170.0.180.0.190.0.20.0.200.0.210.0.220.0.230.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.320.0.330.0.340.0.350.0.360.0.370.0.380.0.390.0.40.0.400.0.410.0.420.0.430.0.440.0.450.0.460.0.470.0.480.0.490.0.50.0.500.0.520.0.530.0.540.0.55
4.6.78

History

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

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

In short

CVE-2026-60088 is a medium vulnerability (CVSS 6.8) in Mervinpraison Praisonai, published July 11, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.22%, the 12th percentile. Fixed in praisonai 4.6.78.

References

This page doesn't list everything

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

MoveEnter Open