Buyer-visible release-verification resource gap
Fresh protected main 7faf7a3b8a47980113982914000e724ab6a6cda5 still materializes the complete source-distribution member list in scripts/ci/verify_distribution.py before applying any semantic member-count ceiling. _verify_sdist(...) opens the gzip tar and calls TarFile.getmembers(), then builds a second full Python list of member names before _safe_archive_names(...) validates them.
This is distinct from #69's already-integrated bounded SBOM generator. That source repair is concentrated in scripts/ci/generate_release_sbom.py; the final distribution verifier remains a separate publication-admission path and can still allocate metadata proportional to attacker/build-output-controlled archive member count inside the existing compressed-file ceiling.
RCA
First failing boundary: release artifact verification, after the bounded archive snapshot is created but before sdist path/type/required-file validation.
Immediate cause: _verify_sdist(...) uses TarFile.getmembers() and then a list comprehension over every returned TarInfo.
Systemic cause: the distribution verifier has a compressed-byte ceiling and stable-source snapshot, but no independent finite semantic member-count ceiling and no streaming tar-member admission path. The earlier archive-enumeration hardening did not cover this separate verifier.
Falsifiable hypothesis: if sdist verification switches to a streaming gzip-tar reader, validates each member incrementally, and fails before accepting more than a documented finite member budget, the verifier no longer needs getmembers() or a second full member list while preserving canonical package checks.
Distinct remedies / feasibility
- Preferred: stream sdist members (
r|gz), enforce a finite member ceiling before retaining each member, validate path and unsupported member types incrementally, and retain only the bounded name set needed for required/root checks. Python's supported tarfile streaming mode processes data as a stream of blocks and is available throughout the supported Python 3.10–3.14 matrix.
- Keep
getmembers() and check len(...) afterward: rejected because the unbounded materialization has already occurred before the ceiling is enforced.
- Rely only on the 256 MiB compressed-file limit: rejected because highly compressible tar headers/member metadata can amplify object count and memory independently of the compressed-byte ceiling.
- Delegate verification to the SBOM generator: rejected because publication verification and SBOM generation are distinct evidence authorities and must each fail closed at their own parser boundary.
Acceptance criteria
Non-goals
Buyer-visible release-verification resource gap
Fresh protected
main7faf7a3b8a47980113982914000e724ab6a6cda5still materializes the complete source-distribution member list inscripts/ci/verify_distribution.pybefore applying any semantic member-count ceiling._verify_sdist(...)opens the gzip tar and callsTarFile.getmembers(), then builds a second full Python list of member names before_safe_archive_names(...)validates them.This is distinct from #69's already-integrated bounded SBOM generator. That source repair is concentrated in
scripts/ci/generate_release_sbom.py; the final distribution verifier remains a separate publication-admission path and can still allocate metadata proportional to attacker/build-output-controlled archive member count inside the existing compressed-file ceiling.RCA
First failing boundary: release artifact verification, after the bounded archive snapshot is created but before sdist path/type/required-file validation.
Immediate cause:
_verify_sdist(...)usesTarFile.getmembers()and then a list comprehension over every returnedTarInfo.Systemic cause: the distribution verifier has a compressed-byte ceiling and stable-source snapshot, but no independent finite semantic member-count ceiling and no streaming tar-member admission path. The earlier archive-enumeration hardening did not cover this separate verifier.
Falsifiable hypothesis: if sdist verification switches to a streaming gzip-tar reader, validates each member incrementally, and fails before accepting more than a documented finite member budget, the verifier no longer needs
getmembers()or a second full member list while preserving canonical package checks.Distinct remedies / feasibility
r|gz), enforce a finite member ceiling before retaining each member, validate path and unsupported member types incrementally, and retain only the bounded name set needed for required/root checks. Python's supportedtarfilestreaming mode processes data as a stream of blocks and is available throughout the supported Python 3.10–3.14 matrix.getmembers()and checklen(...)afterward: rejected because the unbounded materialization has already occurred before the ceiling is enforced.Acceptance criteria
_verify_sdist(...)calls the full-listgetmembers()path before semantic admission.getmembers().Dependency reviewstep is skipped; unavailable/no-report Strix is likewise non-passing.Non-goals