Apr 7, 2026·6 min read·1 visit
A path traversal vulnerability in PraisonAI's recipe registry allows attackers to achieve arbitrary file write by uploading crafted manifests, exploiting an insecure order of operations where files are written before validation occurs.
PraisonAI versions prior to 4.5.113 contain an improper path limitation vulnerability in the recipe registry publish endpoint. An attacker can craft a malicious recipe bundle with directory traversal sequences in its internal manifest to write files outside the designated registry root directory. This critical path traversal flaw results in arbitrary file write capabilities on the hosting server, bypassing deferred request validation.
PraisonAI operates as a multi-agent teams system, utilizing a recipe registry to manage and distribute configurations. The registry server exposes an HTTP API endpoint to allow publishers to upload new recipe bundles. This endpoint, located at /v1/recipes/{name}/{version}, processes incoming .praison archive files and extracts their internal configuration metadata.
A security flaw exists in the processing logic of this publish endpoint, categorized as CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). The vulnerability occurs because the application extracts and relies on the internal manifest.json file to dictate the storage location on the local filesystem. Attackers can embed directory traversal sequences within the manifest fields to dictate arbitrary destination directories.
Because the registry process runs with specific filesystem privileges, this traversal allows attackers to write arbitrary files to unauthorized locations on the host system. The application eventually detects a mismatch between the HTTP route parameters and the manifest content, rejecting the request with an HTTP 400 error. However, the file write operation has already executed by the time this validation occurs, rendering the error response ineffective at preventing the filesystem manipulation.
The core issue stems from an insecure order of operations combined with insufficient input validation within the server logic. The HTTP request handler RegistryServer._handle_publish() in src/praisonai/praisonai/recipe/server.py accepts the uploaded bundle and writes it to a temporary path. It then immediately invokes LocalRegistry.publish() to process the bundle before performing parameter validation.
Within src/praisonai/praisonai/recipe/registry.py, the LocalRegistry.publish() method opens the provided tarball and extracts the manifest.json file. It extracts the name and version fields directly from the parsed JSON object. The system subsequently utilizes the standard pathlib library to concatenate the base recipe path with these untrusted string values.
Python's pathlib library resolves ../ sequences natively during path concatenation. When an attacker provides a name value containing path traversal characters, the computed recipe_dir escapes the intended recipes_path boundary. The application creates the target directory using recipe_dir.mkdir(parents=True, exist_ok=True) and copies the extracted archive into this unconstrained location.
After completing these filesystem operations, the server code finally compares the manifest.json values against the URL path parameters provided in the initial HTTP request. This deferred validation creates a critical Time-of-Check to Time-of-Use (TOCTOU) logical flaw. The registry server issues an HTTP 400 Bad Request response, but the out-of-bounds directory creation and file write have permanently altered the filesystem state.
Examining the vulnerable implementation reveals the direct use of unsanitized dictionary values in path construction. The vulnerable block in LocalRegistry.publish() retrieves the name and version from the manifest and builds the filesystem path without enforcing security boundaries. The standard shutil.copy2() function then executes the out-of-bounds write operation.
# Vulnerable implementation in LocalRegistry.publish()
name = manifest.get("name")
version = manifest.get("version")
recipe_dir = self.recipes_path / name / version
recipe_dir.mkdir(parents=True, exist_ok=True)
bundle_name = f"{name}-{version}.praison"
dest_path = recipe_dir / bundle_name
shutil.copy2(bundle_path, dest_path)The patch introduced in commit 0cb35a4fe3cc8549074d0efe228c947cc35a8dfd implements comprehensive defense-in-depth mechanisms. The developers introduced strict regular expression validation to restrict allowable characters in the name and version fields. Furthermore, they implemented a robust path confinement check utilizing pathlib.Path.resolve() to ensure the final absolute path remains within the designated registry root.
# Patched implementation snippet (0cb35a4fe3cc8549074d0efe228c947cc35a8dfd)
recipe_dir = self.recipes_path / name / version
recipes_root = self.recipes_path.resolve()
if not str(recipe_dir.resolve()).startswith(str(recipes_root) + os.sep):
raise RegistryError(f"Path traversal detected in manifest name/version: {name}@{version}")Alongside the path confinement logic, the patch replaces the standard tar.extractall() method with a custom _safe_extractall() helper function. This secondary fix prevents Tar Slip attacks where the .praison archive itself contains malicious absolute paths or traversal sequences within its internal file structures.
Exploiting this vulnerability requires network access to the PraisonAI recipe registry publish endpoint. An attacker must formulate a valid .praison archive, which functions technically as a tarball containing at least a manifest.json file. The attacker modifies the manifest.json to include a malicious name attribute containing relative path traversal indicators.
The attacker submits the crafted archive via a standard HTTP POST request to a route such as /v1/recipes/safe/1.0.0. The server receives the payload and writes it to a temporary directory. The internal processing logic extracts the manipulated manifest.json and constructs the target filesystem path based on the poisoned values.
By defining a manifest name like ../../outside-dir, the attacker forces the application to evaluate the destination as [registry_root]/../../outside-dir/1.0.0/. The application automatically creates these directories and writes the bundle to /tmp/praisonai-publish-traversal-poc/outside-dir-1.0.0.praison. This provides the attacker precise control over the output destination.
The server response to this payload is an HTTP 400 Bad Request, explicitly stating that the bundle name does not match the URL parameters. Despite this error response, the filesystem manipulation succeeds. The attack sequence acts as a blind write, requiring the attacker to either overwrite known system files or place executable content in web-accessible directories to escalate privileges.
The primary impact of this vulnerability is arbitrary file write access on the system hosting the PraisonAI recipe registry. The severity is constrained only by the filesystem permissions assigned to the PraisonAI application process. Write access enables an attacker to overwrite configuration files, inject malicious code into existing Python modules, or establish persistence mechanisms.
The CVSS v3.1 base score of 7.1 accurately reflects the requirement for low-privileged access and the high impact on system integrity. The vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L indicates that exploitation is network-based and requires minimal complexity. The confidentiality impact is rated as none, as the vulnerability does not natively provide read access to the filesystem.
While arbitrary file read is not directly exposed, the file write capability often serves as a stepping stone to full remote code execution. If the application service runs as a privileged user, the attacker can overwrite sensitive system binaries or SSH authorized_keys files. The inclusion of the secondary tar slip vector further amplified the risk before the comprehensive patch was applied.
The official remediation requires upgrading the praisonai package to version 4.5.113 or newer. This update includes the comprehensive patch addressing the insecure order of operations, the path concatenation flaw, and the associated tar slip vulnerability. Administrators should verify the installed package version using standard Python package management tools.
For deployments where immediate patching is unfeasible, administrators must restrict network access to the registry publish endpoint. Implementing network-level filtering or a reverse proxy to block unauthenticated POST requests to /v1/recipes/* effectively mitigates the remote exploitation vector. This temporary workaround prevents external threat actors from reaching the vulnerable code path.
Employing the principle of least privilege limits the potential impact of a successful exploitation attempt. The PraisonAI service should execute under a dedicated, unprivileged user account. The filesystem permissions must restrict write access exclusively to the designated registry storage directory, preventing out-of-bounds writes from affecting the broader operating system.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
PraisonAI MervinPraison | < 4.5.113 | 4.5.113 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS Base Score | 7.1 |
| Impact | Arbitrary File Write |
| Exploit Maturity | Proof-of-Concept |
| CISA KEV Listed | False |
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.