Skip to main content

Poetry CVE-2026-41140

LOW
Path Traversal (CWE-22)
2026-04-22 https://github.com/python-poetry/poetry GHSA-73h3-mf4w-8647
0.6
CVSS 4.0 · Vendor: https://github.com/python-poetry/poetry
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Recon
    Attacker publishes malicious sdist to package index
  2. Delivery
    Developer runs poetry add malicious-package
  3. Exploit
    Poetry fetches sdist and inspects metadata
  4. Install
    extractall() called without path validation on vulnerable Python
  5. C2
    Tar member with ../../ path extracted outside dest directory
  6. Execute
    Arbitrary file written to filesystem
  7. Impact
    Potential code injection or system compromise

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V5 - File Handling Path canonicalization, traversal prevention
MITRE ATT&CK

2 techniques across 2 tactics: Discovery, Collection

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Exploitation requires all of the following: (1) Poetry version prior to 2.3.4 installed and active; (2) Python version 3.10.0 through 3.10.12 or 3.11.0 through 3.11.4 running Poetry; (3) a crafted source distribution (sdist) with tar members containing path traversal sequences (e.g., ../../), symlinks pointing outside the extraction directory, or hardlinks targeting external files; (4) execution of a Poetry command that triggers tar extraction, specifically poetry add <malicious-package> (during dependency resolution) or poetry install with an sdist in the lock file. The exploitation is not automatically triggered during normal package installation from pre-built wheels (which do not invoke extractall()) and requires the presence of PKG-INFO metadata that lacks Requires-Dist fields, forcing Poetry to extract and inspect the sdist for dependency information. The attacker has no privileges requirement-exploitation works against default Poetry installations and configurations with no elevated permissions needed.

Impact

Despite a missing CVSS vector and score, this vulnerability carries moderate real-world risk when contextualized against available signals. The vulnerability requires three converging conditions: (1) a vulnerable Poetry version on an affected Python version, (2) dependency resolution or installation of a malicious sdist, and (3) network exposure during poetry add or poetry lock operations. The attack vector is network-adjacent (the attacker must craft and distribute a malicious sdist), but once a user attempts to add or lock dependencies, extraction occurs automatically without user interaction beyond normal Poetry commands. The vendor's own assessment states 'impact is low in practice' because a malicious sdist can already execute arbitrary code via setup.py during the build phase; however, the vulnerability enables file writes during tar extraction before the build backend runs, which is unexpected and violates least-privilege extraction semantics. No EPSS or KEV status is available, so exploitation probability cannot be quantified from official sources. The suggested patch (path validation with resolve() and is_relative_to() checks) is straightforward and has low implementation complexity, suggesting exploitation was not hardened by design complexity but rather by oversight. The scope is limited to specific Python minor versions, reducing affected population compared to a universal vulnerability. Organizations using affected configurations (Poetry + Python 3.10.0-3.10.12 or 3.11.0-3.11.4) should prioritize upgrading Poetry to 2.3.4 or later, but the urgency is moderate rather than critical, as the attack requires distribution and installation of a malicious package.

Exploit ScenarioDerived

An attacker publishes a malicious Python package to PyPI (or a private index) with a source distribution containing a crafted tarball with members named ../../etc/passwd or similar path traversal sequences. When a developer or CI/CD system runs poetry add malicious-package on a system with Poetry < 2.3.4 and Python 3.10.0-3.10.12 or 3.11.0-3.11.4, Poetry's dependency resolver extracts the sdist to inspect metadata. The unvalidated tar extraction writes files outside the package directory, potentially overwriting system files, injecting code into sibling projects, or poisoning the developer's environment before setup.py even runs.

Monitor Track and reassess. Why: LOW severity (CVSS 0.1-3.9).
Patch available 0.1% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Apr 22, 2026

Path traversal vulnerability in Poetry's tar extraction function allows arbitrary file writes when processing untrusted source distributions on Python 3.10.0-3.10.12 and 3.11.0-3.11.4, where the tarfile.data_filter safety mechanism is absent or broken. The vulnerability is triggered during dependency resolution (poetry add --lock) or installation before the build backend executes, enabling attackers to write files outside the intended extraction directory via crafted tar member paths, symlinks, or hardlinks in malicious sdists.

RemediationAI

Upgrade Poetry to version 2.3.4 or newer, which includes path validation in the extractall() function to reject tar members with paths traversing outside the target directory, symlinks pointing outside the boundary, and hardlinks targeting external files. This is the primary and recommended fix. If an immediate upgrade is not possible, restrict Poetry's operation to Python 3.11.5 or later, or Python 3.10.13 or later (where tarfile.data_filter is functional), by configuring poetry/python version constraints in your CI/CD and development environments. Additionally, audit dependency sources and avoid adding untrusted or unverified packages via poetry add, and enable dependency verification mechanisms (e.g., signed package indexes) if available. A temporary compensating control (not recommended long-term) is to run poetry add and poetry lock in isolated containers with restricted filesystem access, though this adds operational overhead and does not address the root vulnerability.

Affected Packages

pypi poetry fix: 2.3.4

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in pypi

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

Poetry's extractall() function in src/poetry/utils/helpers.py (lines 410-426) conditionally applies tarfile.data_filter to defend against path traversal attacks, but only when the filter is available and not in a known-broken state. On affected Python versions (3.10.0-3.10.12, 3.11.0-3.11.4)-including Debian Bookworm's Python 3.11.2 and Ubuntu 22.04 LTS's Python 3.10.6-the fallback code path calls tarfile.extractall() without any path validation. This exposes three attack vectors: direct path traversal using ../../ path components in tar member names, symlink traversal where symlink targets escape the extraction boundary, and hardlink attacks that can read or overwrite arbitrary files. The root cause is CWE-22 (improper limitation of a pathname to a restricted directory), where untrusted tar archive content is extracted without validating member paths against the destination directory.

Know when Python gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/python-poetry/poetry) PRIMARY 0.6 LOW
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

Primary rating from Vendor (https://github.com/python-poetry/poetry) · only source for this CVE.

CVSS VectorVendor: https://github.com/python-poetry/poetry

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
Present
Privileges Required
None
User Interaction
Passive
Vulnerable system: Confidentiality
None
Vulnerable system: Integrity
Low
Vulnerable system: Availability
None
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

Lifecycle Timeline

3
CVSS changed
Apr 24, 2026 - 18:22 NVD
0.6 (LOW)
Patch released
Apr 23, 2026 - 02:30 nvd
Patch available
CVE Published
Apr 22, 2026 - 14:35 nvd
LOW 0.6

DescriptionCVE.org

Summary

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.

Impact

Arbitrary file write (path traversal) from untrusted sdist content.

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.

On 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.

Affected Environments:

  • Python 3.10.0 through 3.10.12 (inclusive): tarfile.data_filter absent or broken
  • Python 3.11.0 through 3.11.4 (inclusive): tarfile.data_filter absent or broken
  • Debian Bookworm: Python 3.11.2 (default)
  • Ubuntu 22.04 LTS: Python 3.10.6 (default)

Patches

Versions 2.3.4 and newer of Poetry ensure that paths are inside the target directory.

Root Cause

File: src/poetry/utils/helpers.py, lines 410-426:

python
def extractall(source: Path, dest: Path, zip: bool) -> None:
    """Extract all members from either a zip or tar archive."""
    if zip:
        with zipfile.ZipFile(source) as archive:
            archive.extractall(dest)
    else:
        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}
        with tarfile.open(source) as archive:
            if (
                hasattr(tarfile, "data_filter")
                and sys.version_info[:3] not in broken_tarfile_filter
            ):
                archive.extractall(dest, filter="data")
            else:
                archive.extractall(dest)
# <-- NO FILTER: path traversal

On 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:

  1. Direct path traversal: Tar members with ../../ path components write files outside the extraction directory.
  2. Symlink traversal: A symlink member pointing outside dest, followed by a file written through that symlink, escapes the boundary.
  3. Hardlink attacks: Hardlink members can read arbitrary files (same inode) or overwrite targets outside dest.
Call Sites

This function is called from two locations:

  1. 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).
  2. 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).

Suggested Fix

Apply path validation in the else branch, covering direct traversal, symlinks, and hardlinks:

python
def extractall(source: Path, dest: Path, zip: bool) -> None:
    """Extract all members from either a zip or tar archive."""
    if zip:
        with zipfile.ZipFile(source) as archive:
            archive.extractall(dest)
    else:
        broken_tarfile_filter = {(3, 9, 17), (3, 10, 12), (3, 11, 4)}
        with tarfile.open(source) as archive:
            if (
                hasattr(tarfile, "data_filter")
                and sys.version_info[:3] not in broken_tarfile_filter
            ):
                archive.extractall(dest, filter="data")
            else:
# Validate all member paths before extraction
                dest_resolved = dest.resolve()
                safe_members = []
                for member in archive.getmembers():
                    member_path = (dest_resolved / member.name).resolve()
                    if not member_path.is_relative_to(dest_resolved):
                        raise ValueError(
                            f"Refusing to extract {member.name}: "
                            f"would write outside {dest}"
                        )
                    if member.issym():
                        link_target = (member_path.parent / member.linkname).resolve()
                        if not link_target.is_relative_to(dest_resolved):
                            raise ValueError(
                                f"Refusing symlink {member.name}: "
                                f"target {member.linkname} outside {dest}"
                            )
                    elif member.islnk():
                        link_target = (dest_resolved / member.linkname).resolve()
                        if not link_target.is_relative_to(dest_resolved):
                            raise ValueError(
                                f"Refusing hardlink {member.name}: "
                                f"target {member.linkname} outside {dest}"
                            )
                    safe_members.append(member)
                archive.extractall(dest, members=safe_members)

Threat Intelligence Overview

EPSS Score
0.1%
Top 81% exploitable
Threat Intel
1
1 source
Data Quality
6 sources
Confidence: High
NVD CVE.org EPSS Vulnrichment GHSA GitHub VulDB

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity LOW
CISA KEV No
Public POC No
EPSS 0.1%
Patch available Yes
Active threat No
Deciding rule
LOW severity (CVSS 0.1-3.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Defer
Exploitation
None
System Exposure· derived
Open
Automatable
No
Human Impact· derived
Low
Technical Impact
Partial

Priority Score

3
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +3
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Path traversal exploits occur when applications use user-controlled input to construct file system paths without proper validation.

Remote Code Execution represents the critical moment when an attacker successfully runs arbitrary code on a target system without physical access.

Affected Products (CPE)CVE.org

pkg:pip/poetry

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-49869 CRITICAL POC
10.0 Jun 26

Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy