[PR #902] [CLOSED] feat(artifacts): let extraction and JavaScript analysis record integrity mismatches #2334

Closed
opened 2026-10-16 21:17:25 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/902
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ❌ Closed

Base: main ← Head: feat/artifact-integrity-policy


📝 Commits (10+)

  • 69af884 feat(artifacts): let extraction and JavaScript analysis record integrity mismatches
  • 92b373e Merge remote-tracking branch 'origin/main' into feat/artifact-integrity-policy
  • 5aafaa0 fix(artifacts): carry recorded integrity contradictions into application workflows
  • 883db73 chore: merge main and resolve PR #902 conflicts
  • 464115f chore: merge main and retain artifact integrity evidence
  • 40a33d3 chore: merge main and regenerate combined catalog metadata
  • be011ea Merge origin/main into feat/artifact-integrity-policy
  • 22f4dc1 Merge origin/main into feat/artifact-integrity-policy
  • 467f684 Merge remote-tracking branch 'origin/main' into feat/artifact-integrity-policy
  • ac392da fix(artifacts): keep integrity policy and extraction claims aligned (#1083)

📊 Changes

27 files changed (+457 additions, -36 deletions)

View changed files

📝 .github/workflows/ci.yml (+2 -2)
📝 docs/mcp-contracts.md (+12 -4)
📝 package.json (+2 -1)
📝 src/application/javascript/JavaScriptApplicationEvidence.ts (+1 -0)
📝 src/application/javascript/JavaScriptApplicationService.test.ts (+111 -1)
📝 src/application/javascript/JavaScriptApplicationService.ts (+1 -0)
📝 src/application/javascript/JavaScriptArtifactGraphBuilder.ts (+16 -0)
📝 src/application/javascript/JavaScriptArtifactReconstruction.ts (+12 -2)
📝 src/application/javascript/JavaScriptArtifactReconstructionInput.ts (+3 -0)
📝 src/application/javascript/JavaScriptSemanticGraphBuilder.ts (+17 -2)
📝 src/artifacts/ArtifactProvider.ts (+3 -0)
📝 src/artifacts/extraction/ArtifactExtraction.ts (+27 -0)
📝 src/cli/artifactCommands.ts (+11 -9)
📝 src/cli/coreBinaryCommands.ts (+11 -2)
📝 src/cli/electronCommands.ts (+5 -1)
📝 src/cliObservationOptions.ts (+4 -0)
📝 src/contracts/artifactToolContracts.ts (+7 -3)
📝 src/contracts/javascript/electronToolContracts.ts (+1 -1)
📝 src/domain/analysisErrorPresentation.ts (+14 -1)
📝 src/domain/artifactGraph.ts (+7 -1)

...and 7 more files

📄 Description

Summary

On macOS, Electron packagers record each unpacked file's SHA-256 in the ASAR header, then code-sign the bundle. Signing rewrites unpacked native modules (*.node in app.asar.unpacked), so their observed bytes no longer match the declared integrity. REA's checks correctly report this. Only inspect_artifact offered integrity_policy, though, so the other two tools that scan an artifact inventory could not run on such apps at all:

App (Contents/Resources/app.asar) inspect_artifact (default) extract_artifact analyze_javascript_application
Claude, ChatGPT, Notion, Obsidian, Tabby artifact_integrity_mismatch on a signed .node always fails always fails
Discord ok ok ok

The remediation said Artifact is invalid or has changed. Get a fresh copy and try again., which cannot help for a signed installation.

Changes:

  • extract_artifact / rea extract-artifact --integrity-policy accept the existing integrity_policy (fail by default, or record-and-continue). With record-and-continue, contradicted files are extracted with their observed bytes, and the result gains integrity_contradictions (the inventory's existing contradiction records) plus a limitation.
  • analyze_javascript_application / rea analyze-javascript-application --integrity-policy accept the same option. Contradicted files are read as observed, and contradicted nested archives remain unexpanded, as in the inventory. The result gains integrity_contradictions. The application graph becomes partial, and its limitations (inherited by the result and the semantic graph) list every contradicted path. Workflows built on that Evidence (compare_application_versions, compare_source_to_bundle, trace_application_feature, compare_javascript_export_shapes) derive their coverage from the graph, so they report partial coverage instead of complete-within-inputs. Evidence parameters record the policy.
  • Remediation. For artifact_integrity_mismatch from these three operations, the error now says: Artifact bytes contradict their declared integrity. If that is expected, such as an unpacked native module signed after packaging, rerun with the record-and-continue integrity policy (integrity_policy in MCP, --integrity-policy on the CLI) to keep the mismatch as an untrusted contradiction; otherwise get a fresh copy. Other operations keep the previous message.

fail stays the default everywhere, so existing calls behave as before. The policy schema moved to domain/artifactGraph.ts so the JavaScript input can share it. Both output schemas add integrity_contradictions with a [] default. The catalog, SKILL.md, conformance manifest, and docs/mcp-contracts.md are updated.

Question for review: for analyze_javascript_application in particular, would you rather default to record-and-continue? Most signed macOS Electron apps hit this, and the contradictions are reported either way. I kept fail to match inspect_artifact.

MCP example

{"name": "extract_artifact", "arguments": {"integrity_policy": "record-and-continue"}}

returns, alongside the existing fields:

{
  "integrity_contradictions": [{"logical_path": "main.js", "unpacked": true, "trust": "observed-untrusted", "declared_sha256": "…", "observed_sha256": "…", "...": "…"}],
  "limitations": ["…", "1 extracted file(s) contradict declared integrity; their observed bytes were written and are untrusted."]
}

Verification

  • tests/boundary/mcp/artifactMcp.test.ts: a new case extracts an ASAR whose unpacked main.js changed after packaging. The default fails with the new remediation. record-and-continue writes the observed bytes and the untouched sibling, and reports the contradiction and limitation. The two existing mismatch cases now assert the new message.
  • src/application/javascript/JavaScriptApplicationService.test.ts: an ASAR whose unpacked addon.node changed after packaging fails by default. With record-and-continue it is analyzed and returns the contradiction with a partial graph. It records the policy in Evidence parameters, and a version comparison against a clean analysis reports partial coverage.
  • Real apps through the built CLI:
    • analyze-javascript-application on Obsidian's app.asar with --integrity-policy record-and-continue: 147 graph nodes, 5 contradictions (all signed btime/get-fonts .node files).
    • extract-artifact on Tabby's app.asar: 3504 files and 24 contradictions. The output was removed afterwards.
    • Probes: a tampered unpacked JS file is analyzed as observed and listed as a contradiction, and a tampered unpacked nested ASAR is not expanded.
  • npm run check:changed (62 files, 578 tests), npm run docs:check, and the 30 boundary/acceptance/composition files that exercise these tools (119 tests) pass.

No Hopper/Ghidra/IDA verification is needed.

🤖 Generated with Claude Code


🔄 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/902 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feat/artifact-integrity-policy` --- ### 📝 Commits (10+) - [`69af884`](https://github.com/morluto/rea/commit/69af8846662c66fd696e7a6ba99aaa41c4a996f9) feat(artifacts): let extraction and JavaScript analysis record integrity mismatches - [`92b373e`](https://github.com/morluto/rea/commit/92b373e660b7704fb1dea13f92767d51e664e43a) Merge remote-tracking branch 'origin/main' into feat/artifact-integrity-policy - [`5aafaa0`](https://github.com/morluto/rea/commit/5aafaa00ecd76065cc40c7adf4d66ae8d9d3c764) fix(artifacts): carry recorded integrity contradictions into application workflows - [`883db73`](https://github.com/morluto/rea/commit/883db73b3fae8c2865cff4f7c5f1319fc0f0eeb7) chore: merge main and resolve PR #902 conflicts - [`464115f`](https://github.com/morluto/rea/commit/464115f2ec2e39e36863966ed45f1019c24d0cca) chore: merge main and retain artifact integrity evidence - [`40a33d3`](https://github.com/morluto/rea/commit/40a33d3d3ba0ddca8ff7838755903a6ae56f048a) chore: merge main and regenerate combined catalog metadata - [`be011ea`](https://github.com/morluto/rea/commit/be011ea3ba3833b2187633cb9fd346d1f71ff08c) Merge origin/main into feat/artifact-integrity-policy - [`22f4dc1`](https://github.com/morluto/rea/commit/22f4dc101a2a01a6cb46d729ecb70ec8a2bd1081) Merge origin/main into feat/artifact-integrity-policy - [`467f684`](https://github.com/morluto/rea/commit/467f68412e6e528703b44650082a9a613872d522) Merge remote-tracking branch 'origin/main' into feat/artifact-integrity-policy - [`ac392da`](https://github.com/morluto/rea/commit/ac392da33214ff6ac438d50af19cebd8da2295a0) fix(artifacts): keep integrity policy and extraction claims aligned (#1083) ### 📊 Changes **27 files changed** (+457 additions, -36 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/ci.yml` (+2 -2) 📝 `docs/mcp-contracts.md` (+12 -4) 📝 `package.json` (+2 -1) 📝 `src/application/javascript/JavaScriptApplicationEvidence.ts` (+1 -0) 📝 `src/application/javascript/JavaScriptApplicationService.test.ts` (+111 -1) 📝 `src/application/javascript/JavaScriptApplicationService.ts` (+1 -0) 📝 `src/application/javascript/JavaScriptArtifactGraphBuilder.ts` (+16 -0) 📝 `src/application/javascript/JavaScriptArtifactReconstruction.ts` (+12 -2) 📝 `src/application/javascript/JavaScriptArtifactReconstructionInput.ts` (+3 -0) 📝 `src/application/javascript/JavaScriptSemanticGraphBuilder.ts` (+17 -2) 📝 `src/artifacts/ArtifactProvider.ts` (+3 -0) 📝 `src/artifacts/extraction/ArtifactExtraction.ts` (+27 -0) 📝 `src/cli/artifactCommands.ts` (+11 -9) 📝 `src/cli/coreBinaryCommands.ts` (+11 -2) 📝 `src/cli/electronCommands.ts` (+5 -1) 📝 `src/cliObservationOptions.ts` (+4 -0) 📝 `src/contracts/artifactToolContracts.ts` (+7 -3) 📝 `src/contracts/javascript/electronToolContracts.ts` (+1 -1) 📝 `src/domain/analysisErrorPresentation.ts` (+14 -1) 📝 `src/domain/artifactGraph.ts` (+7 -1) _...and 7 more files_ </details> ### 📄 Description ## Summary On macOS, Electron packagers record each unpacked file's SHA-256 in the ASAR header, then code-sign the bundle. Signing rewrites unpacked native modules (`*.node` in `app.asar.unpacked`), so their observed bytes no longer match the declared integrity. REA's checks correctly report this. Only `inspect_artifact` offered `integrity_policy`, though, so the other two tools that scan an artifact inventory could not run on such apps at all: | App (`Contents/Resources/app.asar`) | `inspect_artifact` (default) | `extract_artifact` | `analyze_javascript_application` | | --- | --- | --- | --- | | Claude, ChatGPT, Notion, Obsidian, Tabby | `artifact_integrity_mismatch` on a signed `.node` | **always fails** | **always fails** | | Discord | ok | ok | ok | The remediation said `Artifact is invalid or has changed. Get a fresh copy and try again.`, which cannot help for a signed installation. Changes: - **`extract_artifact` / `rea extract-artifact --integrity-policy`** accept the existing `integrity_policy` (`fail` by default, or `record-and-continue`). With `record-and-continue`, contradicted files are extracted with their observed bytes, and the result gains `integrity_contradictions` (the inventory's existing contradiction records) plus a limitation. - **`analyze_javascript_application` / `rea analyze-javascript-application --integrity-policy`** accept the same option. Contradicted files are read as observed, and contradicted nested archives remain unexpanded, as in the inventory. The result gains `integrity_contradictions`. The application graph becomes `partial`, and its limitations (inherited by the result and the semantic graph) list every contradicted path. Workflows built on that Evidence (`compare_application_versions`, `compare_source_to_bundle`, `trace_application_feature`, `compare_javascript_export_shapes`) derive their coverage from the graph, so they report partial coverage instead of `complete-within-inputs`. Evidence parameters record the policy. - **Remediation.** For `artifact_integrity_mismatch` from these three operations, the error now says: `Artifact bytes contradict their declared integrity. If that is expected, such as an unpacked native module signed after packaging, rerun with the record-and-continue integrity policy (integrity_policy in MCP, --integrity-policy on the CLI) to keep the mismatch as an untrusted contradiction; otherwise get a fresh copy.` Other operations keep the previous message. `fail` stays the default everywhere, so existing calls behave as before. The policy schema moved to `domain/artifactGraph.ts` so the JavaScript input can share it. Both output schemas add `integrity_contradictions` with a `[]` default. The catalog, SKILL.md, conformance manifest, and `docs/mcp-contracts.md` are updated. **Question for review:** for `analyze_javascript_application` in particular, would you rather default to `record-and-continue`? Most signed macOS Electron apps hit this, and the contradictions are reported either way. I kept `fail` to match `inspect_artifact`. ### MCP example ```json {"name": "extract_artifact", "arguments": {"integrity_policy": "record-and-continue"}} ``` returns, alongside the existing fields: ```json { "integrity_contradictions": [{"logical_path": "main.js", "unpacked": true, "trust": "observed-untrusted", "declared_sha256": "…", "observed_sha256": "…", "...": "…"}], "limitations": ["…", "1 extracted file(s) contradict declared integrity; their observed bytes were written and are untrusted."] } ``` ## Verification - `tests/boundary/mcp/artifactMcp.test.ts`: a new case extracts an ASAR whose unpacked `main.js` changed after packaging. The default fails with the new remediation. `record-and-continue` writes the observed bytes and the untouched sibling, and reports the contradiction and limitation. The two existing mismatch cases now assert the new message. - `src/application/javascript/JavaScriptApplicationService.test.ts`: an ASAR whose unpacked `addon.node` changed after packaging fails by default. With `record-and-continue` it is analyzed and returns the contradiction with a partial graph. It records the policy in Evidence parameters, and a version comparison against a clean analysis reports partial coverage. - Real apps through the built CLI: - `analyze-javascript-application` on Obsidian's `app.asar` with `--integrity-policy record-and-continue`: 147 graph nodes, 5 contradictions (all signed `btime`/`get-fonts` `.node` files). - `extract-artifact` on Tabby's `app.asar`: 3504 files and 24 contradictions. The output was removed afterwards. - Probes: a tampered unpacked JS file is analyzed as observed and listed as a contradiction, and a tampered unpacked nested ASAR is not expanded. - `npm run check:changed` (62 files, 578 tests), `npm run docs:check`, and the 30 boundary/acceptance/composition files that exercise these tools (119 tests) pass. No Hopper/Ghidra/IDA verification is needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-16 21:17:25 +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#2334
No description provided.