Skip to content

release: preflight wheel member count before ZipInfo materialization #217

Description

@seonghobae

Buyer-visible release-verification resource gap

Fresh protected main 7faf7a3b8a47980113982914000e724ab6a6cda5 still opens the final publication wheel with zipfile.ZipFile(...) before applying any semantic member-count ceiling in scripts/ci/verify_distribution.py. ZipFile parses and materializes the complete central directory into ZipInfo objects during initialization, after which _verify_wheel(...) calls namelist() and builds another complete Python list before path validation.

This is distinct from #69's already-integrated bounded SBOM generator and is the wheel-side sibling of #215/#216. The SBOM generator already preflights the ZIP end record and central directory before allowing ZipFile to allocate member objects; the final publication distribution verifier remains a separate evidence authority and does not yet do so.

RCA

First failing boundary: final wheel publication verification, after a bounded immutable source snapshot is created but before ZipFile central-directory object materialization.

Immediate cause: _verify_wheel(...) constructs zipfile.ZipFile(wheel_file) with no prior finite central-directory member-count admission. namelist() then creates a second full list.

Systemic cause: the verifier has a compressed 256 MiB file ceiling and stable snapshot/digest binding, but no independent finite semantic wheel-member ceiling before the standard-library ZIP parser allocates one ZipInfo per central-directory entry.

Falsifiable hypothesis: if the stable snapshot receives a bounded canonical EOCD/central-directory preflight that rejects an over-budget or unsupported ZIP structure before ZipFile(...), then later standard-library semantic parsing is bounded by the reviewed member ceiling and cannot allocate an unbounded number of ZipInfo objects.

Distinct remedies / feasibility

  1. Preferred: reuse the already-proven EgressWeave SBOM design at this separate parser boundary: bounded EOCD tail read, single-disk/non-ZIP64 canonical central-directory validation, count entries before ZipFile, reject above an explicit finite member ceiling, reset the snapshot, then run normal ZipFile semantic verification.
  2. Open ZipFile and check len(infolist()) or len(namelist()) afterward: rejected because the complete ZipInfo set has already been materialized.
  3. Rely only on the 256 MiB compressed-file ceiling: rejected because metadata object count and Python object overhead are not independently bounded by compressed bytes.
  4. Delegate final verification to the SBOM generator: rejected because publication verification and SBOM generation are distinct evidence authorities with different outputs and failure contracts.

Acceptance criteria

  • Add immutable test-only RED evidence on the exact current release: bound sdist and wheel verifier member enumeration #216 accepted-tree candidate proving an over-budget canonical wheel reaches the current verifier without a semantic wheel-member rejection.
  • Add a deterministic finite MAX_WHEEL_MEMBERS contract and reject over-budget central-directory entry count before zipfile.ZipFile(...) is constructed.
  • Validate one canonical single-disk, non-ZIP64 central-directory framing path with bounded reads; fail closed on inconsistent EOCD/count/size/offset or ZIP64/multi-disk forms outside the canonical release contract.
  • Preserve unsafe/duplicate path rejection, required package/schema/license/metadata checks, exact version/Requires-Python/license metadata, stable source snapshot/digest binding and checksum publication bytes.
  • Do not extract wheel contents or add a dependency/credential/network/repository authority.
  • Preserve Python 3.10–3.14, Ruff, compileall, wheel/sdist/installed-package acceptance and exact 100% owned-production statement/branch coverage.
  • Pass exact-head CI/SAST and all live required workflow evidence. Wrapper-green with the actual pinned Dependency Review step skipped is non-passing; pending/unavailable/no-report Strix is likewise non-passing.
  • Keep this implementation on the existing release: bound sdist and wheel verifier member enumeration #216 verify_distribution.py writer lane; do not create a competing branch for the same path.
  • Merge only an unchanged gate-clean exact head and verify the bounded final wheel/sdist verifier from protected main.

Non-goals

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions