[PR #1400] [MERGED] fix(electron): support packaged app.asar pages and rewritten unpacked companions #2857

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

📋 Pull Request Information

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

Base: main ← Head: fix/electron-asar-signed-unpacked


📝 Commits (4)

  • b07161e fix(electron): resolve pages served from inside app.asar
  • c08130a fix(artifacts): extract and explain rewritten ASAR unpacked companions
  • f873177 fix(electron): bound ASAR headers before allocation
  • 6fea8fb Merge remote-tracking branch 'origin/main' into HEAD

📊 Changes

17 files changed (+480 additions, -41 deletions)

View changed files

📝 docs/electron-observation.md (+7 -1)
📝 docs/mcp-contracts.md (+12 -0)
📝 src/application/javascript/JavaScriptArtifactReconstructionInput.ts (+2 -1)
📝 src/artifacts/ArtifactProvider.ts (+1 -0)
📝 src/artifacts/extraction/ArtifactExtraction.ts (+32 -0)
📝 src/browser/ElectronFileScope.ts (+143 -8)
📝 src/cli/artifactCommands.ts (+11 -2)
📝 src/contracts/artifactToolContracts.ts (+10 -6)
📝 src/domain/analysisErrorPresentation.ts (+25 -9)
📝 src/domain/artifactGraph.ts (+1 -0)
➕ src/domain/artifactIntegrityPolicy.ts (+16 -0)
📝 src/domain/javascript/javascriptApplicationAnalysis.ts (+2 -1)
📝 tests/boundary/browser/cdpElectronProvider.test.ts (+47 -1)
📝 tests/boundary/filesystem/electronFileScope.test.ts (+64 -1)
📝 tests/boundary/mcp/artifactMcp.test.ts (+9 -6)
➕ tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts (+95 -0)
📝 tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts (+3 -5)

📄 Description

Summary

Using REA on a shipped macOS Electron application (Neat Reader 9.0.10, Electron 13.6.9) exposed two gaps in the packaged-app workflow:

  1. Live Electron inspection could not see any packaged page. Packaged apps load their renderer from file:///…/app.asar/… URLs. authorizedElectronFile stats the URL path on the host filesystem, which cannot resolve paths inside an archive. So list_electron_targets reported targets: [] with excluded.unsupported_url: 1, and inspect_electron_page could not select the page at all.
  2. A rewritten .asar.unpacked companion blocked extraction and gave the wrong advice. The app ships node_modules/font-list/libs/darwin/fontlist as an unpacked Mach-O that was code-signed after the ASAR header was written, so its declared SHA-256 no longer matches. 7915ee51 (feat(javascript): record Electron integrity mismatches) already lets analyze_javascript_application continue past this. extract_artifact still had no integrity_policy, though, and inspect_artifact/extract_artifact still said "Artifact is invalid or has changed. Get a fresh copy and try again." A fresh copy of a signed app has the same mismatch. Separately, extracting an archive whose unpacked companion file is missing was reported as a format failure ("not an extractable regular child file").

No issue is linked: I hit both problems while reverse-engineering a real application and am reporting them here with the fix.

Problem and expected behavior

Packaged pages

  • Smallest trigger: a CDP target whose URL is file:///Applications/App.app/Contents/Resources/app.asar/build-app/index.html?x=0#/library.
  • Violated invariant: Electron serves that URL as a regular file. REA should list and inspect it, and report a stable local path. Before this change it was excluded as unsupported_url, and so were every script, frame, and resource loaded from the archive.
  • Expected: resolve the URL the way Electron's file protocol does. The first .asar path component that is a regular file is the archive, and the remainder must name a regular file in its header (packed or unpacked). The reported path is the canonical archive path joined with that member.

Rewritten unpacked companions

  • Smallest trigger: createPackageWithOptions(src, "app.asar", { unpack: "**/*.node" }), then rewrite the unpacked .node file (as macOS signing or distro stripping does) and run extract_artifact.
  • Expected:
    • The default stays fail-closed.
    • The caller can opt into record-and-continue exactly as for inspect_artifact and analyze_javascript_application.
    • The failure names the entry, explains the likely cause, and tells the caller which operation to rerun with which option.
    • A missing companion is reported as unavailable, with its logical path.

Change and scope

Two commits:

fix(electron): resolve pages served from inside app.asar

  • src/browser/ElectronFileScope.ts: if the URL path is not a regular host file, walk its components to the first .asar component that is a regular file. Then look the remainder up in the archive header through @electron/asar's getRawHeader.
    • Only regular file entries are accepted. Directories, link entries, missing members, and empty/./.. segments are rejected.
    • Parsed headers are cached by archive identity (dev, ino, size, mtimeMs), with at most 8 entries, because one inspection authorizes every frame, script, and resource URL.
  • Every existing consumer (list_electron_targets, inspect_electron_page frames/scripts/resources/DOM, Electron workers, V8 Inspector runtime locations) goes through the same function, so all of them pick this up without further changes.

fix(artifacts): extract and explain rewritten ASAR unpacked companions

  • extract_artifact accepts integrity_policy (MCP input and rea extract-artifact --integrity-policy). With record-and-continue it writes the observed bytes. It returns the extracted files' canonical integrity_contradictions records (the same schema the JavaScript application result uses), plus one summary limitation.
  • analysisErrorPresentation: this generalizes the JavaScript-only message added in 7915ee51 to every operation that accepts integrity_policy (inventory_artifact/inspect_artifact, extract_artifact, analyze_javascript_application), naming the operation to rerun.
    • For an unpacked entry, the message names the logical path and explains that packaging tools commonly sign or strip unpacked native binaries after writing the archive.
    • The non-unpacked message for analyze_javascript_application is unchanged.
  • Extraction: an active unpacked entry with no companion bytes (artifact_id: null, hash_status: "unavailable") now fails as unavailable with artifactDetails (logical_path, unpacked: true) instead of format. The message tells the caller to keep the .unpacked directory beside the archive.
  • src/domain/artifactIntegrityPolicy.ts: one shared integrity_policy schema now replaces the three duplicated inline enums, and the new extraction input uses it too.

Intentionally not included:

  • Unpacked entries are not exempted from integrity checks. AsarArtifactReader documents that unpacked is not an integrity exemption, and existing tests rely on the fail-closed default. The caller still chooses record-and-continue.
  • No repacking or mutation support. REA stays read-only.
  • The size of analyze_javascript_application output is not addressed. For this app it was about 830 MB of JSON; that deserves its own issue.

Contract and boundary impact

  • Semantic owner and earliest changed stage: ElectronFileScope (URL authorization, before any Electron observation); ArtifactExtraction (inventory selection and result); analysisErrorPresentation (error projection).
  • CLI and MCP/tool-catalog contract: additive. extract_artifact gains the optional input integrity_policy (default fail) and the result field integrity_contradictions (an empty array when there is no contradiction). rea extract-artifact gains --integrity-policy. The extract_artifact description is updated. Existing calls are unchanged.
  • Provider, bridge, target-format, or platform compatibility: the Electron CDP provider now accepts file:// URLs inside regular .asar files on all hosts. Paths are built with the host path module, and fileURLToPath still owns URL→path conversion.
  • Evidence, artifact, provenance, or reconstruction contract:
    • Continued extraction mismatches are reported with the existing integrity_contradictions schema (trust: "observed-untrusted") and are scoped to the extracted occurrences.
    • Electron file_path values for packaged pages are <realpath(archive)>/<member>. They identify the member and are never opened as host files.
  • Process execution, authorization, cleanup, or containment impact: URL authorization now admits regular-file members of a regular .asar file. It still rejects remote hosts, credentials, encoded separators, archive directories and links, and unknown members. Extraction containment and cleanup are unchanged. A failed extraction still leaves no output root (covered by the existing completeness test).
  • Generated metadata, package, or installation impact: none committed. npm run docs:check passes against the changed contracts.

Evidence and regression coverage

  • Tests added or updated:

    • tests/boundary/filesystem/electronFileScope.test.ts: packed, nested, query/hash-bearing, and unpacked ASAR members resolve. Archive directories, missing members, and a non-ASAR file used as a parent are rejected.
    • tests/boundary/browser/cdpElectronProvider.test.ts: listTargets and inspectPage against a fake CDP endpoint whose page and script live inside a real app.asar.
    • tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts (MCP, reuses createStrippedAsarAddon):
      • Strict extraction fails with the unpacked-entry message, which names extract_artifact.
      • record-and-continue extracts the observed bytes and returns integrity_contradictions plus the summary limitation.
      • A missing companion fails as unavailable with the logical path.
    • tests/boundary/mcp/artifactMcp.test.ts: the embedded/unpacked inspect_artifact integrity messages are updated.
    • tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts: the active-member refusal is now asserted as unavailable with logical_path/unpacked instead of format. Refusal and no output root are still asserted. The direct materializeArtifactInventory call passes integrityPolicy: "fail".
  • Base reproduction:

    • With src/ reverted to origin/main, both new extraction MCP tests fail.
    • With ElectronFileScope.ts reverted to the base, the new ASAR file-scope test fails.
    • On the real app, base list-electron-targets returned "targets": [], "excluded": {"unsupported_url": 1, "non_page": 0}.
  • User-visible CLI/MCP output (paths shortened):

    // rea list-electron-targets http://127.0.0.1:9333  (after)
    {"targets":[{"target_id":"CDDBB0EA…","file_path":"…/NeatReader.app/Contents/Resources/app.asar/build-app/index.html"}],
     "excluded":{"unsupported_url":0,"non_page":0}}
    
    // rea inspect-electron-page http://127.0.0.1:9333 <target>  (after)
    {"target":"…/app.asar/build-app/index.html","scripts":11,"resources":6,"dom_nodes":460}
    
    // rea extract-artifact …/app.asar  (default, after)
    {"code":"artifact_integrity_mismatch",
     "message":"Declared ASAR integrity for unpacked entry node_modules/font-list/libs/darwin/fontlist contradicts its companion file; packaging tools commonly sign or strip unpacked native binaries after writing the archive. If expected, rerun extract_artifact with integrity_policy=record-and-continue (CLI: --integrity-policy record-and-continue) to retain observed bytes as untrusted; otherwise get a fresh copy."}
    
    // rea extract-artifact …/app.asar --integrity-policy record-and-continue  (after)
    {"artifacts":5321,
     "integrity_contradictions":[{"logical_path":"node_modules/font-list/libs/darwin/fontlist","unpacked":true,"trust":"observed-untrusted"}],
     "limitations":["…","1 extracted file(s) contradict declared integrity metadata; their bytes are observed-untrusted."]}
    
  • Remaining proof gaps:

    • The live Electron check used one application, Electron 13.6.9 on macOS arm64; Windows and Linux packaged apps were not exercised live.
    • Of the inspected page's 474 excluded scripts, I confirmed by category against raw Debugger.scriptParsed events that all were Node/Electron internals or anonymous scripts. Every app.asar script (11) was included.
    • The 5 excluded resources are 4 data: images and 1 https: avatar.

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 check:changed — typecheck and lint passed; 98 test files / 973 tests passed (6 skipped).
  • npm run docs:check — 6/6 tasks passed.
  • npx vitest run over the touched boundary suites (tests/boundary/mcp/{asarUnpackedExtractionMcp,artifactMcp,electronMcp}.test.ts, tests/boundary/filesystem/electronFileScope.test.ts, tests/boundary/browser/cdpElectronProvider.test.ts, tests/boundary/providers/native) — all passed except nativeSignatureTargetBinding.test.ts › keeps real codesign bound to registered bytes during an ancestor directory swap, which also fails on unmodified origin/main on this host.
  • Real application, Neat Reader 9.0.10 / Electron 13.6.9 on macOS:
    • rea extract-artifact, rea inspect-artifact, and rea analyze-javascript-application on app.asar (default policy) each return the unpacked-entry message naming their own operation.
    • rea extract-artifact --integrity-policy record-and-continue extracted 5321 files with one observed-untrusted contradiction.
    • With the app running with --remote-debugging-port, rea list-electron-targets, rea inspect-electron-page, and the MCP tools list_electron_targets / inspect_electron_page all list and inspect the app.asar page, with identical results between CLI and MCP.

Compatibility, safety, and release

  • Breaking changes or migration steps: none intended. Two caller-visible differences:
    • Extracting an archive whose active unpacked companion is missing now reports category unavailable with logical_path/unpacked details instead of artifact_operation_failed details {operation, reason: "format", detail}. The error code (artifact_operation_failed) and the refusal itself are unchanged.
    • Integrity failure messages for inspect_artifact/extract_artifact changed from the generic "get a fresh copy" text.
  • Real Hopper/Ghidra, browser, or OS coverage: no Hopper or Ghidra paths are touched, so none was run. Electron coverage is the real macOS application described above.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the new URL admission is limited to header-listed regular files inside a regular .asar file reached through a hostless file:// URL. It reads only the archive header and never executes or extracts archive content. Header reads are bounded by the existing @electron/asar parser and cached per file identity.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • 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/1400 **Author:** [@suninrain086](https://github.com/suninrain086) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/10/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/electron-asar-signed-unpacked` --- ### 📝 Commits (4) - [`b07161e`](https://github.com/morluto/rea/commit/b07161e7f4ad647556f46a3f6914a197015c82ab) fix(electron): resolve pages served from inside app.asar - [`c08130a`](https://github.com/morluto/rea/commit/c08130a1a926d15e789e8961d2e9e6b3c55aca87) fix(artifacts): extract and explain rewritten ASAR unpacked companions - [`f873177`](https://github.com/morluto/rea/commit/f873177a43a56973211d66fb1ef95a42e0a720c2) fix(electron): bound ASAR headers before allocation - [`6fea8fb`](https://github.com/morluto/rea/commit/6fea8fbfa3564befd052e98eb9abd4b4a06c7932) Merge remote-tracking branch 'origin/main' into HEAD ### 📊 Changes **17 files changed** (+480 additions, -41 deletions) <details> <summary>View changed files</summary> 📝 `docs/electron-observation.md` (+7 -1) 📝 `docs/mcp-contracts.md` (+12 -0) 📝 `src/application/javascript/JavaScriptArtifactReconstructionInput.ts` (+2 -1) 📝 `src/artifacts/ArtifactProvider.ts` (+1 -0) 📝 `src/artifacts/extraction/ArtifactExtraction.ts` (+32 -0) 📝 `src/browser/ElectronFileScope.ts` (+143 -8) 📝 `src/cli/artifactCommands.ts` (+11 -2) 📝 `src/contracts/artifactToolContracts.ts` (+10 -6) 📝 `src/domain/analysisErrorPresentation.ts` (+25 -9) 📝 `src/domain/artifactGraph.ts` (+1 -0) ➕ `src/domain/artifactIntegrityPolicy.ts` (+16 -0) 📝 `src/domain/javascript/javascriptApplicationAnalysis.ts` (+2 -1) 📝 `tests/boundary/browser/cdpElectronProvider.test.ts` (+47 -1) 📝 `tests/boundary/filesystem/electronFileScope.test.ts` (+64 -1) 📝 `tests/boundary/mcp/artifactMcp.test.ts` (+9 -6) ➕ `tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts` (+95 -0) 📝 `tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts` (+3 -5) </details> ### 📄 Description ## Summary Using REA on a shipped macOS Electron application (Neat Reader 9.0.10, Electron 13.6.9) exposed two gaps in the packaged-app workflow: 1. **Live Electron inspection could not see any packaged page.** Packaged apps load their renderer from `file:///…/app.asar/…` URLs. `authorizedElectronFile` stats the URL path on the host filesystem, which cannot resolve paths inside an archive. So `list_electron_targets` reported `targets: []` with `excluded.unsupported_url: 1`, and `inspect_electron_page` could not select the page at all. 2. **A rewritten `.asar.unpacked` companion blocked extraction and gave the wrong advice.** The app ships `node_modules/font-list/libs/darwin/fontlist` as an unpacked Mach-O that was code-signed after the ASAR header was written, so its declared SHA-256 no longer matches. 7915ee51 (`feat(javascript): record Electron integrity mismatches`) already lets `analyze_javascript_application` continue past this. `extract_artifact` still had no `integrity_policy`, though, and `inspect_artifact`/`extract_artifact` still said "Artifact is invalid or has changed. Get a fresh copy and try again." A fresh copy of a signed app has the same mismatch. Separately, extracting an archive whose unpacked companion file is missing was reported as a `format` failure ("not an extractable regular child file"). No issue is linked: I hit both problems while reverse-engineering a real application and am reporting them here with the fix. ## Problem and expected behavior **Packaged pages** - Smallest trigger: a CDP target whose URL is `file:///Applications/App.app/Contents/Resources/app.asar/build-app/index.html?x=0#/library`. - Violated invariant: Electron serves that URL as a regular file. REA should list and inspect it, and report a stable local path. Before this change it was excluded as `unsupported_url`, and so were every script, frame, and resource loaded from the archive. - Expected: resolve the URL the way Electron's file protocol does. The first `.asar` path component that is a regular file is the archive, and the remainder must name a regular file in its header (packed or unpacked). The reported path is the canonical archive path joined with that member. **Rewritten unpacked companions** - Smallest trigger: `createPackageWithOptions(src, "app.asar", { unpack: "**/*.node" })`, then rewrite the unpacked `.node` file (as macOS signing or distro stripping does) and run `extract_artifact`. - Expected: - The default stays fail-closed. - The caller can opt into `record-and-continue` exactly as for `inspect_artifact` and `analyze_javascript_application`. - The failure names the entry, explains the likely cause, and tells the caller which operation to rerun with which option. - A missing companion is reported as `unavailable`, with its logical path. ## Change and scope Two commits: **`fix(electron): resolve pages served from inside app.asar`** - `src/browser/ElectronFileScope.ts`: if the URL path is not a regular host file, walk its components to the first `.asar` component that is a regular file. Then look the remainder up in the archive header through `@electron/asar`'s `getRawHeader`. - Only regular file entries are accepted. Directories, `link` entries, missing members, and empty/`.`/`..` segments are rejected. - Parsed headers are cached by archive identity (`dev`, `ino`, `size`, `mtimeMs`), with at most 8 entries, because one inspection authorizes every frame, script, and resource URL. - Every existing consumer (`list_electron_targets`, `inspect_electron_page` frames/scripts/resources/DOM, Electron workers, V8 Inspector runtime locations) goes through the same function, so all of them pick this up without further changes. **`fix(artifacts): extract and explain rewritten ASAR unpacked companions`** - `extract_artifact` accepts `integrity_policy` (MCP input and `rea extract-artifact --integrity-policy`). With `record-and-continue` it writes the observed bytes. It returns the extracted files' canonical `integrity_contradictions` records (the same schema the JavaScript application result uses), plus one summary limitation. - `analysisErrorPresentation`: this generalizes the JavaScript-only message added in 7915ee51 to every operation that accepts `integrity_policy` (`inventory_artifact`/`inspect_artifact`, `extract_artifact`, `analyze_javascript_application`), naming the operation to rerun. - For an unpacked entry, the message names the logical path and explains that packaging tools commonly sign or strip unpacked native binaries after writing the archive. - The non-unpacked message for `analyze_javascript_application` is unchanged. - Extraction: an active unpacked entry with no companion bytes (`artifact_id: null`, `hash_status: "unavailable"`) now fails as `unavailable` with `artifactDetails` (`logical_path`, `unpacked: true`) instead of `format`. The message tells the caller to keep the `.unpacked` directory beside the archive. - `src/domain/artifactIntegrityPolicy.ts`: one shared `integrity_policy` schema now replaces the three duplicated inline enums, and the new extraction input uses it too. Intentionally not included: - **Unpacked entries are not exempted from integrity checks.** `AsarArtifactReader` documents that `unpacked` is not an integrity exemption, and existing tests rely on the fail-closed default. The caller still chooses `record-and-continue`. - **No repacking or mutation support.** REA stays read-only. - **The size of `analyze_javascript_application` output is not addressed.** For this app it was about 830 MB of JSON; that deserves its own issue. ## Contract and boundary impact - Semantic owner and earliest changed stage: `ElectronFileScope` (URL authorization, before any Electron observation); `ArtifactExtraction` (inventory selection and result); `analysisErrorPresentation` (error projection). - CLI and MCP/tool-catalog contract: additive. `extract_artifact` gains the optional input `integrity_policy` (default `fail`) and the result field `integrity_contradictions` (an empty array when there is no contradiction). `rea extract-artifact` gains `--integrity-policy`. The `extract_artifact` description is updated. Existing calls are unchanged. - Provider, bridge, target-format, or platform compatibility: the Electron CDP provider now accepts `file://` URLs inside regular `.asar` files on all hosts. Paths are built with the host `path` module, and `fileURLToPath` still owns URL→path conversion. - Evidence, artifact, provenance, or reconstruction contract: - Continued extraction mismatches are reported with the existing `integrity_contradictions` schema (`trust: "observed-untrusted"`) and are scoped to the extracted occurrences. - Electron `file_path` values for packaged pages are `<realpath(archive)>/<member>`. They identify the member and are never opened as host files. - Process execution, authorization, cleanup, or containment impact: URL authorization now admits regular-file members of a regular `.asar` file. It still rejects remote hosts, credentials, encoded separators, archive directories and links, and unknown members. Extraction containment and cleanup are unchanged. A failed extraction still leaves no output root (covered by the existing completeness test). - Generated metadata, package, or installation impact: none committed. `npm run docs:check` passes against the changed contracts. ## Evidence and regression coverage - Tests added or updated: - `tests/boundary/filesystem/electronFileScope.test.ts`: packed, nested, query/hash-bearing, and unpacked ASAR members resolve. Archive directories, missing members, and a non-ASAR file used as a parent are rejected. - `tests/boundary/browser/cdpElectronProvider.test.ts`: `listTargets` and `inspectPage` against a fake CDP endpoint whose page and script live inside a real `app.asar`. - `tests/boundary/mcp/asarUnpackedExtractionMcp.test.ts` (MCP, reuses `createStrippedAsarAddon`): - Strict extraction fails with the unpacked-entry message, which names `extract_artifact`. - `record-and-continue` extracts the observed bytes and returns `integrity_contradictions` plus the summary limitation. - A missing companion fails as `unavailable` with the logical path. - `tests/boundary/mcp/artifactMcp.test.ts`: the embedded/unpacked `inspect_artifact` integrity messages are updated. - `tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts`: the active-member refusal is now asserted as `unavailable` with `logical_path`/`unpacked` instead of `format`. Refusal and no output root are still asserted. The direct `materializeArtifactInventory` call passes `integrityPolicy: "fail"`. - Base reproduction: - With `src/` reverted to `origin/main`, both new extraction MCP tests fail. - With `ElectronFileScope.ts` reverted to the base, the new ASAR file-scope test fails. - On the real app, base `list-electron-targets` returned `"targets": []`, `"excluded": {"unsupported_url": 1, "non_page": 0}`. - User-visible CLI/MCP output (paths shortened): ```jsonc // rea list-electron-targets http://127.0.0.1:9333 (after) {"targets":[{"target_id":"CDDBB0EA…","file_path":"…/NeatReader.app/Contents/Resources/app.asar/build-app/index.html"}], "excluded":{"unsupported_url":0,"non_page":0}} // rea inspect-electron-page http://127.0.0.1:9333 <target> (after) {"target":"…/app.asar/build-app/index.html","scripts":11,"resources":6,"dom_nodes":460} // rea extract-artifact …/app.asar (default, after) {"code":"artifact_integrity_mismatch", "message":"Declared ASAR integrity for unpacked entry node_modules/font-list/libs/darwin/fontlist contradicts its companion file; packaging tools commonly sign or strip unpacked native binaries after writing the archive. If expected, rerun extract_artifact with integrity_policy=record-and-continue (CLI: --integrity-policy record-and-continue) to retain observed bytes as untrusted; otherwise get a fresh copy."} // rea extract-artifact …/app.asar --integrity-policy record-and-continue (after) {"artifacts":5321, "integrity_contradictions":[{"logical_path":"node_modules/font-list/libs/darwin/fontlist","unpacked":true,"trust":"observed-untrusted"}], "limitations":["…","1 extracted file(s) contradict declared integrity metadata; their bytes are observed-untrusted."]} ``` - Remaining proof gaps: - The live Electron check used one application, Electron 13.6.9 on macOS arm64; Windows and Linux packaged apps were not exercised live. - Of the inspected page's 474 excluded scripts, I confirmed by category against raw `Debugger.scriptParsed` events that all were Node/Electron internals or anonymous scripts. Every `app.asar` script (11) was included. - The 5 excluded resources are 4 `data:` images and 1 `https:` avatar. 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 check:changed` — typecheck and lint passed; 98 test files / 973 tests passed (6 skipped). - `npm run docs:check` — 6/6 tasks passed. - `npx vitest run` over the touched boundary suites (`tests/boundary/mcp/{asarUnpackedExtractionMcp,artifactMcp,electronMcp}.test.ts`, `tests/boundary/filesystem/electronFileScope.test.ts`, `tests/boundary/browser/cdpElectronProvider.test.ts`, `tests/boundary/providers/native`) — all passed except `nativeSignatureTargetBinding.test.ts › keeps real codesign bound to registered bytes during an ancestor directory swap`, which also fails on unmodified `origin/main` on this host. - Real application, Neat Reader 9.0.10 / Electron 13.6.9 on macOS: - `rea extract-artifact`, `rea inspect-artifact`, and `rea analyze-javascript-application` on `app.asar` (default policy) each return the unpacked-entry message naming their own operation. - `rea extract-artifact --integrity-policy record-and-continue` extracted 5321 files with one `observed-untrusted` contradiction. - With the app running with `--remote-debugging-port`, `rea list-electron-targets`, `rea inspect-electron-page`, and the MCP tools `list_electron_targets` / `inspect_electron_page` all list and inspect the `app.asar` page, with identical results between CLI and MCP. ## Compatibility, safety, and release - Breaking changes or migration steps: none intended. Two caller-visible differences: - Extracting an archive whose active unpacked companion is missing now reports category `unavailable` with `logical_path`/`unpacked` details instead of `artifact_operation_failed` details `{operation, reason: "format", detail}`. The error code (`artifact_operation_failed`) and the refusal itself are unchanged. - Integrity failure messages for `inspect_artifact`/`extract_artifact` changed from the generic "get a fresh copy" text. - Real Hopper/Ghidra, browser, or OS coverage: no Hopper or Ghidra paths are touched, so none was run. Electron coverage is the real macOS application described above. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the new URL admission is limited to header-listed regular files inside a regular `.asar` file reached through a hostless `file://` URL. It reads only the archive header and never executes or extracts archive content. Header reads are bounded by the existing `@electron/asar` parser and cached per file identity. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [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-10-17 21:11:05 +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#2857
No description provided.