Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-59890

CVE-2026-59890: Python setuptools Unicode Vulnerability

CVE-2026-59890 is a Unicode normalization bypass vulnerability in Python setuptools that allows NFD file names to evade exclusion rules on macOS file systems. This article covers technical details, affected versions, and mitigation.

Published:

CVE-2026-59890 Overview

CVE-2026-59890 is a Unicode normalization flaw [CWE-176] in the Python setuptools package. Prior to version 83.0.0, the FileList class compared MANIFEST.in exclusion patterns against on-disk file names without normalizing Unicode form. On macOS APFS or HFS+ filesystems, file names are stored in Normalization Form Decomposed (NFD), while patterns authored in editors are typically stored in Normalization Form Composed (NFC). This mismatch allows non-ASCII file names to bypass exclude, global-exclude, recursive-exclude, and prune directives. The result is silent inclusion of files that maintainers intended to omit from source distributions.

Critical Impact

Sensitive or unintended non-ASCII-named files may be published to PyPI in source distributions despite explicit MANIFEST.in exclusion rules.

Affected Products

  • setuptools versions prior to 83.0.0
  • Python source distributions built on macOS APFS or HFS+ filesystems
  • Packaging workflows relying on MANIFEST.in exclusion directives

Discovery Timeline

  • 2026-07-08 - CVE-2026-59890 published to NVD
  • 2026-07-08 - Last updated in NVD database

Technical Details for CVE-2026-59890

Vulnerability Analysis

The flaw resides in how setuptools builds source distributions via the FileList class in setuptools/command/egg_info.py. When processing MANIFEST.in, setuptools compiles glob patterns like *.txt into regular expressions and matches them against file paths returned from walking the working tree. The matcher performs a byte-for-byte comparison without normalizing Unicode composition. A pattern such as exclude café.txt authored in an editor is stored as NFC (caf\\u00e9.txt), while the same file on APFS is stored as NFD (cafe\\u0301.txt). These two strings represent identical characters but do not match at the byte level.

Root Cause

The root cause is missing Unicode normalization [CWE-176] before pattern matching. Both sides of the comparison — the compiled regex derived from MANIFEST.in and the file path from the filesystem walk — must be normalized to a common form. Without normalization, the exclusion silently fails and the file is packaged.

Attack Vector

Exploitation requires local access and user interaction, since a maintainer must invoke sdist to build a distribution. A contributor who introduces or renames a non-ASCII file with a decomposed form can cause it to bypass exclusion rules and be published to a package index. This can leak internal files, credentials, or development artifacts that maintainers assumed were excluded.

python
class _NormalizedMatcher:
    """
    Wrap a compiled pattern so that matching is insensitive to Unicode
    normalization form.

    File names walked from disk (NFD on macOS APFS/HFS+) and patterns from
    ``MANIFEST.in`` (typically NFC) can denote the same file while differing
    byte-for-byte. Normalizing both sides before matching keeps an exclusion
    (or inclusion) from silently failing. See GHSA-h35f-9h28-mq5c.
    """

    def __init__(self, pattern: re.Pattern) -> None:
        self._pattern = pattern

    def match(self, path):
        return self._pattern.match(unicode_utils.normalize(path))

    def search(self, path):
        return self._pattern.search(unicode_utils.normalize(path))

Source: pypa/setuptools commit dd9f436

Detection Methods for CVE-2026-59890

Indicators of Compromise

  • Published source distributions containing non-ASCII-named files that appear in MANIFEST.in exclusion directives.
  • sdist builds executed on macOS hosts producing tarballs larger than expected or containing unexpected files.
  • Presence of NFD-encoded filenames in SOURCES.txt generated by egg_info.

Detection Strategies

  • Inventory Python build environments and identify hosts running setuptools versions below 83.0.0.
  • Audit recent PyPI uploads and internal package indexes for tarballs built on macOS to check for unintended files.
  • Compare MANIFEST.in exclusion patterns against tar tf output of produced distributions using unicodedata.normalize on both sides.

Monitoring Recommendations

  • Log setuptools version and host OS in CI pipeline artifacts to identify builds vulnerable to this flaw.
  • Add pre-publish checks that reject source distributions containing files matching normalized exclusion patterns.
  • Track dependency manifests for setuptools<83.0.0 and flag them during dependency review.

How to Mitigate CVE-2026-59890

Immediate Actions Required

  • Upgrade setuptools to version 83.0.0 or later across all build hosts and CI runners.
  • Re-audit source distributions built on macOS since the introduction of non-ASCII file names in the repository.
  • Yank and rebuild any published packages found to contain files that should have been excluded.

Patch Information

The fix was released in setuptools 83.0.0. The commit dd9f436a36486b4cb8a4c70a2321548b0be09b8f introduces the _NormalizedMatcher wrapper, which normalizes both the pattern and the file path via unicode_utils.normalize before applying regex matching. Refer to the GitHub Security Advisory GHSA-h35f-9h28-mq5c and the setuptools v83.0.0 release notes for details.

Workarounds

  • Rename non-ASCII files to ASCII-only names before building source distributions.
  • Build source distributions on Linux, where filesystems typically preserve the composed form supplied by the writer.
  • Manually normalize MANIFEST.in and filesystem entries to a single Unicode form before running sdist.
bash
# Upgrade setuptools to the patched version
python -m pip install --upgrade 'setuptools>=83.0.0'

# Verify installed version
python -c "import setuptools; print(setuptools.__version__)"

# Optional: pin in requirements or pyproject.toml build-system
# [build-system]
# requires = ["setuptools>=83.0.0"]
# build-backend = "setuptools.build_meta"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.