{"id":"CVE-2026-41140","aliases":["AZL-84035","DEBIAN-CVE-2026-41140","EUVD-2026-25578","GHSA-73h3-mf4w-8647","PYSEC-2026-2890","ROOT-APP-PYPI-CVE-2026-41140","UBUNTU-CVE-2026-41140"],"status":"published","title":"Poetry: Path traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","severity":{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00294,"percentile":0.22233,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22"],"published":"2026-04-22T14:35:30Z","modified":"2026-11-05T05:40:51Z","fixes":[{"ecosystem":"PyPI","package":"poetry","fixed_in":["2.3.4"],"sources":["ghsa","osv"]},{"ecosystem":"Azure Linux:3","package":"poetry","fixed_in":["1.8.5-2"],"sources":["osv"]},{"ecosystem":"Debian:13","package":"poetry","fixed_in":["2.1.2+dfsg-1"],"sources":["osv"]},{"ecosystem":"Debian:14","package":"poetry","fixed_in":["2.1.2+dfsg-1"],"sources":["osv"]},{"ecosystem":"Root:PyPI","package":"rootio-poetry","fixed_in":["2.3.2+root.io.2","2.3.2+root.io.3"],"sources":["osv"]},{"ecosystem":"Root:PyPI","package":"poetry","fixed_in":["2.3.2+aikido.3"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.3.4"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.8.5-2"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.3.2+dfsg-3","1.3.2+dfsg-4","1.3.2+dfsg-5","1.5.1+dfsg-1","1.5.1+dfsg-2","1.5.1+dfsg-3","1.5.1+dfsg-4","1.6.1+dfsg-1","1.6.1+dfsg-2","1.7.1+dfsg-1","1.8.0.dev0~git20240220.cff4d7d5+dfsg-1","1.8.0.dev0~git20240220.cff4d7d5+dfsg-2","1.8.2+dfsg-1","1.8.3+dfsg-1","1.8.3+dfsg-2","1.8.3+dfsg-3","1.8.3+dfsg-4","2.0.1+dfsg-1","2.1.1+dfsg-1","2.1.1+dfsg-2","2.1.1+dfsg-3","2.1.2+dfsg-1","2.2.1+dfsg-1","2.2.1+dfsg-2","2.2.1+dfsg-3","2.3.2+dfsg-1","2.3.2+dfsg-2","2.3.2+dfsg-3","2.3.4-0.1","2.3.4-1","2.3.4-2","2.3.4-3"]},{"source":"osv","ecosystem":"Debian:13","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.1.2+dfsg-1"}]}]},{"source":"osv","ecosystem":"Debian:14","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.1.2+dfsg-1"}]}]},{"source":"osv","ecosystem":"PyPI","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.3.4"}]}],"versions":["0.1.0","0.10.0","0.10.0a0","0.10.0a1","0.10.0a2","0.10.0a3","0.10.1","0.10.2","0.10.3","0.11.0","0.11.0a0","0.11.0a1","0.11.0a2","0.11.0a3","0.11.0a4","0.11.1","0.11.2","0.11.3","0.11.4","0.11.5","0.12.0","0.12.0a0","0.12.0a1","0.12.0a2","0.12.0a3","0.12.0a4","0.12.0a5","0.12.1","0.12.10","0.12.11","0.12.12","0.12.13","0.12.14","0.12.15","0.12.16","0.12.17","0.12.2","0.12.3","0.12.4","0.12.5","0.12.6","0.12.7","0.12.8","0.12.9","0.2.0","0.3.0","0.4.0","0.4.0.post1","0.4.1","0.4.2","0.5.0","0.5.0b1","0.5.0b2","0.6.0","0.6.1","0.6.2","0.6.3","0.6.3b1","0.6.3b2","0.6.3b3","0.6.3b4","0.6.3b5","0.6.3b6","0.6.3b7","0.6.4","0.6.4b1","0.6.5","0.7.0","0.7.0b1","0.7.0b2","0.7.0b3","0.7.0b4","0.7.1","0.8.0","0.8.0a0","0.8.0a1","0.8.0a2","0.8.0a3","0.8.0a4","0.8.1","0.8.1a0","0.8.2","0.8.3","0.8.4","0.8.5","0.8.5a0","0.8.6","0.9.0","0.9.0a0","0.9.0a1","0.9.0a2","0.9.0a3","0.9.1","1.0.0","1.0.0a0","1.0.0a1","1.0.0a2","1.0.0a3","1.0.0a4","1.0.0a5","1.0.0b1","1.0.0b2","1.0.0b3","1.0.0b4","1.0.0b5","1.0.0b6","1.0.0b7","1.0.0b8","1.0.0b9","1.0.1","1.0.10","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.1.0a1","1.1.0a2","1.1.0a3","1.1.0b1","1.1.0b2","1.1.0b3","1.1.0b4","1.1.0rc1","1.1.1","1.1.10","1.1.11","1.1.12","1.1.13","1.1.14","1.1.15","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.1.8","1.1.9","1.2.0","1.2.0a1","1.2.0a2","1.2.0b1","1.2.0b2","1.2.0b3","1.2.0rc1","1.2.0rc2","1.2.1","1.2.2","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.6.0","1.6.1","1.7.0","1.7.1","1.8.0","1.8.1","1.8.2","1.8.3","1.8.4","1.8.5","2.0.0","2.0.1","2.1.0","2.1.1","2.1.2","2.1.3","2.1.4","2.2.0","2.2.1","2.3.0","2.3.1","2.3.2","2.3.3"]},{"source":"osv","ecosystem":"PyPI","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.3.4"}]}],"versions":["0.1.0","0.10.0","0.10.0a0","0.10.0a1","0.10.0a2","0.10.0a3","0.10.1","0.10.2","0.10.3","0.11.0","0.11.0a0","0.11.0a1","0.11.0a2","0.11.0a3","0.11.0a4","0.11.1","0.11.2","0.11.3","0.11.4","0.11.5","0.12.0","0.12.0a0","0.12.0a1","0.12.0a2","0.12.0a3","0.12.0a4","0.12.0a5","0.12.1","0.12.10","0.12.11","0.12.12","0.12.13","0.12.14","0.12.15","0.12.16","0.12.17","0.12.2","0.12.3","0.12.4","0.12.5","0.12.6","0.12.7","0.12.8","0.12.9","0.2.0","0.3.0","0.4.0","0.4.0.post1","0.4.1","0.4.2","0.5.0","0.5.0b1","0.5.0b2","0.6.0","0.6.1","0.6.2","0.6.3","0.6.3b1","0.6.3b2","0.6.3b3","0.6.3b4","0.6.3b5","0.6.3b6","0.6.3b7","0.6.4","0.6.4b1","0.6.5","0.7.0","0.7.0b1","0.7.0b2","0.7.0b3","0.7.0b4","0.7.1","0.8.0","0.8.0a0","0.8.0a1","0.8.0a2","0.8.0a3","0.8.0a4","0.8.1","0.8.1a0","0.8.2","0.8.3","0.8.4","0.8.5","0.8.5a0","0.8.6","0.9.0","0.9.0a0","0.9.0a1","0.9.0a2","0.9.0a3","0.9.1","1.0.0","1.0.0a0","1.0.0a1","1.0.0a2","1.0.0a3","1.0.0a4","1.0.0a5","1.0.0b1","1.0.0b2","1.0.0b3","1.0.0b4","1.0.0b5","1.0.0b6","1.0.0b7","1.0.0b8","1.0.0b9","1.0.1","1.0.10","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.1.0a1","1.1.0a2","1.1.0a3","1.1.0b1","1.1.0b2","1.1.0b3","1.1.0b4","1.1.0rc1","1.1.1","1.1.10","1.1.11","1.1.12","1.1.13","1.1.14","1.1.15","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.1.8","1.1.9","1.2.0","1.2.0a1","1.2.0a2","1.2.0b1","1.2.0b2","1.2.0b3","1.2.0rc1","1.2.0rc2","1.2.1","1.2.2","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.4.2","1.5.0","1.5.1","1.6.0","1.6.1","1.7.0","1.7.1","1.8.0","1.8.1","1.8.2","1.8.3","1.8.4","1.8.5","2.0.0","2.0.1","2.1.0","2.1.1","2.1.2","2.1.3","2.1.4","2.2.0","2.2.1","2.3.0","2.3.1","2.3.2","2.3.3"]},{"source":"osv","ecosystem":"Root:PyPI","package":"rootio-poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.3.2+root.io.2"},{"fixed":"2.3.2+root.io.3"}]}]},{"source":"osv","ecosystem":"Root:PyPI","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.3.2+aikido.3"}]}]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.1.11+dfsg-3","1.1.12+dfsg-1","1.1.12+dfsg-1ubuntu1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.6.1+dfsg-2","1.7.1+dfsg-1","1.8.2+dfsg-1ubuntu2"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.1.1+dfsg-3","2.1.2+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.3.2+dfsg-3","2.3.4-0.1","2.3.4-1","2.3.4-2"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"poetry","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.1.2+dfsg-1","2.3.2+dfsg-3"]}],"products":[{"vendor":"python-poetry","product":"poetry"},{"vendor":"red hat","product":"red hat ansible automation platform 2"},{"vendor":"red hat","product":"red hat ansible automation platform 2.6"},{"vendor":"red hat","product":"red hat openshift ai (rhoai)"},{"vendor":"red hat","product":"red hat openshift container platform 4"},{"vendor":"red hat","product":"red hat satellite 6"}],"references":[{"url":"https://github.com/python-poetry/poetry/security/advisories/GHSA-73h3-mf4w-8647","tags":["x_refsource_CONFIRM","advisory","web","report"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-41140","tags":["vdb-entry","x_refsource_REDHAT","advisory"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2461604","tags":["issue-tracking","x_refsource_REDHAT","report"]},{"url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-41140.json","tags":["x_sadp-csaf-vex","web"]},{"url":"https://access.redhat.com/errata/RHSA-2026:24866","tags":["vendor-advisory","x_refsource_REDHAT","advisory"]},{"url":"https://github.com/python-poetry/poetry/releases/tag/2.3.4","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41140","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-73h3-mf4w-8647","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41140.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-41140","tags":["advisory"]},{"url":"https://github.com/python-poetry/poetry","tags":["package"]},{"url":"https://pypi.org/project/poetry","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-41140","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-41140","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-41140","title":"Poetry: Path traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T17:10:33.869Z","modified":"2026-07-15T00:59:56.835Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low","assessor":"cna"},{"version":"3.1","score":8.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-22","CWE-22"],"updated_at":"2026-11-02T13:40:57.334596Z"}],"euvd":[{"id":"EUVD-2026-25578","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T17:10:33Z","modified":"2026-07-15T00:59:56Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"}],"updated_at":"2026-11-02T18:19:56.47701Z"}],"ghsa":[{"id":"GHSA-73h3-mf4w-8647","title":"Poetry has Path Traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"### Summary\n\nThe `extractall()` function in `src/poetry/utils/helpers.py:410-426` extracts sdist tarballs without path traversal protection on Python versions where `tarfile.data_filter` is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4.\n\n### Impact\n\nArbitrary file write (path traversal) from untrusted sdist content.\n\n**In practice, the impact is low** because an attacker who exploits this vulnerability can as well include arbitrary code in a `setup.py`, which will be executed when the sdist is built after tar extraction. In other words, a malicious sdist can write arbitrary files by design. However, since it is unexpected and not by design that the file write already happens during tar extraction, this is still considered a vulnerability.\n\nOn Python 3.11.2 (Debian Bookworm default, directly tested), a crafted sdist with `../../` tar member paths writes files outside the intended extraction directory. The traversal occurs during metadata resolution (`poetry add --lock`), before the build backend is run.\n\nAffected Environments: \n- **Python 3.10.0 through 3.10.12** (inclusive): `tarfile.data_filter` absent or broken\n- **Python 3.11.0 through 3.11.4** (inclusive): `tarfile.data_filter` absent or broken\n- **Debian Bookworm**: Python 3.11.2 (default)\n- **Ubuntu 22.04 LTS**: Python 3.10.6 (default)\n\n### Patches\n\nVersions 2.3.4 and newer of Poetry ensure that paths are inside the target directory.\n\n### Root Cause\n\nFile: `src/poetry/utils/helpers.py`, lines 410-426:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                archive.extractall(dest)  # \u003c-- NO FILTER: path traversal\n```\n\nOn Python versions without a working `tarfile.data_filter`, the `else` branch at line 426 calls `tarfile.extractall()` without any filter or path validation. This enables three attack vectors:\n\n1. **Direct path traversal**: Tar members with `../../` path components write files outside the extraction directory.\n2. **Symlink traversal**: A symlink member pointing outside dest, followed by a file written through that symlink, escapes the boundary.\n3. **Hardlink attacks**: Hardlink members can read arbitrary files (same inode) or overwrite targets outside dest.\n\n#### Call Sites\n\nThis function is called from two locations:\n\n1. **`src/poetry/installation/chef.py:104`** (`_prepare_sdist`): During `poetry install` / `poetry add` when building a package from sdist. Only triggered when the executor is enabled (actual installation).\n\n2. **`src/poetry/inspection/info.py:322`** (`_from_sdist_file`): During dependency resolution (`poetry lock` / `poetry add`). This path is reached when the sdist's `PKG-INFO` lacks `Requires-Dist` metadata, forcing Poetry to extract the archive (and afterwards build the package).\n\n### Suggested Fix\n\nApply path validation in the `else` branch, covering direct traversal, symlinks, and hardlinks:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                # Validate all member paths before extraction\n                dest_resolved = dest.resolve()\n                safe_members = []\n                for member in archive.getmembers():\n                    member_path = (dest_resolved / member.name).resolve()\n                    if not member_path.is_relative_to(dest_resolved):\n                        raise ValueError(\n                            f\"Refusing to extract {member.name}: \"\n                            f\"would write outside {dest}\"\n                        )\n                    if member.issym():\n                        link_target = (member_path.parent / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing symlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    elif member.islnk():\n                        link_target = (dest_resolved / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing hardlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    safe_members.append(member)\n                archive.extractall(dest, members=safe_members)\n```","status":"published","published":"2026-04-22T14:35:30Z","modified":"2026-04-27T16:41:40Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:39:59.776263Z"}],"nvd":[{"id":"CVE-2026-41140","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T18:16:28.613Z","modified":"2026-10-26T22:10:00.163Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"low","assessor":"secondary"},{"version":"3.1","score":8.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:15.867234Z"}],"osv":[{"id":"CVE-2026-41140","title":"Poetry: Path traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T17:10:33.869Z","modified":"2026-10-28T02:31:01.85897846Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:45:03.424067Z"},{"id":"DEBIAN-CVE-2026-41140","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T18:16:28.613Z","modified":"2026-10-27T10:01:17.807367975Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"low"}],"updated_at":"2026-11-02T15:45:54.892385Z"},{"id":"GHSA-73h3-mf4w-8647","title":"Poetry has Path Traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"### Summary\n\nThe `extractall()` function in `src/poetry/utils/helpers.py:410-426` extracts sdist tarballs without path traversal protection on Python versions where `tarfile.data_filter` is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4.\n\n### Impact\n\nArbitrary file write (path traversal) from untrusted sdist content.\n\n**In practice, the impact is low** because an attacker who exploits this vulnerability can as well include arbitrary code in a `setup.py`, which will be executed when the sdist is built after tar extraction. In other words, a malicious sdist can write arbitrary files by design. However, since it is unexpected and not by design that the file write already happens during tar extraction, this is still considered a vulnerability.\n\nOn Python 3.11.2 (Debian Bookworm default, directly tested), a crafted sdist with `../../` tar member paths writes files outside the intended extraction directory. The traversal occurs during metadata resolution (`poetry add --lock`), before the build backend is run.\n\nAffected Environments: \n- **Python 3.10.0 through 3.10.12** (inclusive): `tarfile.data_filter` absent or broken\n- **Python 3.11.0 through 3.11.4** (inclusive): `tarfile.data_filter` absent or broken\n- **Debian Bookworm**: Python 3.11.2 (default)\n- **Ubuntu 22.04 LTS**: Python 3.10.6 (default)\n\n### Patches\n\nVersions 2.3.4 and newer of Poetry ensure that paths are inside the target directory.\n\n### Root Cause\n\nFile: `src/poetry/utils/helpers.py`, lines 410-426:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                archive.extractall(dest)  # \u003c-- NO FILTER: path traversal\n```\n\nOn Python versions without a working `tarfile.data_filter`, the `else` branch at line 426 calls `tarfile.extractall()` without any filter or path validation. This enables three attack vectors:\n\n1. **Direct path traversal**: Tar members with `../../` path components write files outside the extraction directory.\n2. **Symlink traversal**: A symlink member pointing outside dest, followed by a file written through that symlink, escapes the boundary.\n3. **Hardlink attacks**: Hardlink members can read arbitrary files (same inode) or overwrite targets outside dest.\n\n#### Call Sites\n\nThis function is called from two locations:\n\n1. **`src/poetry/installation/chef.py:104`** (`_prepare_sdist`): During `poetry install` / `poetry add` when building a package from sdist. Only triggered when the executor is enabled (actual installation).\n\n2. **`src/poetry/inspection/info.py:322`** (`_from_sdist_file`): During dependency resolution (`poetry lock` / `poetry add`). This path is reached when the sdist's `PKG-INFO` lacks `Requires-Dist` metadata, forcing Poetry to extract the archive (and afterwards build the package).\n\n### Suggested Fix\n\nApply path validation in the `else` branch, covering direct traversal, symlinks, and hardlinks:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                # Validate all member paths before extraction\n                dest_resolved = dest.resolve()\n                safe_members = []\n                for member in archive.getmembers():\n                    member_path = (dest_resolved / member.name).resolve()\n                    if not member_path.is_relative_to(dest_resolved):\n                        raise ValueError(\n                            f\"Refusing to extract {member.name}: \"\n                            f\"would write outside {dest}\"\n                        )\n                    if member.issym():\n                        link_target = (member_path.parent / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing symlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    elif member.islnk():\n                        link_target = (dest_resolved / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing hardlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    safe_members.append(member)\n                archive.extractall(dest, members=safe_members)\n```","status":"published","published":"2026-04-22T14:35:30Z","modified":"2026-07-13T16:42:25.945183494Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:40.308826Z"},{"id":"PYSEC-2026-2890","title":"Poetry has Path Traversal in tar extraction on Python 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4","description":"### Summary\n\nThe `extractall()` function in `src/poetry/utils/helpers.py:410-426` extracts sdist tarballs without path traversal protection on Python versions where `tarfile.data_filter` is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4.\n\n### Impact\n\nArbitrary file write (path traversal) from untrusted sdist content.\n\n**In practice, the impact is low** because an attacker who exploits this vulnerability can as well include arbitrary code in a `setup.py`, which will be executed when the sdist is built after tar extraction. In other words, a malicious sdist can write arbitrary files by design. However, since it is unexpected and not by design that the file write already happens during tar extraction, this is still considered a vulnerability.\n\nOn Python 3.11.2 (Debian Bookworm default, directly tested), a crafted sdist with `../../` tar member paths writes files outside the intended extraction directory. The traversal occurs during metadata resolution (`poetry add --lock`), before the build backend is run.\n\nAffected Environments: \n- **Python 3.10.0 through 3.10.12** (inclusive): `tarfile.data_filter` absent or broken\n- **Python 3.11.0 through 3.11.4** (inclusive): `tarfile.data_filter` absent or broken\n- **Debian Bookworm**: Python 3.11.2 (default)\n- **Ubuntu 22.04 LTS**: Python 3.10.6 (default)\n\n### Patches\n\nVersions 2.3.4 and newer of Poetry ensure that paths are inside the target directory.\n\n### Root Cause\n\nFile: `src/poetry/utils/helpers.py`, lines 410-426:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                archive.extractall(dest)  # \u003c-- NO FILTER: path traversal\n```\n\nOn Python versions without a working `tarfile.data_filter`, the `else` branch at line 426 calls `tarfile.extractall()` without any filter or path validation. This enables three attack vectors:\n\n1. **Direct path traversal**: Tar members with `../../` path components write files outside the extraction directory.\n2. **Symlink traversal**: A symlink member pointing outside dest, followed by a file written through that symlink, escapes the boundary.\n3. **Hardlink attacks**: Hardlink members can read arbitrary files (same inode) or overwrite targets outside dest.\n\n#### Call Sites\n\nThis function is called from two locations:\n\n1. **`src/poetry/installation/chef.py:104`** (`_prepare_sdist`): During `poetry install` / `poetry add` when building a package from sdist. Only triggered when the executor is enabled (actual installation).\n\n2. **`src/poetry/inspection/info.py:322`** (`_from_sdist_file`): During dependency resolution (`poetry lock` / `poetry add`). This path is reached when the sdist's `PKG-INFO` lacks `Requires-Dist` metadata, forcing Poetry to extract the archive (and afterwards build the package).\n\n### Suggested Fix\n\nApply path validation in the `else` branch, covering direct traversal, symlinks, and hardlinks:\n\n```python\ndef extractall(source: Path, dest: Path, zip: bool) -\u003e None:\n    \"\"\"Extract all members from either a zip or tar archive.\"\"\"\n    if zip:\n        with zipfile.ZipFile(source) as archive:\n            archive.extractall(dest)\n    else:\n        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}\n        with tarfile.open(source) as archive:\n            if (\n                hasattr(tarfile, \"data_filter\")\n                and sys.version_info[:3] not in broken_tarfile_filter\n            ):\n                archive.extractall(dest, filter=\"data\")\n            else:\n                # Validate all member paths before extraction\n                dest_resolved = dest.resolve()\n                safe_members = []\n                for member in archive.getmembers():\n                    member_path = (dest_resolved / member.name).resolve()\n                    if not member_path.is_relative_to(dest_resolved):\n                        raise ValueError(\n                            f\"Refusing to extract {member.name}: \"\n                            f\"would write outside {dest}\"\n                        )\n                    if member.issym():\n                        link_target = (member_path.parent / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing symlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    elif member.islnk():\n                        link_target = (dest_resolved / member.linkname).resolve()\n                        if not link_target.is_relative_to(dest_resolved):\n                            raise ValueError(\n                                f\"Refusing hardlink {member.name}: \"\n                                f\"target {member.linkname} outside {dest}\"\n                            )\n                    safe_members.append(member)\n                archive.extractall(dest, members=safe_members)\n```","status":"published","published":"2026-07-13T15:02:52.466566Z","modified":"2026-07-13T16:32:58.805584132Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"}],"updated_at":"2026-11-02T15:52:56.429374Z"},{"id":"UBUNTU-CVE-2026-41140","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T18:16:00Z","modified":"2026-10-17T12:58:14.482329332Z","cvss":[{"version":"4.0","score":0.6,"vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U","level":"low"},{"version":"3.1","score":8.7,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T16:56:16.686388Z"},{"id":"AZL-84035","title":"CVE-2026-41140 affecting package poetry for versions less than 1.8.5-2","description":"Poetry is a dependency manager for Python. Prior to 2.3.4, the extractall() function in src/poetry/utils/helpers.py:410-426 extracts sdist tarballs without path traversal protection on Python versions where tarfile.data_filter is unavailable. Considering only Python versions which are still supported by Poetry, these are 3.10.0 - 3.10.12 and 3.11.0 - 3.11.4. This vulnerability is fixed in 2.3.4.","status":"published","published":"2026-04-24T18:16:28Z","modified":"2026-11-05T05:40:51Z","updated_at":"2026-11-05T16:41:24.988217Z"},{"id":"ROOT-APP-PYPI-CVE-2026-41140","title":"CVE-2026-41140 in rootio-poetry - Patched by Root","description":"Root has patched CVE-2026-41140 in the rootio-poetry package for Root:PyPI. Multiple fixed versions available.","status":"published","published":"2026-06-03T18:41:37Z","modified":"2026-06-04T02:00:08.648688135Z","updated_at":"2026-11-02T15:53:28.259095Z"}]}}
