[PR #4273] [CLOSED] fix(artifacts): diagnose colliding extraction paths #6151

Closed
opened 2026-10-31 01:12:16 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4273
Author: @maxazure
Created: 10/15/2026
Status: ❌ Closed

Base: main ← Head: fix/zip-extraction-path-collision-4271


📝 Commits (1)

  • cbd3402 fix(artifacts): diagnose colliding extraction paths

📊 Changes

3 files changed (+103 additions, -3 deletions)

View changed files

📝 src/artifacts/extraction/ArtifactExtraction.ts (+10 -3)
➕ tests/acceptance/artifactZipExtractionPaths.test.ts (+75 -0)
📝 tests/boundary/filesystem/artifactZipIntegrity.test.ts (+18 -0)

📄 Description

Summary

Fixes #4271. Report colliding extraction paths instead of falsely claiming valid ZIP bytes failed integrity verification.

Problem and expected behavior

NFC/NFD-equivalent member names or repeated identical names can be valid ZIP entries with different bytes. Inventory retains both occurrences, but extraction's path-keyed Map silently replaced one with the other and verified the first member against the second member's digest. Both integrity policies then failed with an ineffective record-and-continue retry suggestion.

Extraction should refuse the shared destination path without declaring the archive corrupt or overwriting either entry.

Change and scope

Check uniqueness while constructing the selected-path Map, before acquiring the materialization reader or output root. Reuse ArtifactReaderFailure("path", ...) with the normalized path and an explanation that multiple occurrences cannot occupy one destination.

The inventory registry still permits repeated occurrences. Nested active-entry filtering, exact byte/hash verification, cancellation, cleanup and exclusive output creation are unchanged. No suffixing, overwrite option or new schema is introduced.

Contract and boundary impact

  • Semantic owner: selected-occurrence lookup in ArtifactExtraction.materializeSelection.
  • Contribution scope: ordinary static archive-extraction correction; advance maintainer scope decision not applicable.
  • CLI/MCP: existing error contract; collisions now report reason: "path" with a useful message.
  • Provider/platform: no new reader, format or platform behavior.
  • Evidence/provenance: inventory preserves every occurrence and its original content identity.
  • Authority/cleanup: existing local reads and extraction writes only; collision refusal occurs before output acquisition.
  • Generated metadata/package/installation: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

The checked write effect is the existing extraction of files; this change adds no execution or network authority.

Evidence and regression coverage

Executed on Linux with Node 24.18.0 / npm 11.16.0, base 6183544e8b42b65feaf4f674f860b1fb3b0c95eb:

  • Python-created NFC/NFD and exact duplicate-name ZIPs pass both Python testzip() and unzip -t. Public CLI extraction originally reports false reason: "integrity" under both policies; after the fix both report reason: "path", no integrity-policy retry, and no residual output.
  • Representative message: Artifact extraction path collision: "café.txt". Multiple selected occurrences share this normalized logical path; they cannot be materialized at the same destination path, even when the archive is valid. Inventory retains each occurrence.
  • Distinct names and a single decomposed name still extract with exact expected bytes under both policies.
  • A genuinely CRC-corrupt ZIP retains its existing reason: "format" / ZIP archive is malformed: Invalid CRC32 refusal under both policies.
  • New compiled CLI regression and extended duplicate-member integration regression fail on the original implementation: 2 failed, 20 passed. Both pass after the fix; inventory still retains two duplicate occurrences.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- tests/acceptance/artifactZipExtractionPaths.test.ts tests/acceptance/artifactZipCurrentDirectory.test.ts tests/boundary/filesystem/artifactZipIntegrity.test.ts tests/boundary/providers/native/artifactProvider.extraction.test.ts tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts tests/boundary/providers/native/artifactProvider.cleanupRetry.test.ts tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts — 41 passed across 7 files, including no-overwrite, cancellation, nested-content and cleanup controls.
  • npm run check:fast and npm run check — passed; lint reports zero errors and existing warnings.
  • npm run verify:test-discovery — all 1,377 files discovered exactly once.
  • git diff --check and normal pre-commit formatting/lint hooks — passed.
  • Public CLI before/after verification and independent ZIP validity checks — results above.

Compatibility, safety, and release

  • No schema, dependency, release metadata or migration changes.
  • Linux cloud execution only. No macOS/Windows run or full deterministic suite is claimed; those remain for CI. No Hopper/Ghidra/browser engine is needed for these ZIP workflows.
  • Source-owned fixtures only; no archive contents executed. No performance improvement is claimed or measured.

Author readiness

Human owner: @maxazure

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4273 **Author:** [@maxazure](https://github.com/maxazure) **Created:** 10/15/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/zip-extraction-path-collision-4271` --- ### 📝 Commits (1) - [`cbd3402`](https://github.com/morluto/rea/commit/cbd3402fa4652cfb8a7e90c6baf14e3f2d84e818) fix(artifacts): diagnose colliding extraction paths ### 📊 Changes **3 files changed** (+103 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `src/artifacts/extraction/ArtifactExtraction.ts` (+10 -3) ➕ `tests/acceptance/artifactZipExtractionPaths.test.ts` (+75 -0) 📝 `tests/boundary/filesystem/artifactZipIntegrity.test.ts` (+18 -0) </details> ### 📄 Description ## Summary Fixes #4271. Report colliding extraction paths instead of falsely claiming valid ZIP bytes failed integrity verification. ## Problem and expected behavior NFC/NFD-equivalent member names or repeated identical names can be valid ZIP entries with different bytes. Inventory retains both occurrences, but extraction's path-keyed Map silently replaced one with the other and verified the first member against the second member's digest. Both integrity policies then failed with an ineffective `record-and-continue` retry suggestion. Extraction should refuse the shared destination path without declaring the archive corrupt or overwriting either entry. ## Change and scope Check uniqueness while constructing the selected-path Map, before acquiring the materialization reader or output root. Reuse `ArtifactReaderFailure("path", ...)` with the normalized path and an explanation that multiple occurrences cannot occupy one destination. The inventory registry still permits repeated occurrences. Nested active-entry filtering, exact byte/hash verification, cancellation, cleanup and exclusive output creation are unchanged. No suffixing, overwrite option or new schema is introduced. ## Contract and boundary impact - Semantic owner: selected-occurrence lookup in `ArtifactExtraction.materializeSelection`. - Contribution scope: ordinary static archive-extraction correction; advance maintainer scope decision not applicable. - CLI/MCP: existing error contract; collisions now report `reason: "path"` with a useful message. - Provider/platform: no new reader, format or platform behavior. - Evidence/provenance: inventory preserves every occurrence and its original content identity. - Authority/cleanup: existing local reads and extraction writes only; collision refusal occurs before output acquisition. - Generated metadata/package/installation: none. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [x] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. The checked write effect is the existing extraction of files; this change adds no execution or network authority. ## Evidence and regression coverage Executed on Linux with Node 24.18.0 / npm 11.16.0, base `6183544e8b42b65feaf4f674f860b1fb3b0c95eb`: - Python-created NFC/NFD and exact duplicate-name ZIPs pass both Python `testzip()` and `unzip -t`. Public CLI extraction originally reports false `reason: "integrity"` under both policies; after the fix both report `reason: "path"`, no integrity-policy retry, and no residual output. - Representative message: `Artifact extraction path collision: "café.txt". Multiple selected occurrences share this normalized logical path; they cannot be materialized at the same destination path, even when the archive is valid. Inventory retains each occurrence.` - Distinct names and a single decomposed name still extract with exact expected bytes under both policies. - A genuinely CRC-corrupt ZIP retains its existing `reason: "format"` / `ZIP archive is malformed: Invalid CRC32` refusal under both policies. - New compiled CLI regression and extended duplicate-member integration regression fail on the original implementation: 2 failed, 20 passed. Both pass after the fix; inventory still retains two duplicate occurrences. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- tests/acceptance/artifactZipExtractionPaths.test.ts tests/acceptance/artifactZipCurrentDirectory.test.ts tests/boundary/filesystem/artifactZipIntegrity.test.ts tests/boundary/providers/native/artifactProvider.extraction.test.ts tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts tests/boundary/providers/native/artifactProvider.cleanupRetry.test.ts tests/boundary/providers/native/artifactProvider.archiveSafety.test.ts` — 41 passed across 7 files, including no-overwrite, cancellation, nested-content and cleanup controls. - `npm run check:fast` and `npm run check` — passed; lint reports zero errors and existing warnings. - `npm run verify:test-discovery` — all 1,377 files discovered exactly once. - `git diff --check` and normal pre-commit formatting/lint hooks — passed. - Public CLI before/after verification and independent ZIP validity checks — results above. ## Compatibility, safety, and release - No schema, dependency, release metadata or migration changes. - Linux cloud execution only. No macOS/Windows run or full deterministic suite is claimed; those remain for CI. No Hopper/Ghidra/browser engine is needed for these ZIP workflows. - Source-owned fixtures only; no archive contents executed. No performance improvement is claimed or measured. ## Author readiness Human owner: @maxazure - [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [ ] I declare this PR ready for initial maintainer review. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:12:16 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6151
No description provided.