GHSA-xmj3-9gf7-h52w setuptools: Windows path escape via namespace_packages.txt during legacy wheel installation Description

Not scored Published
Exploited in the wild
Not known
Fix
Available

What is GHSA-xmj3-9gf7-h52w?

GHSA-xmj3-9gf7-h52w is a vulnerability in setuptools, published on 23 Oct 2026.

Official description

Summary

On Windows, attacker-controlled content in a wheel's .dist-info/namespace_packages.txt can cause setuptools' legacy wheel installation path to create an __init__.py outside the requested destination_eggdir.

The attacker can influence the destination path, but not the contents of the created file.

Details

Tested setuptools commit:

6ead555c5fb29bc57fe6105b1bffc163f56fd558

Relevant source:

setuptools/wheel.py

SHA-256:

359b7e0d5dd8c3135653abe08a8ed5dc0352eadfd7df22cb5e2b5303b7c3cafd

The relevant call path is:

Wheel(wheel_path).install_as_egg(destination) → _install_as_egg → _convert_metadata → _fix_namespace_packages

A wheel can provide attacker-controlled content in:

<distribution>.dist-info/namespace_packages.txt

The namespace value is eventually used approximately as:

mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
mod_init = os.path.join(mod_dir, '__init__.py')

If mod is a Windows drive-qualified absolute path containing no dots,
mod.split('.') preserves it as a single component.
On Windows, os.path.join(destination_eggdir, absolute_component) discards
the original base directory and resolves to the attacker-supplied absolute
path.
The existing wheel filename and ZIP traversal checks do not validate this
metadata value before it is subsequently interpreted as a filesystem path.

```markdown
### PoC

The reproduction was performed entirely inside a disposable temporary
directory.

Layout:

```text
sandbox/
    destination/
    outside/
    input/
    canary.txt

A synthetic wheel was created containing:

demo_pkg-1.0.dist-info/namespace_packages.txt

whose content was the absolute path of:

sandbox\outside

The real acquired setuptools implementation was then invoked:

setuptools.wheel.Wheel(synthetic_wheel).install_as_egg(
    sandbox_destination
)

The operation completed without exception.

Expected metadata was created under:

sandbox\destination\EGG-INFO

but setuptools also created:

sandbox\outside\__init__.py

The file did not exist before execution.

Its contents were:

__import__('pkg_resources').declare_namespace(__name__)

The unrelated canary remained byte-identical.

The sandbox was deleted after recording the evidence.


### Impact

A crafted wheel can cause setuptools to create a fixed-content
`__init__.py` outside the intended installation directory at a
filesystem path influenced by the wheel, with the permissions of the
process performing the installation.

The demonstrated reproduction required the target parent directory to
already exist.

Which versions are affected by GHSA-xmj3-9gf7-h52w?

2 open source packages ship an affected version, including setuptools.

PackageAffectedFixed in
setuptools PyPIFrom 38.2.284.1.0
setuptools PyPIFrom 38.2.284.1.0

How do I fix GHSA-xmj3-9gf7-h52w?

Upgrade setuptools to 84.1.0 or later.

Is GHSA-xmj3-9gf7-h52w being exploited?

There is no known exploitation of GHSA-xmj3-9gf7-h52w in the wild and no public exploit code.

Sources

Is GHSA-xmj3-9gf7-h52w in your software?

Upload an SBOM and sbomify checks it against every vulnerability here, every day.

Check your SBOMs for free