[PR #2493] [MERGED] fix(artifacts): read every ZIP package family extraction is asked to open #5271

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2493
Author: @XiaoBinGan
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/extraction-zip-format-owner


📝 Commits (1)

  • 65543e8 fix(artifacts): read every ZIP package family extraction is asked to open

📊 Changes

2 files changed (+32 additions, -9 deletions)

View changed files

📝 src/artifacts/extraction/ArtifactExtraction.ts (+3 -9)
📝 tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts (+29 -0)

📄 Description

extract_artifact carried its own copy of the ZIP package format list while inventory_artifact used the domain owner, so the two consumers of one owned value disagreed about which package families are ZIP-backed.

Summary

src/domain/zipPackageFormat.ts declares eight ZIP-backed families (zip, ipa, apk, msix, appx, hap, hsp, app-pack) and exposes isZipPackageFormat as the owner. ArtifactExtraction restated that list with five members and used it for two different decisions:

  • as the guard in requireExtractableFormat, whose own doc comment says "Refuse a format without an extraction reader" — so a .hap, .hsp or App Pack target was refused before any work started;
  • as the selector in createReader, where the branch immediately after constructs new ZipArtifactReader(path, format) for exactly those formats.

The refused formats are the ones that branch exists to read, and inventory_artifact classifies the same path through ZipPackageFormat and reads it. BinaryTarget["format"] is ZipPackageFormat | "asar" | "dmg" | "pkg", and ArtifactProvider passes target.format straight through as inputFormat, so the stale list was the only thing rejecting them.

Problem and expected behavior

Same one-member ZIP bytes, three targets:

Payload.hap   -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: hap
Payload.hsp   -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: hsp
Payload.app   -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: app-pack
Payload.zip   -> extracted, member present

inventory_artifact accepts the .hap input, because it classifies through isZipPackageFormat/zipPackageFormatForPath. Expected: every family the owner declares ZIP-backed is extractable, matching inventory.

Change and scope

ArtifactExtraction imports isZipPackageFormat from ../../domain/zipPackageFormat.js and deletes the local ZIP_FORMATS list, its predicate, and the now-unused local type. Both call sites use the owner. As a side effect the reader branch narrows to ZipPackageFormat, which is what ZipArtifactReader's constructor already requires, instead of the stale five-member union.

No new abstraction, alias or wrapper is introduced: the duplicated representation is removed rather than reconciled. dmg, pkg and unrecognised formats keep failing closed in the guard, which is what makes the removal safe — the guard still refuses anything without a reader.

Contract and boundary impact

  • Semantic owner and earliest changed stage: ZIP package family membership in the artifact extraction reader selection. Inventory, the hardened ZIP reader, safe-output extraction and the manifest are untouched.
  • CLI and MCP/tool-catalog contract: no tool name, command, input field, output schema or error code changes. extract_artifact accepts three target formats it previously refused, which is the intended contract per the owner and per inventory_artifact; all other formats behave exactly as before.
  • Provider, target-format or platform compatibility: extends extraction to HarmonyOS .hap/.hsp and App Pack targets that inventory already handled. No platform-conditional code.
  • Evidence, artifact, provenance or reconstruction contract: the ZIP reader and its central-directory, integrity and occurrence logic are unchanged, so extracted bytes, digests and occurrence identities are those the reader already produced for zip. Cases that previously failed produced no evidence at all.
  • Process execution, authorization, cleanup or containment impact: none. Extraction still writes only inside the caller's output_root through SafeOutputTree.
  • Generated metadata, package or installation impact: none.

Evidence and regression coverage

A new it.each(["zip", "hap", "hsp", "app-pack"]) case in artifactProvider.extractionFormat.test.ts writes real ZIP bytes with the existing writeOrderedZip fixture, runs extract_artifact through ArtifactProvider for each family, and asserts the extracted member is present. The zip case is the control.

Reverting only the source change fails exactly the three new families with Artifact format has no extraction reader: hap|hsp|app-pack while the control passes, which is what establishes the defect and the fix. Verification on macOS 26.5.1 arm64 with Node.js 24.18.0 and npm 11.16.0, based on bb54082:

  • npm run test:focused over the five artifactProvider.* boundary suites plus artifactZipIntegrity and zipDetection: 41 passed, 0 failed.
  • npm run check:fast: passed.
  • npm run verify:test-discovery: all 1015 test files discovered exactly once.

Unavailable coverage: no Windows or Linux run and no real .hap, .hsp or App Pack package from a device. The regression input is real ZIP bytes written by the repository's zip.js fixture, which is the same reader path every ZIP-backed family already uses; no HarmonyOS toolchain was involved.


🔄 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/2493 **Author:** [@XiaoBinGan](https://github.com/XiaoBinGan) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/extraction-zip-format-owner` --- ### 📝 Commits (1) - [`65543e8`](https://github.com/morluto/rea/commit/65543e8ed5e1e899f1fa9762dad57d6ba9fc635f) fix(artifacts): read every ZIP package family extraction is asked to open ### 📊 Changes **2 files changed** (+32 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `src/artifacts/extraction/ArtifactExtraction.ts` (+3 -9) 📝 `tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts` (+29 -0) </details> ### 📄 Description `extract_artifact` carried its own copy of the ZIP package format list while `inventory_artifact` used the domain owner, so the two consumers of one owned value disagreed about which package families are ZIP-backed. ## Summary `src/domain/zipPackageFormat.ts` declares eight ZIP-backed families (`zip`, `ipa`, `apk`, `msix`, `appx`, `hap`, `hsp`, `app-pack`) and exposes `isZipPackageFormat` as the owner. [`ArtifactExtraction`](https://github.com/morluto/rea/blob/bb54082/src/artifacts/extraction/ArtifactExtraction.ts) restated that list with five members and used it for two different decisions: - as the guard in `requireExtractableFormat`, whose own doc comment says "Refuse a format without an extraction reader" — so a `.hap`, `.hsp` or App Pack target was refused before any work started; - as the selector in `createReader`, where the branch immediately after constructs `new ZipArtifactReader(path, format)` for exactly those formats. The refused formats are the ones that branch exists to read, and `inventory_artifact` classifies the same path through `ZipPackageFormat` and reads it. `BinaryTarget["format"]` is `ZipPackageFormat | "asar" | "dmg" | "pkg"`, and `ArtifactProvider` passes `target.format` straight through as `inputFormat`, so the stale list was the only thing rejecting them. ## Problem and expected behavior Same one-member ZIP bytes, three targets: ``` Payload.hap -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: hap Payload.hsp -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: hsp Payload.app -> AnalysisUnsupportedTargetError: Artifact format has no extraction reader: app-pack Payload.zip -> extracted, member present ``` `inventory_artifact` accepts the `.hap` input, because it classifies through `isZipPackageFormat`/`zipPackageFormatForPath`. Expected: every family the owner declares ZIP-backed is extractable, matching inventory. ## Change and scope [`ArtifactExtraction`](https://github.com/XiaoBinGan/rea/blob/HEAD/src/artifacts/extraction/ArtifactExtraction.ts) imports `isZipPackageFormat` from `../../domain/zipPackageFormat.js` and deletes the local `ZIP_FORMATS` list, its predicate, and the now-unused local type. Both call sites use the owner. As a side effect the reader branch narrows to `ZipPackageFormat`, which is what `ZipArtifactReader`'s constructor already requires, instead of the stale five-member union. No new abstraction, alias or wrapper is introduced: the duplicated representation is removed rather than reconciled. `dmg`, `pkg` and unrecognised formats keep failing closed in the guard, which is what makes the removal safe — the guard still refuses anything without a reader. ## Contract and boundary impact - **Semantic owner and earliest changed stage:** ZIP package family membership in the artifact extraction reader selection. Inventory, the hardened ZIP reader, safe-output extraction and the manifest are untouched. - **CLI and MCP/tool-catalog contract:** no tool name, command, input field, output schema or error code changes. `extract_artifact` accepts three target formats it previously refused, which is the intended contract per the owner and per `inventory_artifact`; all other formats behave exactly as before. - **Provider, target-format or platform compatibility:** extends extraction to HarmonyOS `.hap`/`.hsp` and App Pack targets that inventory already handled. No platform-conditional code. - **Evidence, artifact, provenance or reconstruction contract:** the ZIP reader and its central-directory, integrity and occurrence logic are unchanged, so extracted bytes, digests and occurrence identities are those the reader already produced for `zip`. Cases that previously failed produced no evidence at all. - **Process execution, authorization, cleanup or containment impact:** none. Extraction still writes only inside the caller's `output_root` through `SafeOutputTree`. - **Generated metadata, package or installation impact:** none. ## Evidence and regression coverage A new `it.each(["zip", "hap", "hsp", "app-pack"])` case in [`artifactProvider.extractionFormat.test.ts`](https://github.com/XiaoBinGan/rea/blob/HEAD/tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts) writes real ZIP bytes with the existing `writeOrderedZip` fixture, runs `extract_artifact` through `ArtifactProvider` for each family, and asserts the extracted member is present. The `zip` case is the control. Reverting only the source change fails exactly the three new families with `Artifact format has no extraction reader: hap|hsp|app-pack` while the control passes, which is what establishes the defect and the fix. Verification on macOS 26.5.1 arm64 with Node.js 24.18.0 and npm 11.16.0, based on `bb54082`: - `npm run test:focused` over the five `artifactProvider.*` boundary suites plus `artifactZipIntegrity` and `zipDetection`: 41 passed, 0 failed. - `npm run check:fast`: passed. - `npm run verify:test-discovery`: all 1015 test files discovered exactly once. Unavailable coverage: no Windows or Linux run and no real `.hap`, `.hsp` or App Pack package from a device. The regression input is real ZIP bytes written by the repository's zip.js fixture, which is the same reader path every ZIP-backed family already uses; no HarmonyOS toolchain was involved. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:05:21 +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#5271
No description provided.