[PR #6152] [MERGED] fix(artifacts): refuse thin Mach-O extraction before inventory #7166

Closed
opened 2026-11-01 21:45:52 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/6152
Author: @viniciusdsandrade
Created: 10/21/2026
Status: ✅ Merged
Merged: 10/22/2026
Merged by: @morluto

Base: main ← Head: fix/6098-thin-macho-extraction-reader


📝 Commits (4)

  • 65ff901 fix(artifacts): refuse thin Mach-O extraction before inventory
  • 7799905 test(artifacts): cover the thin Mach-O extraction refusal
  • f6e3128 fix(artifacts): keep embedded packs behind a thin Mach-O extractable
  • acd3054 test(artifacts): cover a thin Mach-O PyInstaller onefile extraction

📊 Changes

2 files changed (+83 additions, -6 deletions)

View changed files

📝 src/artifacts/extraction/ArtifactExtraction.ts (+7 -6)
📝 tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts (+76 -0)

📄 Description

Summary

Fixes #6098

extract_artifact disagreed with its own inventory for a thin Mach-O. inspect_artifact lists a thin Mach-O as one root occurrence with no child entries, while extraction routed the same file through the universal slice reader, which yields a slices/<arch> entry that the root-only inventory never recorded. The writer then failed with integrity_mismatch (Regular artifact entry is missing from inventory: slices/arm64e) and recommended an integrity policy retry that returns the same failure.

Extraction now admits only mach-o-universal to the slice reader. A thin mach-o target receives the typed refusal other formats without an extraction reader already get, AnalysisUnsupportedTargetError with reason Artifact format has no extraction reader: mach-o, before any inventory or output work.

Problem and expected behavior

  • Smallest trigger: rea extract-artifact <thin Mach-O> --json (the issue used lipo /bin/echo -thin arm64e).
  • Violated invariant: extraction must materialize the inventory it scanned. src/artifacts/inventory/reader.ts selects MachOSliceArtifactReader only for mach-o-universal, but requireExtractableFormat admitted both Mach-O formats through isMachOFormat, and createReader fell through to the slice reader for the thin file.
  • Expected: a truthful typed refusal that does not report unchanged source bytes as corrupt and does not recommend --integrity-policy record-and-continue.

Change and scope

  • src/artifacts/extraction/ArtifactExtraction.ts: requireExtractableFormat treats only mach-o-universal as having an extraction reader, matching the inventory reader selection. A thin mach-o takes the same embedded-pack probe as ELF and PE: a detected PyInstaller CArchive or Godot PCK proceeds to inventory, whose pyinstaller or godot-pck root selects that reader before the Mach-O fallback, so onefile executables with a thin Mach-O bootloader stay extractable. The now unused isMachOFormat import is removed.
  • tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts: a thin arm64e Mach-O image is refused with the typed unsupported-target error and no output directory is created; a thin Mach-O bootloader followed by a CArchive still extracts its hello member.
  • Not included: merging the extraction and inventory reader selections into one function. They differ by design beyond this format: extraction refuses before inventory runs and never admits DMG, while inventory tolerates a missing reader by reporting unknown coverage. Returning an empty successful extraction for root-only artifacts is also not included, because ELF and PE targets without a packaged payload already receive this refusal and a thin Mach-O should not diverge from them.

Follow the contribution scope.
Scope decision: not applicable (ordinary fix within the existing artifact extraction boundary).

Contract and boundary impact

  • Semantic owner and earliest changed stage: requireExtractableFormat in src/artifacts/extraction/ArtifactExtraction.ts, before inventory scanning.
  • Contribution scope fit and linked maintainer decision: not applicable.
  • CLI and MCP/tool-catalog contract: schemas unchanged. For a thin Mach-O, extract_artifact and rea extract-artifact now return the existing unsupported-target error instead of integrity_mismatch.
  • Provider, bridge, target-format, or platform compatibility: thin Mach-O extraction on every platform; universal Mach-O extraction is unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: no false integrity contradiction is reported for an unchanged thin Mach-O.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable; the refusal happens before any reader or output owner exists.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

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

Evidence and regression coverage

  • Tests added or updated: tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts, refuses a thin Mach-O that inventory lists as root-only and keeps a thin Mach-O PyInstaller onefile extractable.
  • Base reproduction or other evidence: source-derived. On the base revision requireExtractableFormat admits mach-o through isMachOFormat and createReader falls through to MachOSliceArtifactReader, whose entries yields slices/${architecture.name} for the single architecture lipo reports (src/artifacts/MachOSliceArtifactReader.ts). writeSelectedEntries then throws the integrity failure because the root-only inventory has no such path. The issue reports that failure from the built CLI on macOS with real lipo. By reading, the refusal test would reach the slice reader on the base revision instead of the asserted AnalysisUnsupportedTargetError. The PyInstaller test passes on the base revision and guards the review finding that a refusal of every thin Mach-O would have dropped embedded packs.
  • User-visible CLI/MCP output (if applicable): a thin Mach-O now reports Artifact format has no extraction reader: mach-o, the same error shape rea extract-artifact Image.dmg already produces.
  • Remaining proof gaps: no real lipo run and no CLI replay in this PR.

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

  • No local commands were run; verification is source-derived and CI executes the test suite. The repository pre-commit hook formatted and linted the staged files without changes.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. A thin Mach-O never extracted successfully; only the failure category changes from integrity_mismatch to the unsupported-target refusal.
  • Real Hopper/Ghidra, browser, or OS coverage: none run.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no change to process ownership or output handling.

Author readiness

Human owner: @viniciusdsandrade

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

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 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/6152 **Author:** [@viniciusdsandrade](https://github.com/viniciusdsandrade) **Created:** 10/21/2026 **Status:** ✅ Merged **Merged:** 10/22/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/6098-thin-macho-extraction-reader` --- ### 📝 Commits (4) - [`65ff901`](https://github.com/morluto/rea/commit/65ff90122f8d755feca7b9d210373c4a941e7cb3) fix(artifacts): refuse thin Mach-O extraction before inventory - [`7799905`](https://github.com/morluto/rea/commit/7799905fead7ea0567394cd79315f3072eaa39f7) test(artifacts): cover the thin Mach-O extraction refusal - [`f6e3128`](https://github.com/morluto/rea/commit/f6e3128e97344f77a260cf6ececd134b0845b52f) fix(artifacts): keep embedded packs behind a thin Mach-O extractable - [`acd3054`](https://github.com/morluto/rea/commit/acd3054d3c799dbe955671b87b23ab8672c49d7b) test(artifacts): cover a thin Mach-O PyInstaller onefile extraction ### 📊 Changes **2 files changed** (+83 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `src/artifacts/extraction/ArtifactExtraction.ts` (+7 -6) 📝 `tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts` (+76 -0) </details> ### 📄 Description ## Summary Fixes #6098 `extract_artifact` disagreed with its own inventory for a thin Mach-O. `inspect_artifact` lists a thin Mach-O as one root occurrence with no child entries, while extraction routed the same file through the universal slice reader, which yields a `slices/<arch>` entry that the root-only inventory never recorded. The writer then failed with `integrity_mismatch` (`Regular artifact entry is missing from inventory: slices/arm64e`) and recommended an integrity policy retry that returns the same failure. Extraction now admits only `mach-o-universal` to the slice reader. A thin `mach-o` target receives the typed refusal other formats without an extraction reader already get, `AnalysisUnsupportedTargetError` with reason `Artifact format has no extraction reader: mach-o`, before any inventory or output work. ## Problem and expected behavior - Smallest trigger: `rea extract-artifact <thin Mach-O> --json` (the issue used `lipo /bin/echo -thin arm64e`). - Violated invariant: extraction must materialize the inventory it scanned. `src/artifacts/inventory/reader.ts` selects `MachOSliceArtifactReader` only for `mach-o-universal`, but `requireExtractableFormat` admitted both Mach-O formats through `isMachOFormat`, and `createReader` fell through to the slice reader for the thin file. - Expected: a truthful typed refusal that does not report unchanged source bytes as corrupt and does not recommend `--integrity-policy record-and-continue`. ## Change and scope - `src/artifacts/extraction/ArtifactExtraction.ts`: `requireExtractableFormat` treats only `mach-o-universal` as having an extraction reader, matching the inventory reader selection. A thin `mach-o` takes the same embedded-pack probe as ELF and PE: a detected PyInstaller CArchive or Godot PCK proceeds to inventory, whose `pyinstaller` or `godot-pck` root selects that reader before the Mach-O fallback, so onefile executables with a thin Mach-O bootloader stay extractable. The now unused `isMachOFormat` import is removed. - `tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts`: a thin arm64e Mach-O image is refused with the typed unsupported-target error and no output directory is created; a thin Mach-O bootloader followed by a CArchive still extracts its `hello` member. - Not included: merging the extraction and inventory reader selections into one function. They differ by design beyond this format: extraction refuses before inventory runs and never admits DMG, while inventory tolerates a missing reader by reporting unknown coverage. Returning an empty successful extraction for root-only artifacts is also not included, because ELF and PE targets without a packaged payload already receive this refusal and a thin Mach-O should not diverge from them. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Scope decision: not applicable (ordinary fix within the existing artifact extraction boundary). ## Contract and boundary impact - Semantic owner and earliest changed stage: `requireExtractableFormat` in `src/artifacts/extraction/ArtifactExtraction.ts`, before inventory scanning. - Contribution scope fit and linked maintainer decision: not applicable. - CLI and MCP/tool-catalog contract: schemas unchanged. For a thin Mach-O, `extract_artifact` and `rea extract-artifact` now return the existing unsupported-target error instead of `integrity_mismatch`. - Provider, bridge, target-format, or platform compatibility: thin Mach-O extraction on every platform; universal Mach-O extraction is unchanged. - Evidence, artifact, provenance, or reconstruction contract: no false integrity contradiction is reported for an unchanged thin Mach-O. - Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable; the refusal happens before any reader or output owner exists. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] 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. ## Evidence and regression coverage - Tests added or updated: `tests/boundary/providers/native/artifactProvider.extractionFormat.test.ts`, `refuses a thin Mach-O that inventory lists as root-only` and `keeps a thin Mach-O PyInstaller onefile extractable`. - Base reproduction or other evidence: source-derived. On the base revision `requireExtractableFormat` admits `mach-o` through `isMachOFormat` and `createReader` falls through to `MachOSliceArtifactReader`, whose `entries` yields `slices/${architecture.name}` for the single architecture lipo reports (`src/artifacts/MachOSliceArtifactReader.ts`). `writeSelectedEntries` then throws the integrity failure because the root-only inventory has no such path. The issue reports that failure from the built CLI on macOS with real lipo. By reading, the refusal test would reach the slice reader on the base revision instead of the asserted `AnalysisUnsupportedTargetError`. The PyInstaller test passes on the base revision and guards the review finding that a refusal of every thin Mach-O would have dropped embedded packs. - User-visible CLI/MCP output (if applicable): a thin Mach-O now reports `Artifact format has no extraction reader: mach-o`, the same error shape `rea extract-artifact Image.dmg` already produces. - Remaining proof gaps: no real lipo run and no CLI replay in this PR. 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 - No local commands were run; verification is source-derived and CI executes the test suite. The repository pre-commit hook formatted and linted the staged files without changes. ## Compatibility, safety, and release - Breaking changes or migration steps: none. A thin Mach-O never extracted successfully; only the failure category changes from `integrity_mismatch` to the unsupported-target refusal. - Real Hopper/Ghidra, browser, or OS coverage: none run. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no change to process ownership or output handling. ## Author readiness Human owner: @viniciusdsandrade - [x] 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. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:45:52 +00:00
bsakaguchi changed title from [PR #6152] [MERGED] fix(artifacts): refuse thin Mach-O extraction before inventory to [PR #6152] fix(artifacts): refuse thin Mach-O extraction before inventory 2026-11-04 16:32:31 +00:00
bsakaguchi changed title from [PR #6152] fix(artifacts): refuse thin Mach-O extraction before inventory to [PR #6152] [MERGED] fix(artifacts): refuse thin Mach-O extraction before inventory 2026-11-04 17:28:36 +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#7166
No description provided.