Skip to content

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.

REPRO-2026-00149 praisonai · pip Path Traversal May 22, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
7.5
Reproduced in
17m 42s
Tool calls
158
Spend
$1.56
01 · Overview

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).

02 · Severity & CVSS

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 threat level
7.5 / 10 CVSS base
03 · Affected Versions

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
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00149/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

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 561 events · 158 tool calls · 18 min
18 minDuration
158Tool calls
133Reasoning steps
561Events
Agent activity over 18 min
Support
18
Repro
208
Variant
331
0:0017:42

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:

  1. member.name is not absolute
  2. member.name does not contain .. components
  3. The resolved path of member.name stays within dest_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 linkname targets
  • Resolves the relative linkname against dest_dir and 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:

  1. Creates two isolated virtualenvs and installs praisonai==4.6.36 (vulnerable) and praisonai==4.6.37 (fixed).
  2. Builds a malicious .praison bundle (a gzipped tar) containing:
    • A valid manifest.json
    • A symlink member named escape with linkname = ../../outside
  3. Sets up a minimal LocalRegistry filesystem structure pointing at the malicious bundle.
  4. Invokes the user-facing LocalRegistry.pull("testrecipe", "1.0.0", output_dir=..., verify_checksum=False) API on both versions.
  5. 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 symlink escape is created inside the output directory, resolving to a path outside it (../../outside).
  • Fixed (4.6.37): pull() raises RegistryError: Refusing to extract link escaping target directory: escape -> ../../outside. No symlink is extracted.

Evidence

  • Vulnerable run log: logs/vulnerable.log

    • RESULT: no_exception
    • PATH: .../repro/output/vuln/testrecipe
    • Filesystem: symlink escape -> ../../outside exists and resolves outside the destination directory.
  • Fixed run log: logs/fixed.log

    • RESULT: registry_error
    • ERROR: 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.

Recommendations / Next Steps

  1. Upgrade immediately to praisonai >= 4.6.37.
  2. Validate archive extraction logic across the entire codebase. Any custom extractall wrapper should validate both member names and link targets.
  3. Add regression tests for symlink and hardlink traversal, as the upstream project did in test_safe_extractall_symlink.py.
  4. Use tarfile.extractall(filter="data") on Python 3.12+ to benefit from built-in data-only extraction filters.
  5. Consider disabling symlink/hardlink extraction entirely in recipe bundles if they are not required for functionality.

Additional Notes

  • Idempotency: repro/reproduction_steps.sh was 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 on tarfile.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.

Event 1/40
0:000:41
0:00
session startedaccounts/fireworks/models/kimi-k2p6 · cve-2026-44340 · cve-2026
0:02
0:03
0:05
web search
0:09
0:10
0:11
web search
0:27
0:27
extract_facts
no facts extracted
0:28
0:28
0:28
supportrepro
0:30
0:31
0:31
0:31
0:31
0:31
0:35
0:40
$git clone --depth=100 https://github.com/MervinPraison/PraisonAI.git /tmp/PraisonAI
4.4s
Cloning into '/tmp/PraisonAI'...
0:41

Artifacts and Evidence for CVE-2026-44340

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix CVE-2026-44340

Upgrade praisonai · pip to 4.6.37 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-44340 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-44340

How does the CVE-2026-44340 ZipSlip attack work?

An attacker crafts a .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?

Versions before 4.6.37 are affected (earliest confirmed affected at least 2.7.2, specifically confirmed on < 4.6.36); it is fixed in 4.6.37.

How severe is CVE-2026-44340?

It is rated high severity (CVSS 8.7): exploitation can lead to arbitrary file write or overwrite outside the intended extraction directory on subsequent operations.

How can I reproduce CVE-2026-44340?

Download the verified script from this page and run it in an isolated environment against PraisonAI before 4.6.37. It builds a malicious .praison tar archive containing a symlink member whose linkname escapes the destination directory plus a write entry that traverses it, then shows a file written outside the destination, and confirms 4.6.37 blocks it.
11 · References

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.