Your message dated Sat, 11 Jul 2026 10:32:48 +0000
with message-id <[email protected]>
and subject line Released in 13.6
has caused the Debian Bug report #1141377,
regarding trixie-pu: package poetry/2.1.2+dfsg-1+deb13u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1141377: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1141377
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:poetry
User: [email protected]
Usertags: pu

  * CVE-2026-34591: Wheel Path Traversal Leading to Arbitrary File Write
    (Closes: #1132609)
diffstat for poetry-2.1.2+dfsg poetry-2.1.2+dfsg

 changelog                                                               |    8 
 patches/0001-installer-fix-path-traversal-10792.patch                   |   90 
++++
 patches/0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch |  185 
++++++++++
 patches/series                                                          |    2 
 4 files changed, 285 insertions(+)

diff -Nru poetry-2.1.2+dfsg/debian/changelog poetry-2.1.2+dfsg/debian/changelog
--- poetry-2.1.2+dfsg/debian/changelog  2025-04-04 02:49:58.000000000 +0300
+++ poetry-2.1.2+dfsg/debian/changelog  2026-07-03 18:25:04.000000000 +0300
@@ -1,3 +1,11 @@
+poetry (2.1.2+dfsg-1+deb13u1) trixie; urgency=medium
+
+  * Non-maintainer upload.
+  * CVE-2026-34591: Wheel Path Traversal Leading to Arbitrary File Write
+    (Closes: #1132609)
+
+ -- Adrian Bunk <[email protected]>  Fri, 03 Jul 2026 18:25:04 +0300
+
 poetry (2.1.2+dfsg-1) unstable; urgency=medium
 
   * New upstream version.
diff -Nru 
poetry-2.1.2+dfsg/debian/patches/0001-installer-fix-path-traversal-10792.patch 
poetry-2.1.2+dfsg/debian/patches/0001-installer-fix-path-traversal-10792.patch
--- 
poetry-2.1.2+dfsg/debian/patches/0001-installer-fix-path-traversal-10792.patch  
    1970-01-01 02:00:00.000000000 +0200
+++ 
poetry-2.1.2+dfsg/debian/patches/0001-installer-fix-path-traversal-10792.patch  
    2026-07-03 18:24:45.000000000 +0300
@@ -0,0 +1,90 @@
+From 6895943e9e2f9ce563ea51b984830fb87f4d7371 Mon Sep 17 00:00:00 2001
+From: Randy Döring <[email protected]>
+Date: Sun, 29 Mar 2026 10:24:17 +0200
+Subject: installer: fix path traversal (#10792)
+
+(cherry picked from commit ed59537ac3709cfbdbf95d957de801c13872991a)
+---
+ src/poetry/installation/wheel_installer.py |  9 +++-
+ tests/installation/test_wheel_installer.py | 48 ++++++++++++++++++++++
+ 2 files changed, 56 insertions(+), 1 deletion(-)
+
+diff --git a/src/poetry/installation/wheel_installer.py 
b/src/poetry/installation/wheel_installer.py
+index 57ee02d9..a086ca73 100644
+--- a/src/poetry/installation/wheel_installer.py
++++ b/src/poetry/installation/wheel_installer.py
+@@ -44,7 +44,14 @@ class WheelDestination(SchemeDictionaryDestination):
+         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.
+diff --git a/tests/installation/test_wheel_installer.py 
b/tests/installation/test_wheel_installer.py
+index 98e3f4cf..33e95dd9 100644
+--- a/tests/installation/test_wheel_installer.py
++++ b/tests/installation/test_wheel_installer.py
+@@ -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()
++
++
[email protected]
++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()
+-- 
+2.47.3
+
diff -Nru 
poetry-2.1.2+dfsg/debian/patches/0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch
 
poetry-2.1.2+dfsg/debian/patches/0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch
--- 
poetry-2.1.2+dfsg/debian/patches/0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch
    1970-01-01 02:00:00.000000000 +0200
+++ 
poetry-2.1.2+dfsg/debian/patches/0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch
    2026-07-03 18:24:45.000000000 +0300
@@ -0,0 +1,185 @@
+From bd6f54aec6e4f07dcf77ad9e8acee474e55cb06a Mon Sep 17 00:00:00 2001
+From: Randy Döring <[email protected]>
+Date: Sun, 12 Apr 2026 14:21:51 +0200
+Subject: perf: use `os.path.abspath()` instead of `Path.resolve()` (#10821)
+
+(cherry picked from commit a029a41ea4adb1b35e9d1ba4fe391cd1c77804d8)
+---
+ src/poetry/installation/wheel_installer.py |  15 ++-
+ tests/installation/test_wheel_installer.py | 101 +++++++++++++++++++--
+ 2 files changed, 108 insertions(+), 8 deletions(-)
+
+diff --git a/src/poetry/installation/wheel_installer.py 
b/src/poetry/installation/wheel_installer.py
+index a086ca73..c4fff3b1 100644
+--- a/src/poetry/installation/wheel_installer.py
++++ b/src/poetry/installation/wheel_installer.py
+@@ -1,6 +1,7 @@
+ from __future__ import annotations
+ 
+ import logging
++import os
+ import platform
+ import sys
+ 
+@@ -44,8 +45,18 @@ class WheelDestination(SchemeDictionaryDestination):
+         from installer.utils import copyfileobj_with_hashing
+         from installer.utils import make_file_executable
+ 
+-        target_dir = Path(self.scheme_dict[scheme]).resolve()
+-        target_path = (target_dir / path).resolve()
++        # See https://docs.python.org/3/library/zipfile.html#zipfile.Path:
++        #  When handling untrusted archives,
++        #  consider resolving filenames using os.path.abspath()
++        #  and checking against the target directory with 
os.path.commonpath().
++        #
++        # Attention: Path.absolute() is not sufficient because it does not
++        #  normalize, i.e. does not remove "..".
++        #
++        # We want to avoid Path.resolve() because it is significantly slower
++        # than os.path.abspath()!
++        target_dir = Path(os.path.abspath(self.scheme_dict[scheme]))
++        target_path = Path(os.path.abspath(target_dir / path))
+ 
+         if not target_path.is_relative_to(target_dir):
+             raise ValueError(
+diff --git a/tests/installation/test_wheel_installer.py 
b/tests/installation/test_wheel_installer.py
+index 33e95dd9..3ab60a59 100644
+--- a/tests/installation/test_wheel_installer.py
++++ b/tests/installation/test_wheel_installer.py
+@@ -10,6 +10,7 @@ import pytest
+ from poetry.core.constraints.version import parse_constraint
+ 
+ from poetry.installation.wheel_installer import WheelInstaller
++from poetry.utils._compat import WINDOWS
+ from poetry.utils.env import MockEnv
+ 
+ 
+@@ -21,7 +22,7 @@ if TYPE_CHECKING:
+ 
+ @pytest.fixture
+ def env(tmp_path: Path) -> MockEnv:
+-    return MockEnv(path=tmp_path)
++    return MockEnv(path=tmp_path / "env")
+ 
+ 
+ @pytest.fixture(scope="module")
+@@ -97,14 +98,20 @@ def test_install_dir_is_symlink(tmp_path: Path, 
demo_wheel: Path) -> None:
+     assert (Path(env.paths["purelib"]) / "demo").exists()
+ 
+ 
[email protected]
+-def wheel_with_path_traversal(tmp_path: Path) -> Path:
[email protected](params=[False, True])  # relative path
++def wheel_with_path_traversal(tmp_path: Path, request: pytest.FixtureRequest) 
-> Path:
+     import zipfile
+ 
++    traversal_path = (
++        "../../traversal.txt"
++        if request.param
++        else (tmp_path / "traversal.txt").as_posix()
++    )
++
+     wheel = tmp_path / "traversal-0.1-py3-none-any.whl"
+     files = {
+         "traversal/__init__.py": b"",
+-        "../../traversal.txt": b"",
++        traversal_path: b"path traversal",
+         "traversal-0.1.dist-info/WHEEL": (
+             b"Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n"
+         ),
+@@ -124,8 +131,90 @@ def wheel_with_path_traversal(tmp_path: Path) -> Path:
+     return wheel
+ 
+ 
+-def test_path_traversal(env: MockEnv, wheel_with_path_traversal: Path) -> 
None:
[email protected]("existing", [False, True])
++def test_no_path_traversal(
++    env: MockEnv, wheel_with_path_traversal: Path, existing: bool
++) -> None:
++    target = env.path.parent / "traversal.txt"
++    if existing:
++        target.write_text("original", encoding="utf-8")
+     installer = WheelInstaller(env)
+     with pytest.raises(ValueError):
+         installer.install(wheel_with_path_traversal)
+-    assert not (env.path.parent / "traversal.txt").exists()
++
++    if existing:
++        assert target.exists()
++        assert target.read_text(encoding="utf-8") == "original"
++    else:
++        assert not target.exists()
++
++
[email protected](params=[False, True])  # relative path
++def wheel_with_symlink(tmp_path: Path, request: pytest.FixtureRequest) -> 
Path:
++    import stat
++    import zipfile
++
++    wheel = tmp_path / "symlink-0.1-py3-none-any.whl"
++    files = {
++        "symlink/__init__.py": b"",
++        "symlink-0.1.dist-info/WHEEL": (
++            b"Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n"
++        ),
++        "symlink-0.1.dist-info/METADATA": (
++            b"Metadata-Version: 2.1\nName: symlink-pkg\nVersion: 0.1\n"
++        ),
++    }
++
++    symlink_entry = "symlink/traversal_link"
++    symlink_target = (
++        b"../../target"
++        if request.param
++        else (tmp_path / "target").as_posix().encode("utf-8")
++    )
++    traversal_file = "symlink/traversal_link/traversal.txt"
++
++    record_lines = [f"{k},," for k in files]
++    record_lines.append(f"{symlink_entry},,")
++    record_lines.append(f"{traversal_file},,")
++    record_lines.append("symlink-0.1.dist-info/RECORD,,")
++    files["symlink-0.1.dist-info/RECORD"] = ("\n".join(record_lines) + 
"\n").encode()
++
++    with zipfile.ZipFile(wheel, "w") as z:
++        for k, v in files.items():
++            z.writestr(k, v)
++
++        # Add a ZIP entry whose external attributes mark it as a symlink.
++        # The entry's data is the symlink target, pointing outside the
++        # installation directory.
++        info = zipfile.ZipInfo(symlink_entry)
++        info.create_system = 3  # unix
++        info.external_attr = (stat.S_IFLNK | 0o777) << 16
++        z.writestr(info, symlink_target)
++
++        z.writestr(traversal_file, b"path traversal")
++
++    return wheel
++
++
[email protected]("existing", [False, True])
++def test_no_path_traversal_via_symlink(
++    tmp_path: Path, env: MockEnv, wheel_with_symlink: Path, existing: bool
++) -> None:
++    target_dir = tmp_path / "target"
++    target_dir.mkdir()
++    target = target_dir / "traversal.txt"
++    if existing:
++        target.write_text("original", encoding="utf-8")
++
++    installer = WheelInstaller(env)
++    with pytest.raises(FileNotFoundError if WINDOWS else NotADirectoryError):
++        installer.install(wheel_with_symlink)
++
++    traversal_link = Path(env.paths["purelib"]) / "symlink" / "traversal_link"
++    assert traversal_link.exists()
++    assert not traversal_link.is_symlink()  # not even extracted as symlink
++    assert target_dir.exists()
++    if existing:
++        assert target.read_text(encoding="utf-8") == "original"
++    else:
++        assert not list(target_dir.iterdir())
+-- 
+2.47.3
+
diff -Nru poetry-2.1.2+dfsg/debian/patches/series 
poetry-2.1.2+dfsg/debian/patches/series
--- poetry-2.1.2+dfsg/debian/patches/series     2025-04-04 02:49:29.000000000 
+0300
+++ poetry-2.1.2+dfsg/debian/patches/series     2026-07-03 18:25:04.000000000 
+0300
@@ -2,3 +2,5 @@
 fix-env-manager.patch
 fix-tests.patch
 increase-sleep-time-in-threading-test.patch
+0001-installer-fix-path-traversal-10792.patch
+0002-perf-use-os.path.abspath-instead-of-Path.resolve-108.patch

--- End Message ---
--- Begin Message ---
Version: 13.6

This update was released as part of 13.6.

--- End Message ---

Reply via email to