Skip to content

Bump setuptools to 83.0.0 to remediate Unicode MANIFEST exclusion bypass - #2893

Open
Sergio Sisternes (sergio-sisternes-epam) with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-setuptools-manifest-in-exclusion-bypass
Open

Bump setuptools to 83.0.0 to remediate Unicode MANIFEST exclusion bypass#2893
Sergio Sisternes (sergio-sisternes-epam) with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-setuptools-manifest-in-exclusion-bypass

Conversation

Copilot AI commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Description

This updates setuptools to the first patched version to address CVE-2026-59890 / GHSA-h35f-9h28-mq5c, where Unicode normalization mismatches can bypass MANIFEST.in exclusions during sdist creation on macOS filesystems.

  • Dependency remediation

    • Regenerated uv.lock via uv tooling with a targeted package upgrade.
    • Locked setuptools from 80.9.0 to 83.0.0 (lowest non-vulnerable version).
  • Reachability Assessment

    • Reachable.
    • Repository workflows and tests actively build sdists (uv build --sdist / build backend path), which is the affected code path in the advisory.
    • Relevant call sites include:
      • tests/integration/test_tls_wheel_content.py (--sdist build path)
      • .github/workflows/build-release.yml (uv build for publish artifacts)
    • Confidence: High (advisory names concrete affected behavior and repo uses that packaging path directly).
[[package]]
name = "setuptools"
version = "83.0.0"

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

Spec conformance (OpenAPM v0.1)

If this PR changes behaviour that an OpenAPM v0.1 req-XXX covers,
confirm the three-step ritual (see CONTRIBUTING.md "Adding or
changing a normative requirement"):

  • Spec edit: docs/src/content/docs/specs/openapm-v0.1.md updated
    (new/changed <a id="req-XXX"></a> anchor + prose + Appendix C
    row).
  • Manifest edit: docs/src/content/docs/specs/manifests/openapm-v0.1.requirements.yml
    updated.
  • Test edit: a @pytest.mark.req("req-XXX") test under
    tests/spec_conformance/ added or extended.
  • CONFORMANCE.{md,json} regenerated via
    uv run --extra dev python -m tests.spec_conformance.gen_statement
    and committed.
  • N/A -- this PR does not change OpenAPM-observable behaviour.
Original prompt

This section details the Dependabot vulnerability alert you should resolve

<alert_title>setuptools: MANIFEST.in exclusion bypass in sdist via Unicode normalization collision (NFC/NFD) on macOS APFS/HFS+</alert_title>
<alert_description>## Summary

When 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.

Details

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

# setuptools/command/egg_info.py  (FileList.global_exclude)
def global_exclude(self, pattern):
    match = translate_pattern(os.path.join('**', pattern))   # fnmatch.translate -> regex, no NFC/NFD
    return self._remove_files(match.match)                   # byte-level regex over raw os.walk names

A 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.

A 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.

Impact

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.

Proof of concept

With a project containing MANIFEST.in:

global-include *.txt *.json
global-exclude secret_café.txt    # rule saved NFC

and 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.

Remediation

Normalize 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.

Credit

Reported by Tomas Illuminati. Coordinated via CERT/CC VINCE VU#604762.</alert_description>

moderate
GHSA-h35f-9h28-mq5c, CVE-2026-59890
setuptools
pip
<vulnerable_versions>= 80.9.0</vulnerable_versions>
<patched_version>83.0.0</patched_version>
<manifest_path>uv.lock</manifest_path>

https://github.com/pypa/setuptools/security/advisories/GHSA-h35f-9h28-mq5c https://nvd.nist.gov/vuln/detail/CVE-2026-59890 https://github.com/pypa/setuptools/commit/dd9f436a36486b4cb8a4c70a2321548b0be09b8f https://github.com/pypa/advisory-database/tree/main/vulns/setuptools/PYSEC-2026-3447.yaml https://github.com/pypa/setuptools/releases/tag/v83.0.0 https://github.com/advisories/GHSA-h35f-9h28-mq5c

<task_instructions>Resolve this alert by updating the affected package to a non-vulnerable version. Prefer the lowest non-vulnerable version (see the patched_version field above) over the latest to minimize breaking changes. Include a Reachability Assessment section in the PR description. Review the alert_description field to understand which APIs, features, or configurations are affected, then search the codebase for usage of those specific items. If the vulnerable code path is reachable, explain how (which files, APIs, or call sites use the affected functionality) and note that the codebase is a...

Co-authored-by: sergio-sisternes-epam <207026618+sergio-sisternes-epam@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix setuptools exclusion bypass in sdist on macOS Bump setuptools to 83.0.0 to remediate Unicode MANIFEST exclusion bypass Sep 8, 2026
@danielmeppiel
Daniel Meppiel (danielmeppiel) marked this pull request as ready for review September 8, 2026 09:35
Copilot AI lite review requested due to automatic review settings September 8, 2026 09:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.

@danielmeppiel
Daniel Meppiel (danielmeppiel) added this pull request to the merge queue Sep 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix CI

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants