Vulnerabilities in pkg:pypi/setuptools
PACKAGECVE-2026-59890 is a MODERATE (CVSS 6.1) manifest.in exclusion bypass vulnerability (CWE-176: Improper Handling of Unicode Encoding) affecting setuptools libraries in the PyPI ecosystem. The issue involves manifest.in exclusion bypass in sdist via unicode normalization collision (nfc/nfd) on macos apfs/hfs+. Remediation is available by upgrading affected packages to version 83.0.0.
pkg:pypi/setuptools
Vulnerability advisories affecting package pkg:pypi/setuptools. Select an advisory below to view technical details or fixes.
| Advisory ID | CVE / NVD | Severity | Summary |
|---|---|---|---|
| GHSA-h35f-9h28-mq5c | CVE-2026-59890 | MODERATE | setuptools: MANIFEST.in exclusion bypass in sdist via Unicode normalization collision (NFC/NFD) on macOS APFS/HFS+ |
| GHSA-cx63-2mw6-8hw5 | CVE-2024-6345 | HIGH | setuptools vulnerable to Command Injection via package URL |
| GHSA-r9hx-vwmv-q579 | CVE-2022-40897 | HIGH | pypa/setuptools vulnerable to Regular Expression Denial of Service (ReDoS) |
| GHSA-5rjg-fvgr-3xxf | CVE-2025-47273 | HIGH | setuptools has a path traversal vulnerability in PackageIndex.download that leads to Arbitrary File Write |
| GHSA-27x4-j476-jp5f | CVE-2013-1633 | HIGH | Setuptools vulnerable to Man-in-the-middle attacks |
CVE-2026-59890 at a Glance
Security Impact
CVSS: 6.1
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N).
How to Fix CVE-2026-59890
To remediate this vulnerability, upgrade all affected setuptools dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install setuptools>=83.0.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show setuptools
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-59890, inspect your installed packages using pip:
pip show setuptools
Analyze the terminal output to locate any version of setuptools in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 83.0.0), your application is affected.
Note: The pip show setuptools command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-176: Improper Handling of Unicode Encoding, where the product does not properly handle when an input contains unicode encoding. In setuptools, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-59890 Affected setuptools Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
setuptools
|
PyPI |
< 83.0.0
|
83.0.0
|
References
Frequently Asked Questions
What is CVE-2026-59890?
Which setuptools versions are affected?
What version fixes CVE-2026-59890?
Is setuptools affected?
What is the CVSS score of CVE-2026-59890?
What CWE is associated with CVE-2026-59890?
How can I check PyPI dependencies for CVE-2026-59890?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-h35f-9h28-mq5c",
"modified" : "2026-07-21T19:09:21Z",
"published" : "2026-07-21T19:09:21Z",
"aliases" : [ "CVE-2026-59890" ],
"summary" : "setuptools: MANIFEST.in exclusion bypass in sdist via Unicode normalization collision (NFC/NFD) on macOS APFS/HFS+",
"details" : "## Summary\n\nWhen building a source distribution (`python -m build --sdist` / `setup.py sdist`), setuptools' `FileList` applies `MANIFEST.in` directives (`exclude`, `global-exclude`, `recursive-exclude`, `prune`) by matching a compiled glob against on-disk file names **byte-for-byte, with no Unicode normalization**. On normalization-preserving filesystems (notably macOS APFS and HFS+), a file written in NFD and a `MANIFEST.in` rule written in NFC refer to the same file but are byte-distinct, so the exclusion silently fails to match. A file the maintainer intended to exclude is then packed into the `.tar.gz` and, if published, uploaded to the public, immutable PyPI index.\n\n## Details\n\nFile names in `FileList.files` come from `os.walk` (`setuptools/_distutils/filelist.py`, `_find_all_simple`), so on APFS a file written NFD is offered to the matcher in NFD, while the `MANIFEST.in` pattern carries the author's editor form (typically NFC). The matching path performs no canonicalization:\n\n```python\n# setuptools/command/egg_info.py (FileList.global_exclude)\ndef global_exclude(self, pattern):\n match = translate_pattern(os.path.join('**', pattern)) # fnmatch.translate -> regex, no NFC/NFD\n return self._remove_files(match.match) # byte-level regex over raw os.walk names\n```\n\nA rule written NFC (`café` = `63 61 66 c3 a9`) does not match an on-disk name written NFD (`café` = `63 61 66 65 cc 81`), even though the filesystem treats the two as one file.\n\nA `unicodedata.normalize('NFD', ...)` helper exists in `setuptools/unicode_utils.py` (`decompose()`), but it is **never called in the manifest matching path**, so neither the pattern nor the walked path is normalized before matching. The only normalization in this area, `EggInfoCommand._manifest_normalize`, uses `filesys_decode` (bytes→str decode only, no NFC/NFD) and runs when writing `SOURCES.txt`, after matching has already occurred.\n\n## Impact\n\n`MANIFEST.in` exclusions are the documented mechanism maintainers use to keep secrets, local configs, and private fixtures out of the published sdist. A non-ASCII excluded file may be published to the public, immutable PyPI index despite the rule — an irreversible disclosure with no visual cue (NFC and NFD forms render identically). Exposure is filesystem-dependent and most relevant on macOS APFS/HFS+, where many maintainers build and publish. Pure-ASCII rules are unaffected.\n\n## Proof of concept\n\nWith a project containing `MANIFEST.in`:\n\n```\nglobal-include *.txt *.json\nglobal-exclude secret_café.txt # rule saved NFC\n```\n\nand an on-disk file `secret_café.txt` written in NFD, `python -m build --sdist` packs the secret file into the resulting `.tar.gz`, while an ASCII control file excluded by the same directive is correctly dropped — isolating the bypass to the NFC-pattern vs. NFD-name mismatch. Reproduced on macOS APFS with setuptools 82.0.1.\n\n## Remediation\n\nNormalize both the walked path and each `MANIFEST.in` pattern to a single canonical form before matching, in both `setuptools/command/egg_info.py` (`FileList`) and the vendored `setuptools/_distutils/filelist.py`. For an exclusion list, err toward excluding more, and document that `MANIFEST.in` matching is normalization-insensitive on macOS.\n\n## Credit\n\nReported by Tomas Illuminati. Coordinated via CERT/CC VINCE VU#604762.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "setuptools"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "83.0.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/pypa/setuptools/security/advisories/GHSA-h35f-9h28-mq5c"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-59890"
}, {
"type" : "WEB",
"url" : "https://github.com/pypa/setuptools/commit/dd9f436a36486b4cb8a4c70a2321548b0be09b8f"
}, {
"type" : "WEB",
"url" : "https://github.com/pypa/advisory-database/tree/main/vulns/setuptools/PYSEC-2026-3447.yaml"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/pypa/setuptools"
}, {
"type" : "WEB",
"url" : "https://github.com/pypa/setuptools/releases/tag/v83.0.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-176", "CWE-697" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-21T19:09:21Z",
"nvd_published_at" : "2026-07-08T17:17:27Z"
}
}