Papers
Topics
Authors
Recent
Search
2000 character limit reached

PyPitfall: Mapping Vulnerable Dependencies in PyPI

Updated 4 July 2026
  • PyPitfall is a large-scale empirical framework that quantifies how known vulnerabilities propagate through direct and transitive dependencies on PyPI.
  • The framework analyzes dependency metadata using PEP 440 specifiers and classifies exposures as guaranteed or potential based on version constraints.
  • Empirical findings from over 378,573 packages reveal widespread potential exposures, underscoring the need for stricter dependency management in Python projects.

Searching arXiv for the PyPitfall paper and closely related PyPI supply-chain security work. arXiv search query: PyPitfall Python dependency chaos software supply chain vulnerabilities in Python PyPitfall is a large-scale empirical analysis framework for the Python Package Index (PyPI) that quantifies how known vulnerabilities propagate through direct and transitive dependencies. It studies dependency metadata across the PyPI ecosystem, maps version constraints to a curated set of CVE-identified vulnerable releases, and classifies exposure as either “Guaranteed,” when every permitted version is vulnerable, or “Potential,” when at least one permitted version is vulnerable (Mahon et al., 24 Jul 2025). The framework is motivated by the fact that Python software development heavily relies on third-party packages, while direct and transitive dependencies create a software supply chain in which vulnerabilities can propagate downstream. In the reported analysis, 378,573 PyPI packages were resolved, yielding 4,655 packages that explicitly require at least one known-vulnerable version and 141,044 packages that permit vulnerable versions within specified ranges (Mahon et al., 24 Jul 2025).

1. Definition, scope, and analytical objectives

PyPitfall is defined as a quantitative analysis of vulnerable dependencies across the PyPI ecosystem (Mahon et al., 24 Jul 2025). Its stated objectives are to crawl and resolve the dependency metadata of all publicly available PyPI packages, map these dependencies against a curated set of known vulnerabilities in Python libraries, classify exposures as “Guaranteed” or “Potential,” and produce ecosystem-wide statistics, case studies, and recommendations for improving Python software supply-chain security (Mahon et al., 24 Jul 2025).

The study is confined to dependency-mediated exposure. It does not treat all package risk as equivalent. A dependency may be vulnerable because its allowed version set overlaps a known vulnerable range, even when the parent package itself is not directly vulnerable in isolation. This distinction is central to the framework’s emphasis on transitive dependencies, which may occur at arbitrary depth and are resolved by pip’s recursive solver (Mahon et al., 24 Jul 2025).

A common misunderstanding is to equate all supply-chain risk in PyPI with the same phenomenon. PyPitfall concerns known vulnerabilities in dependency version ranges, whereas other work on the PyPI ecosystem studies intentionally malicious packages and their lifecycle, including install-time, import-time, and run-time attack vectors (Guo et al., 2023). These are adjacent but distinct problem classes.

2. Dependency model and exposure semantics

In PyPI’s packaging model, a package PP publishes requirement specifiers on other packages. A direct dependency of PP is any package DD explicitly listed in PP’s metadata under PEP 508, whereas a transitive dependency of PP is any package TT reachable through a dependency chain

PD1D2T.P \to D_1 \to D_2 \to \cdots \to T.

PyPitfall models the resolved ecosystem as a directed graph

G=(V,E),G=(V,E),

where VV is the set of unique packages encountered and (u,v)E(u,v)\in E indicates that PP0 declares PP1 as a dependency (Mahon et al., 24 Jul 2025).

Version admissibility is defined using PEP 440 specifiers. Exact matches such as ==1.2.3, inequalities and ranges such as >=1.0,\<2.0, and wildcards such as ~=1.4 define allowed version sets PP2 for each dependency (Mahon et al., 24 Jul 2025). For each vulnerable package PP3, PyPitfall records a vulnerability set PP4 described by unions of version ranges, for example \<1.3.0 or (\geq 2.0.0 \land \<2.0.6)\lor(\<1.26.17) (Mahon et al., 24 Jul 2025).

Exposure is then computed by intersecting a parent package’s allowed version set with the vulnerable set:

PP5

The classification rule is exact. There is Guaranteed Exposure if PP6, meaning no non-vulnerable version is permitted. There is Potential Exposure if PP7 but PP8, meaning at least one vulnerable version is allowed but not exclusively so (Mahon et al., 24 Jul 2025).

This distinction is analytically important. Guaranteed exposure corresponds to a resolver outcome space in which every admissible choice is vulnerable. Potential exposure corresponds to a mixed admissible set containing both vulnerable and non-vulnerable releases. A plausible implication is that the latter category is more sensitive to resolver behavior, environment, and release-time package availability, whereas the former reflects a stronger metadata-level commitment to vulnerable versions.

3. Data collection and computational methodology

The study retrieved 616,266 PyPI package names out of 627,810 unique names claimed and successfully resolved dependency metadata for 378,573 packages, corresponding to 60.3% of the retrieved set (Mahon et al., 24 Jul 2025). Collection was performed through continuous dry-run resolution via Johnnydep over approximately 17 days. Metadata was obtained from the PyPI Simple Index under PEP 503 and pip’s backtracking resolver, accessed through Johnnydep (Mahon et al., 24 Jul 2025).

The vulnerability corpus consisted of 67 CVE entries affecting third-party Python packages, collected manually from NVD and MITRE, with the built-in standard library excluded (Mahon et al., 24 Jul 2025). Comparison between dependency constraints and vulnerable version ranges was performed by iteratively parsing JSON-encoded dependency trees with an iterative DFS and using Python’s packaging library to interpret and compare version specifiers (Mahon et al., 24 Jul 2025).

PyPitfall’s scalable pipeline rests on three implementation choices: parallelized dry-run dependency resolution through Johnnydep, memory-efficient iterative JSON parsing through ijson, and PEP 440–compliant version specifier resolution (Mahon et al., 24 Jul 2025). These choices support an ecosystem-wide mapping from dependency reachability to vulnerability exposure.

The study also defines reachability and a package-level exposure measure. For each node PP9, the transitive dependency set is derived from the reachability relation DD0. The vulnerability-aware subset is

DD1

and the end-to-end map of exposure is summarized through

DD2

This quantifies the fraction of a package’s total dependency footprint that is vulnerable (Mahon et al., 24 Jul 2025).

4. Ecosystem-scale measurements

The resolved subgraph contains 57,767 nodes. Across the analyzed packages, the number of dependency edges (non-unique transitive nodes) is 47,974,375, the average direct dependencies per top-level package is 2.6, the average number of non-unique transitive dependencies is 129.6, and the average depth of dependency chains is 2.3 levels, with a maximum observed depth of 23 (Mahon et al., 24 Jul 2025). The analysis also detected 1,075,559 unique circular dependency occurrences, with mean circular depth 10.3 (Mahon et al., 24 Jul 2025).

The exposure counts are the central empirical result.

Metric Value Note
Resolved packages 378,573 60.3% of retrieved packages
Guaranteed exposures 4,655 Approximately 1.23%
Potential exposures 141,044 Approximately 37.27%

These numbers indicate that Guaranteed exposures are relatively rare in percentage terms, while Potential exposures are widespread (Mahon et al., 24 Jul 2025). This suggests that much of the ecosystem’s supply-chain risk is encoded not as hard pins to vulnerable releases, but as permissive version ranges that still admit vulnerable versions.

PyPitfall also reports graph metrics specific to vulnerability spread. The average depth of guaranteed exposures is 4.1, whereas the average depth of potential exposures is 6.2 (Mahon et al., 24 Jul 2025). The effective vulnerability propagation diameter, defined as the longest vulnerability path observed, is 17 for guaranteed exposures, and no guaranteed exposure is observed beyond depth 17 (Mahon et al., 24 Jul 2025). Packages such as setuptools and urllib3 have breadth factors exceeding 300,000 occurrences across all dependency trees (Mahon et al., 24 Jul 2025). These breadth values identify a small number of highly reused packages as dominant amplifiers of ecosystem-wide exposure.

5. Transitive propagation and the urllib3 case study

The study emphasizes that vulnerability propagation is not confined to immediate dependencies. Aggregating over top-level packages, a single widely used vulnerable package such as urllib3 can appear in over 407,000 distinct dependency paths and can reach depths up to 22 (Mahon et al., 24 Jul 2025). This is the clearest demonstration of the transitive nature of the problem: a package may inherit exposure through deep dependency chains without directly naming the vulnerable package.

The urllib3 case study is quantitatively specific. PyPitfall records 407,333 urllib3 occurrences in dependencies and associates urllib3 with the curated CVEs CVE-2020-7212, CVE-2021-28363, CVE-2023-43804, CVE-2023-45803, and CVE-2024-37891 (Mahon et al., 24 Jul 2025). The study identifies 1,906 top-level packages with guaranteed exposures to urllib3, representing 41.4% of all guaranteed exposures, and 100,213 top-level packages with potential exposures, representing 71.1% of all potential exposures (Mahon et al., 24 Jul 2025).

The accompanying visualization is described as showing multiple popular packages, including requests and boto3, propagating urllib3 vulnerabilities deep into the ecosystem (Mahon et al., 24 Jul 2025). This does not imply that these upstream packages are malicious or intrinsically defective; rather, it illustrates how central libraries can function as transmission hubs for downstream vulnerability exposure when version constraints remain permissive or outdated.

A common misconception is that transitive risk is a secondary issue compared with direct dependency hygiene. The reported depth, breadth, and path-count statistics indicate the opposite: transitive propagation is structurally central to ecosystem-wide exposure (Mahon et al., 24 Jul 2025).

6. Mitigations, infrastructure implications, and relation to adjacent Python ecosystem risks

The recommendations are directed at maintainers, users, repository infrastructure, and dependency resolution tooling. For package maintainers and users, the study recommends stricter version pinning to non-vulnerable versions via exact (==) or narrow range specifiers so that DD3, automated security scans in CI/CD using tools such as pip-audit or in-toto, and regular dependency reviews against updated vulnerability databases (Mahon et al., 24 Jul 2025).

For PyPI infrastructure and resolver behavior, the recommendations are to extend the PyPI Simple Index to expose vulnerability advisories, enhance pip’s resolver to emit warnings or errors when a final candidate set contains guaranteed or potential exposures, and support advisory-driven resolution strategies that automatically avoid vulnerable version ranges unless explicitly overridden (Mahon et al., 24 Jul 2025). These proposals target the metadata and resolution layers rather than only post hoc auditing.

Within the broader Python ecosystem, PyPitfall complements rather than replaces other security analyses. Work on malicious code in PyPI focuses on disguised malicious packages, multi-behavior malware, source-install attack success, and persistence on mirror servers (Guo et al., 2023). PyPitfall instead addresses a different failure mode: ordinary package metadata that permits or requires known-vulnerable versions (Mahon et al., 24 Jul 2025). Both are supply-chain problems, but one is primarily about malicious artifact publication and distribution, while the other is about vulnerability propagation through dependency constraints.

A further distinction can be drawn from Python testing research. Flaky tests in PyPI projects are defined by nondeterministic PASS/FAIL or ERROR outcomes under unchanged code and are dominated by order dependency and infrastructure causes (Gruber et al., 2021). That phenomenon concerns test reliability, not dependency vulnerability propagation. The comparison is useful because it shows that “pitfalls” in the Python ecosystem span orthogonal layers: testing nondeterminism, malicious package publication, and dependency-mediated exposure are separate empirical domains with different methodologies and mitigations.

PyPitfall’s principal significance lies in making dependency chaos measurable. By constructing DD4, computing reachability, and classifying exposures through the relations

DD5

it provides an ecosystem-level account of how vulnerable version allowances are embedded in Python package metadata and how those allowances propagate through transitive dependency structures (Mahon et al., 24 Jul 2025).

Definition Search Book Streamline Icon: https://streamlinehq.com
References (3)

Topic to Video (Beta)

No one has generated a video about this topic yet.

Whiteboard

No one has generated a whiteboard explanation for this topic yet.

Follow Topic

Get notified by email when new papers are published related to PyPitfall.