CVE-2026-44340: Verified Repro With Script Download
CVE-2026-44340: PraisonAI: ZipSlip path traversal via unchecked tar symlink linkname in safe extractall
CVE-2026-44340 is verified against praisonai · pip. Affected versions: < 4.6.37 (specifically < 4.6.36; earliest affected at least 2.7.2). Fixed in 4.6.37. Vulnerability class: Path Traversal. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00149.
What Is CVE-2026-44340?
CVE-2026-44340 is a high-severity ZipSlip path traversal vulnerability (CWE-22) in PraisonAI's recipe-archive extraction helper, _safe_extractall. Pruva reproduced it (reproduction REPRO-2026-00149).
CVE-2026-44340 Severity & CVSS Score
CVE-2026-44340 is rated high severity, with a CVSS base score of 7.5 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected praisonai Versions
praisonai · pip versions < 4.6.37 (specifically < 4.6.36; earliest affected at least 2.7.2) are affected.
How to Reproduce CVE-2026-44340
pruva-verify REPRO-2026-00149 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00149/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-44340
Reproduced by Pruva's autonomous agents — 158 tool calls over 18 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-44340
Summary
PraisonAI's _safe_extractall helper function in src/praisonai/praisonai/recipe/registry.py validates tar archive member names to prevent path traversal, but it does not validate member.linkname for symlink or hardlink members. This allows a malicious .praison recipe bundle containing a symlink with a relative linkname that escapes the destination directory (e.g., ../../outside) to be extracted unchecked. The extracted symlink points outside the intended extraction directory, enabling a ZipSlip-style path traversal attack.
Impact
- Package:
praisonai(PyPI) - Repository: https://github.com/MervinPraison/PraisonAI
- Affected versions:
< 4.6.37 - Fixed version:
4.6.37 - CWE: CWE-22 (Path Traversal)
- Severity: High — CVSS 8.7
- Consequences: Any code path that downloads, publishes, or unpacks an attacker-supplied recipe archive (via
LocalRegistry.pull(),HttpRegistry.pull(), etc.) can be exploited to create symlinks outside the intended destination directory, potentially leading to arbitrary file write or overwrite on subsequent operations.
Root Cause
The _safe_extractall function iterates over all tar members and checks:
member.nameis not absolutemember.namedoes not contain..components- The resolved path of
member.namestays withindest_dir
However, it never inspects member.linkname. When a tar member is a symlink (tarfile.SYMTYPE) or hardlink (tarfile.LNKTYPE), tarfile.TarFile.extractall() creates the link on disk using member.linkname as the target. If that target points outside dest_dir, the link escapes the sandbox.
Fix commit: 0cec9fd — "refactor: harden archive extraction and tool resolution boundary"
The fix adds validation for symlink and hardlink members:
- Rejects absolute
linknametargets - Resolves the relative
linknameagainstdest_dirand rejects targets that escape it - Also switches to
tar.extractall(dest_dir, filter="data")on Python 3.12+ for additional hardening
Reproduction Steps
The reproduction script is repro/reproduction_steps.sh. It performs the following:
- Creates two isolated virtualenvs and installs
praisonai==4.6.36(vulnerable) andpraisonai==4.6.37(fixed). - Builds a malicious
.praisonbundle (a gzipped tar) containing:- A valid
manifest.json - A symlink member named
escapewithlinkname = ../../outside
- A valid
- Sets up a minimal
LocalRegistryfilesystem structure pointing at the malicious bundle. - Invokes the user-facing
LocalRegistry.pull("testrecipe", "1.0.0", output_dir=..., verify_checksum=False)API on both versions. - Captures whether the symlink was extracted and whether it resolves outside the output directory.
Expected evidence:
- Vulnerable (4.6.36):
pull()succeeds without error. A symlinkescapeis created inside the output directory, resolving to a path outside it (../../outside). - Fixed (4.6.37):
pull()raisesRegistryError: Refusing to extract link escaping target directory: escape -> ../../outside. No symlink is extracted.
Evidence
Vulnerable run log:
logs/vulnerable.logRESULT: no_exceptionPATH: .../repro/output/vuln/testrecipe- Filesystem: symlink
escape -> ../../outsideexists and resolves outside the destination directory.
Fixed run log:
logs/fixed.logRESULT: registry_errorERROR: Refusing to extract link escaping target directory: escape -> ../../outside- Filesystem: no symlink created.
Runtime manifest:
repro/runtime_manifest.json- Contains
verdict: confirmed, structured pass/fail booleans, and captured logs for both versions.
- Contains
Recommendations / Next Steps
- Upgrade immediately to
praisonai >= 4.6.37. - Validate archive extraction logic across the entire codebase. Any custom
extractallwrapper should validate both member names and link targets. - Add regression tests for symlink and hardlink traversal, as the upstream project did in
test_safe_extractall_symlink.py. - Use
tarfile.extractall(filter="data")on Python 3.12+ to benefit from built-in data-only extraction filters. - Consider disabling symlink/hardlink extraction entirely in recipe bundles if they are not required for functionality.
Additional Notes
- Idempotency:
repro/reproduction_steps.shwas run twice consecutively and produced identical results both times. - Limitations: The reproduction demonstrates symlink extraction outside
dest_dir. The actual write-through follow-up attack (a regular file member whose path traverses the created symlink) depends ontarfile.extractall()behavior and Python version; the core vulnerability is the unchecked symlink extraction itself, which is what the upstream fix addresses. - Environment: Python 3.11, Ubuntu-based container.
CVE-2026-44340 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
git clone --depth=100 https://github.com/MervinPraison/PraisonAI.git /tmp/PraisonAICloning into '/tmp/PraisonAI'...
Artifacts and Evidence for CVE-2026-44340
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-44340
Upgrade praisonai · pip to 4.6.37 or later.
FAQ: CVE-2026-44340
How does the CVE-2026-44340 ZipSlip attack work?
.praison recipe bundle containing a symlink member with a linkname that traverses outside the destination directory. That symlink is extracted unchecked because only member.name is validated; a subsequent write entry in the same archive then traverses through the just-created symlink and writes its contents outside the intended destination directory. Any code path that pulls, publishes, or unpacks an attacker-supplied recipe archive (LocalRegistry.pull(), HttpRegistry.pull(), etc.) is exploitable.Which PraisonAI versions are affected by CVE-2026-44340, and where is it fixed?
How severe is CVE-2026-44340?
How can I reproduce CVE-2026-44340?
References for CVE-2026-44340
Authoritative sources for CVE-2026-44340 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.