VYPR
Medium severity6.5NVD Advisory· Published Apr 2, 2026· Updated Apr 13, 2026

CVE-2026-34591

CVE-2026-34591

Description

Poetry is a dependency manager for Python. From version 1.4.0 to before version 2.3.3, 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. It is reachable from untrusted package artifacts during normal install flows. (Normally, installing a malicious wheel is not sufficient for execution of malicious code. Malicious code will only be executed after installation if the malicious package is imported or invoked by the user.). This issue has been patched in version 2.3.3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
poetryPyPI
>= 1.4.0, < 2.3.32.3.3

Affected products

1

Patches

1
ed59537ac370

installer: fix path traversal (#10792)

https://github.com/python-poetry/poetryRandy DöringMar 29, 2026via ghsa
2 files changed · +56 1
  • src/poetry/installation/wheel_installer.py+8 1 modified
    @@ -44,7 +44,14 @@ def write_to_fs(
             from installer.utils import copyfileobj_with_hashing
             from installer.utils import make_file_executable
     
    -        target_path = Path(self.scheme_dict[scheme]) / path
    +        target_dir = Path(self.scheme_dict[scheme]).resolve()
    +        target_path = (target_dir / path).resolve()
    +
    +        if not target_path.is_relative_to(target_dir):
    +            raise ValueError(
    +                f"Attempting to write {path} outside of the target directory"
    +            )
    +
             if target_path.exists():
                 # Contrary to the base library we don't raise an error here since it can
                 # break pkgutil-style and pkg_resource-style namespace packages.
    
  • tests/installation/test_wheel_installer.py+48 0 modified
    @@ -81,3 +81,51 @@ def test_enable_bytecode_compilation(
             assert not list(cache_dir.glob("*.opt-2.pyc"))
         else:
             assert not cache_dir.exists()
    +
    +
    +def test_install_dir_is_symlink(tmp_path: Path, demo_wheel: Path) -> None:
    +    target_dir = tmp_path / "target"
    +    target_dir.mkdir()
    +    symlink_dir = tmp_path / "symlink"
    +    symlink_dir.symlink_to(target_dir, target_is_directory=True)
    +
    +    env = MockEnv(path=symlink_dir)
    +
    +    installer = WheelInstaller(env)
    +    installer.install(demo_wheel)
    +
    +    assert (Path(env.paths["purelib"]) / "demo").exists()
    +
    +
    +@pytest.fixture
    +def wheel_with_path_traversal(tmp_path: Path) -> Path:
    +    import zipfile
    +
    +    wheel = tmp_path / "traversal-0.1-py3-none-any.whl"
    +    files = {
    +        "traversal/__init__.py": b"",
    +        "../../traversal.txt": b"",
    +        "traversal-0.1.dist-info/WHEEL": (
    +            b"Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n"
    +        ),
    +        "traversal-0.1.dist-info/METADATA": (
    +            b"Metadata-Version: 2.1\nName: traversal\nVersion: 0.1\n"
    +        ),
    +    }
    +    files["traversal-0.1.dist-info/RECORD"] = (
    +        "\n".join([f"{k},," for k in files] + ["traversal-0.1.dist-info/RECORD,,"])
    +        + "\n"
    +    ).encode()
    +
    +    with zipfile.ZipFile(wheel, "w") as z:
    +        for k, v in files.items():
    +            z.writestr(k, v)
    +
    +    return wheel
    +
    +
    +def test_path_traversal(env: MockEnv, wheel_with_path_traversal: Path) -> None:
    +    installer = WheelInstaller(env)
    +    with pytest.raises(ValueError):
    +        installer.install(wheel_with_path_traversal)
    +    assert not (env.path.parent / "traversal.txt").exists()
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

7