CVE-2026-60088: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') in MervinPraison PraisonAI
Description
# 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: ```text 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: ```python return interpolator.interpolate(command.template, arguments, Path.cwd()) ``` `TemplateInterpolator._interpolate_files()` then matches every `@([^\s]+)` token and reads the referenced file: ```python 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: ```text 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. ```bash 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 not None: return "installed" yaml_stub = types.ModuleType("yaml") class YAMLError(Exception): pass def safe_load(text: str): data = {} for raw_line in text.splitlines(): line = raw_line.strip() if not line or line.startswith("#") or ":" not in line: continue key, value = line.split(":", 1) data[key.strip()] = value.strip().strip("'\"") return data yaml_stub.safe_load = safe_load yaml_stub.YAMLError = YAMLError sys.modules["yaml"] = yaml_stub return "stubbed" def add_source_to_path(source_root: Path) -> None: candidate = source_root / "src" / "praisonai" if (candidate / "praisonai").exists(): sys.path.insert(0, str(candidate)) return raise SystemExit(f"Could not find PraisonAI sources below {source_root}") class pushd: def __init__(self, path: Path): self.path = path self.old = Path.cwd() def __ent
CVSS v4.0
Score 6.8medium
Affected software
MervinPraison
PraisonAI
pkg:github/mervinpraison/PraisonAIRun on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
CVE-2026-60088 is a path traversal vulnerability in MervinPraison's PraisonAI product before version 4.6.78. The software fails to properly validate file path inputs in custom command templates, enabling attackers to include path traversal sequences (e.g., '@../outside_secret.txt') or absolute paths. This flaw allows attackers to exfiltrate files readable by the process from outside the workspace into model prompts. The vulnerability requires local access (AV:L) and user interaction (UI:P), with no privileges required (PR:N).
Potential Impact
Successful exploitation allows an attacker to read arbitrary files accessible by the PraisonAI process outside the intended workspace directory. This can lead to unauthorized disclosure of sensitive information contained in those files. There is no indication of privilege escalation, code execution, or denial of service from the provided data.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Since no official fix or patch link is provided, users should monitor MervinPraison's advisories for updates. Until a fix is available, avoid using untrusted custom command templates that include file path references and restrict access to the PraisonAI environment to trusted users only.
Technical Details
- Data Version
- 5.2
- Assigner Short Name
- VulnCheck
- Date Reserved
- 2026-07-08T12:14:28.344Z
- Cvss Version
- 4.0
- State
- PUBLISHED
Threat ID: 6a52461668715ace43df4a80
Added to database: 07/11/2026, 13:33:10 UTC
Last enriched: 07/18/2026, 15:32:58 UTC
Last updated: 10/08/2026, 21:48:37 UTC
Views: 115
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.