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


- **Severity:** Not scored
- **Exploited in the wild:** Not known
- **Fix available:** Yes
- **Published:** 23 Oct 2026

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

```python
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:

```python
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:

```python
__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.


| Package | Ecosystem | Affected | Fixed in |
| --- | --- | --- | --- |
| `setuptools` | PyPI | From 38.2.2 | 84.1.0 |
| `setuptools` | PyPI | From 38.2.2 | 84.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.

## Fix prompt for AI coding agents

```text
Check this codebase for GHSA-xmj3-9gf7-h52w and fix it if it is affected.

What it is: GHSA-xmj3-9gf7-h52w is a vulnerability in setuptools, published on 23 Oct 2026. 
Exploitation: There is no known exploitation of GHSA-xmj3-9gf7-h52w in the wild and no public exploit code.

Affected packages and fixed versions:
- setuptools (PyPI): affected from 38.2.2; fixed in 84.1.0
- setuptools (PyPI): affected from 38.2.2; fixed in 84.1.0

Task:
1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.
2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.
3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.
4. Run the tests and summarise what changed and why.
5. If nothing in the project is affected, say so and change nothing.

Reference: https://insights.sbomify.com/GHSA-xmj3-9gf7-h52w/ (Markdown: https://insights.sbomify.com/GHSA-xmj3-9gf7-h52w/index.md)
```

## Sources

- OSV: https://osv.dev/vulnerability/GHSA-xmj3-9gf7-h52w

---
From sbomify Insights: https://insights.sbomify.com/GHSA-xmj3-9gf7-h52w/
Summaries are generated and may contain mistakes; the official description above is the record.
