[PR #1874] [MERGED] fix(javascript): trace wrapped Electron IPC paths in large bundles #4870

Closed
opened 2026-10-31 00:56:13 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1874
Author: @fengdwx
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/12/2026
Merged by: @morluto

Base: main ← Head: fix/electron-bundle-clipboard-paths


📝 Commits (2)

  • 1017d61 fix(javascript): trace wrapped Electron IPC paths in large bundles
  • cfc32c4 fix(javascript): preserve wrapper alternatives and merge upstream main

📊 Changes

46 files changed (+3210 additions, -352 deletions)

View changed files

📝 docs/javascript-artifact-reconstruction.md (+41 -2)
📝 src/application/javascript/ElectronBoundaryAnalysis.ts (+1 -1)
📝 src/application/javascript/ElectronBoundaryGraph.ts (+2 -0)
➕ src/application/javascript/ElectronBoundaryGraphCallPaths.ts (+179 -0)
📝 src/application/javascript/ElectronBoundaryGraphContext.ts (+1 -0)
📝 src/application/javascript/ElectronBoundaryGraphIpc.ts (+29 -11)
📝 src/application/javascript/ElectronBoundaryGraphWindows.ts (+8 -2)
📝 src/application/javascript/JavaScriptApplicationEvidence.ts (+6 -0)
📝 src/application/javascript/JavaScriptApplicationService.ts (+6 -0)
📝 src/application/javascript/JavaScriptArtifactReconstruction.ts (+8 -1)
📝 src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts (+47 -0)
📝 src/application/javascript/JavaScriptSemanticGraphBuilder.ts (+243 -221)
➕ src/application/javascript/JavaScriptSemanticGraphElectronProjection.test.ts (+72 -0)
➕ src/application/javascript/JavaScriptSemanticGraphElectronProjection.ts (+73 -0)
📝 src/cli/electronCommands.ts (+9 -0)
📝 src/cliObservationOptions.ts (+4 -0)
📝 src/composition/javascriptAnalysisWorkerProcess.ts (+6 -1)
📝 src/domain/javascript/electronStaticAnalysisIpc.ts (+3 -1)
📝 src/domain/javascript/electronStaticAnalysisTypes.ts (+33 -0)
➕ src/domain/javascript/electronWrappedCallPaths.test.ts (+260 -0)

...and 26 more files

📄 Description

Summary

Recover candidate feature paths through Electron wrapper functions and literal CommonJS module tables, and allow large bundles to retain their semantic graph within explicit caller-selected budgets.

An exposed member such as desktop.copy: sender("example:copy") currently loses its connection to ipcRenderer.send and the wrapped main-process callback. This change lets trace-application-feature follow that member through a unique literal IPC pairing to event.sender.copy(), preserving source locations, guards, and inferred authority.

Problem and expected behavior

The following pattern should produce a candidate cross-file path:

// preload.js
const e = require("electron");
const sender = channel => (...args) => e.ipcRenderer.send(channel, ...args);
e.contextBridge.exposeInMainWorld("desktop", { copy: sender("example:copy") });

// main.js
const e = require("electron");
function register(channel, handler) {
  e.ipcMain.addListener(channel, event => allowed(event) && handler(event));
}
register("example:copy", event => event.sender.copy());

Large bundles hit the fixed 20,000-node per-file semantic projection limit even when more heap is available. This PR adds explicit node budgets and bounded adaptive result transfer. A class/getter alias regression also incorrectly retains a literal after its shared object escapes through a loop.

These were found while testing publicly distributed Notion desktop JavaScript. This PR includes the reproduction directly; no separate issue was opened. Upstream integration: merged main at 6611bcf6e86630c775c24e6afa00d7239c64e70e, preserving #1857 and #1912 (the fix for #1909). The upstream intrinsic-invocation and module-interop owners and their regression tests are unchanged. Upstream source selection and cancellation behavior are retained.

Change and scope

  • Recognize literal standalone module-table bootstraps and substitute lexical exports, wrapper arguments, returned closures, object members/spreads, and IPC callback parameters without evaluating target code.
  • Emit contextual candidate paths with source ranges and guard expressions; join only unique compatible literal channels. Ambiguous handlers, reassigned bindings, unknown spreads, and unsupported values remain unresolved.
  • Prioritize discovered boundary paths during semantic projection. Add explicit total/per-file node controls while preserving the 100,000/20,000 defaults and independent worker/parent memory admission.
  • Use upstream main's sequential next-checkpoint reserve unchanged; remove this PR's superseded reserve helper. Scale streamed transfer capacity with parent admission while retaining the 8 MiB record limit and a 2 GiB file ceiling.
  • Preserve distinct class/getter origins during mutation-path pruning. Retain upstream's newer general alias-cycle termination logic.

Review fixes

  • Ambiguous handler selection: expand a handler only when its symbolic value contains exactly one function and no unresolved alternative. Keep an unresolved channel registration so it still prevents a false unique pairing with another handler.
  • Nullish short-circuiting: share right-operand selection across value substitution, callback traversal, and seed traversal. Known non-nullish values (including false, 0, and "") exclude the fallback; nullish values select it; unknown values retain a nullish guard.
  • Factory return alternatives: retain both branches of an unknown condition. Return collection tracks fallthrough separately, includes implicit undefined, and excludes statements after all branches definitely exit.
  • Added public CLI and stdio MCP checks for the resulting application graph, plus source cases for nested branches, definite exits, nullish guards, and handlers with an unresolved alternative. The new regression cases first reproduced 12 failures before the fixes.

Contract and boundary impact

  • Semantic owner and earliest changed stage: domain JavaScript bootstrap/binding analysis, then application graph projection and feature tracing.
  • CLI and MCP/tool-catalog contract: optional max_semantic_nodes and max_semantic_nodes_per_file (CLI: --max-semantic-nodes, --max-semantic-nodes-per-file), integers from 1 through 1,000,000. Explicit choices are retained in Evidence parameters; omitted choices remain absent.
  • Provider, bridge, target-format, or platform compatibility: static JavaScript/Electron analysis; no runtime provider or native bridge changes. Current Node/Bun platform-specific worker enforcement is preserved.
  • Evidence, artifact, provenance, or reconstruction contract: optional electron.call_paths, resolution_basis: "wrapper-substitution", and contextual javascript-expression graph nodes. New paths and IPC joins are inferred candidates; runtime execution and guard acceptance remain unverified. Resource exhaustion is reported in coverage.
  • Process execution, authorization, cleanup, or containment impact: existing isolated analysis worker, cancellation, and owned cleanup remain in use. Target JavaScript is never executed.
  • Generated metadata, package, or installation impact: owning documentation updated; ignored catalogs regenerated and checked locally. No dependency, package-version, or installation changes; no generated output committed.

Evidence and regression coverage

  • On base d3e9ee5505251f760666bb8e1e6c295b6d6aedb2, the wrapper and reference-lifetime regression files produced 5 failures / 115 passes: four missing wrapper-path behaviors and the getter escape case. The corresponding tests pass with this patch.

  • A compiled public CLI test runs analysis and then trace-application-feature using the complete returned Evidence and only the desktop.copy seed. It asserts the path, guards, inferred authority, and separation from desktop.cut; a competing untraced handler prevents pairing.

  • Additional coverage exercises bootstrap syntax, shadowing/overwrites, helper-name spoofing, definite throws/returns, explicit node limits across files, transfer admission, IPC provenance, cancellation, and MCP resource recovery.

  • Notion 7.37.1 macOS arm64: the two unchanged distributed bundles (.webpack/main/index.js and .webpack/renderer/tab_browser_view/preload.js) were analyzed with the public CLI, using a 6,144 MiB worker heap, 24,576 MiB parent old-space budget, 750,000 total nodes, and 700,000 nodes per file. No Notion binaries or source files are included in this PR.

  • Verified analysis result: 651,734 semantic nodes / 635,848 relations, both files parsed, zero parse failures, truncated: false, zero omitted nodes/relations, and no resource limits. Analysis exited successfully in 105.4 s. The 220,735 dynamic/ambiguous facts remain unknown; overall semantic coverage is still unknown.

  • Passing the complete returned Evidence to trace-application-feature with only the API seed __electronApi.copy and match: "contains" recovered all three candidate paths below (63.292 s, exit 0). The source hashes, every directed edge, guard evidence, and unique literal notion:copy join were independently checked against the saved CLI output.

Entry Terminal Nodes / edges
__electronApi.copy ipc-event.sender.copy 15 / 14
__electronApi.copyText navigator.clipboard.writeText 3 / 2
__electronApi.copyImage navigator.clipboard.write 5 / 4

Input SHA-256 digests (for reproducibility):

main/index.js: 9dda511b87214c82b06e90b232f5e10db07bd4581f8747251199dc3dd2a34aa0
tab_browser_view/preload.js: dee77f63b2f50333a36bbc711a8f54e416b121d96f8278f53627eb00ce9bac38
  • Remaining proof gaps: static candidates do not establish actual Notion UI/clipboard execution, accepted senders, OS implementation, or client reimplementation. Dynamic JavaScript unknowns remain explicitly reported.

  • 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 -- … across 91 JavaScript/Electron source, filesystem, CLI, worker, MCP, and acceptance test files: 1,548 tests passed on macOS arm64, Node 24.18.0 / npm 11.16.0.
  • npm run check: passed, including typecheck, lint, formatting, knip, metadata, module boundaries, and architecture guards.
  • npm run docs:check: passed.
  • npm run verify:test-discovery: all 892 test files discovered exactly once.
  • git diff origin/main --check: passed. The committed tree exactly matches the tested tree; working tree is clean.
  • Real artifact analysis and feature trace: results above, performed through the compiled public CLI on the PR implementation.

Compatibility, safety, and release

  • Breaking changes or migration steps: no existing invocation needs migration. New optional fields and a new application-node kind are documented above. Existing resource defaults remain unchanged.
  • Real Hopper/Ghidra, browser, or OS coverage: not exercised; this changes static Electron/JavaScript analysis. Linux, Windows, Bun, and actual Notion UI behavior were not independently exercised locally.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: source is parsed inertly; IPC pairing remains inferred and guards are retained. No proprietary fixtures, credentials, local evaluation output, or build artifacts are committed.

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/1874 **Author:** [@fengdwx](https://github.com/fengdwx) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/12/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/electron-bundle-clipboard-paths` --- ### 📝 Commits (2) - [`1017d61`](https://github.com/morluto/rea/commit/1017d61dbe85155489b3e100eb20ca9735d87171) fix(javascript): trace wrapped Electron IPC paths in large bundles - [`cfc32c4`](https://github.com/morluto/rea/commit/cfc32c4421becb4260e5969a2a52b3a0cc1a7185) fix(javascript): preserve wrapper alternatives and merge upstream main ### 📊 Changes **46 files changed** (+3210 additions, -352 deletions) <details> <summary>View changed files</summary> 📝 `docs/javascript-artifact-reconstruction.md` (+41 -2) 📝 `src/application/javascript/ElectronBoundaryAnalysis.ts` (+1 -1) 📝 `src/application/javascript/ElectronBoundaryGraph.ts` (+2 -0) ➕ `src/application/javascript/ElectronBoundaryGraphCallPaths.ts` (+179 -0) 📝 `src/application/javascript/ElectronBoundaryGraphContext.ts` (+1 -0) 📝 `src/application/javascript/ElectronBoundaryGraphIpc.ts` (+29 -11) 📝 `src/application/javascript/ElectronBoundaryGraphWindows.ts` (+8 -2) 📝 `src/application/javascript/JavaScriptApplicationEvidence.ts` (+6 -0) 📝 `src/application/javascript/JavaScriptApplicationService.ts` (+6 -0) 📝 `src/application/javascript/JavaScriptArtifactReconstruction.ts` (+8 -1) 📝 `src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts` (+47 -0) 📝 `src/application/javascript/JavaScriptSemanticGraphBuilder.ts` (+243 -221) ➕ `src/application/javascript/JavaScriptSemanticGraphElectronProjection.test.ts` (+72 -0) ➕ `src/application/javascript/JavaScriptSemanticGraphElectronProjection.ts` (+73 -0) 📝 `src/cli/electronCommands.ts` (+9 -0) 📝 `src/cliObservationOptions.ts` (+4 -0) 📝 `src/composition/javascriptAnalysisWorkerProcess.ts` (+6 -1) 📝 `src/domain/javascript/electronStaticAnalysisIpc.ts` (+3 -1) 📝 `src/domain/javascript/electronStaticAnalysisTypes.ts` (+33 -0) ➕ `src/domain/javascript/electronWrappedCallPaths.test.ts` (+260 -0) _...and 26 more files_ </details> ### 📄 Description ## Summary Recover candidate feature paths through Electron wrapper functions and literal CommonJS module tables, and allow large bundles to retain their semantic graph within explicit caller-selected budgets. An exposed member such as `desktop.copy: sender("example:copy")` currently loses its connection to `ipcRenderer.send` and the wrapped main-process callback. This change lets `trace-application-feature` follow that member through a unique literal IPC pairing to `event.sender.copy()`, preserving source locations, guards, and inferred authority. ## Problem and expected behavior The following pattern should produce a candidate cross-file path: ```js // preload.js const e = require("electron"); const sender = channel => (...args) => e.ipcRenderer.send(channel, ...args); e.contextBridge.exposeInMainWorld("desktop", { copy: sender("example:copy") }); // main.js const e = require("electron"); function register(channel, handler) { e.ipcMain.addListener(channel, event => allowed(event) && handler(event)); } register("example:copy", event => event.sender.copy()); ``` Large bundles hit the fixed 20,000-node per-file semantic projection limit even when more heap is available. This PR adds explicit node budgets and bounded adaptive result transfer. A class/getter alias regression also incorrectly retains a literal after its shared object escapes through a loop. These were found while testing publicly distributed Notion desktop JavaScript. This PR includes the reproduction directly; no separate issue was opened. Upstream integration: merged `main` at `6611bcf6e86630c775c24e6afa00d7239c64e70e`, preserving #1857 and #1912 (the fix for #1909). The upstream intrinsic-invocation and module-interop owners and their regression tests are unchanged. Upstream source selection and cancellation behavior are retained. ## Change and scope - Recognize literal standalone module-table bootstraps and substitute lexical exports, wrapper arguments, returned closures, object members/spreads, and IPC callback parameters without evaluating target code. - Emit contextual candidate paths with source ranges and guard expressions; join only unique compatible literal channels. Ambiguous handlers, reassigned bindings, unknown spreads, and unsupported values remain unresolved. - Prioritize discovered boundary paths during semantic projection. Add explicit total/per-file node controls while preserving the 100,000/20,000 defaults and independent worker/parent memory admission. - Use upstream main's sequential next-checkpoint reserve unchanged; remove this PR's superseded reserve helper. Scale streamed transfer capacity with parent admission while retaining the 8 MiB record limit and a 2 GiB file ceiling. - Preserve distinct class/getter origins during mutation-path pruning. Retain upstream's newer general alias-cycle termination logic. ## Review fixes - [Ambiguous handler selection](https://github.com/morluto/rea/pull/1874#discussion_r4242102477): expand a handler only when its symbolic value contains exactly one function and no unresolved alternative. Keep an unresolved channel registration so it still prevents a false unique pairing with another handler. - [Nullish short-circuiting](https://github.com/morluto/rea/pull/1874#discussion_r4242102481): share right-operand selection across value substitution, callback traversal, and seed traversal. Known non-nullish values (including `false`, `0`, and `""`) exclude the fallback; nullish values select it; unknown values retain a nullish guard. - [Factory return alternatives](https://github.com/morluto/rea/pull/1874#discussion_r4242102486): retain both branches of an unknown condition. Return collection tracks fallthrough separately, includes implicit `undefined`, and excludes statements after all branches definitely exit. - Added public CLI and stdio MCP checks for the resulting application graph, plus source cases for nested branches, definite exits, nullish guards, and handlers with an unresolved alternative. The new regression cases first reproduced 12 failures before the fixes. ## Contract and boundary impact - Semantic owner and earliest changed stage: domain JavaScript bootstrap/binding analysis, then application graph projection and feature tracing. - CLI and MCP/tool-catalog contract: optional `max_semantic_nodes` and `max_semantic_nodes_per_file` (CLI: `--max-semantic-nodes`, `--max-semantic-nodes-per-file`), integers from 1 through 1,000,000. Explicit choices are retained in Evidence parameters; omitted choices remain absent. - Provider, bridge, target-format, or platform compatibility: static JavaScript/Electron analysis; no runtime provider or native bridge changes. Current Node/Bun platform-specific worker enforcement is preserved. - Evidence, artifact, provenance, or reconstruction contract: optional `electron.call_paths`, `resolution_basis: "wrapper-substitution"`, and contextual `javascript-expression` graph nodes. New paths and IPC joins are inferred candidates; runtime execution and guard acceptance remain unverified. Resource exhaustion is reported in coverage. - Process execution, authorization, cleanup, or containment impact: existing isolated analysis worker, cancellation, and owned cleanup remain in use. Target JavaScript is never executed. - Generated metadata, package, or installation impact: owning documentation updated; ignored catalogs regenerated and checked locally. No dependency, package-version, or installation changes; no generated output committed. ## Evidence and regression coverage - On base `d3e9ee5505251f760666bb8e1e6c295b6d6aedb2`, the wrapper and reference-lifetime regression files produced **5 failures / 115 passes**: four missing wrapper-path behaviors and the getter escape case. The corresponding tests pass with this patch. - A compiled public CLI test runs analysis and then `trace-application-feature` using the complete returned Evidence and only the `desktop.copy` seed. It asserts the path, guards, inferred authority, and separation from `desktop.cut`; a competing untraced handler prevents pairing. - Additional coverage exercises bootstrap syntax, shadowing/overwrites, helper-name spoofing, definite throws/returns, explicit node limits across files, transfer admission, IPC provenance, cancellation, and MCP resource recovery. - Notion 7.37.1 macOS arm64: the two unchanged distributed bundles (`.webpack/main/index.js` and `.webpack/renderer/tab_browser_view/preload.js`) were analyzed with the public CLI, using a 6,144 MiB worker heap, 24,576 MiB parent old-space budget, 750,000 total nodes, and 700,000 nodes per file. No Notion binaries or source files are included in this PR. - Verified analysis result: **651,734 semantic nodes / 635,848 relations**, both files parsed, zero parse failures, `truncated: false`, zero omitted nodes/relations, and no resource limits. Analysis exited successfully in 105.4 s. The 220,735 dynamic/ambiguous facts remain unknown; overall semantic coverage is still `unknown`. - Passing the complete returned Evidence to `trace-application-feature` with only the API seed `__electronApi.copy` and `match: "contains"` recovered all three candidate paths below (63.292 s, exit 0). The source hashes, every directed edge, guard evidence, and unique literal `notion:copy` join were independently checked against the saved CLI output. | Entry | Terminal | Nodes / edges | | --- | --- | --- | | `__electronApi.copy` | `ipc-event.sender.copy` | 15 / 14 | | `__electronApi.copyText` | `navigator.clipboard.writeText` | 3 / 2 | | `__electronApi.copyImage` | `navigator.clipboard.write` | 5 / 4 | Input SHA-256 digests (for reproducibility): ```text main/index.js: 9dda511b87214c82b06e90b232f5e10db07bd4581f8747251199dc3dd2a34aa0 tab_browser_view/preload.js: dee77f63b2f50333a36bbc711a8f54e416b121d96f8278f53627eb00ce9bac38 ``` - Remaining proof gaps: static candidates do not establish actual Notion UI/clipboard execution, accepted senders, OS implementation, or client reimplementation. Dynamic JavaScript unknowns remain explicitly reported. - [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 -- …` across 91 JavaScript/Electron source, filesystem, CLI, worker, MCP, and acceptance test files: **1,548 tests passed** on macOS arm64, Node 24.18.0 / npm 11.16.0. - `npm run check`: passed, including typecheck, lint, formatting, knip, metadata, module boundaries, and architecture guards. - `npm run docs:check`: passed. - `npm run verify:test-discovery`: all 892 test files discovered exactly once. - `git diff origin/main --check`: passed. The committed tree exactly matches the tested tree; working tree is clean. - Real artifact analysis and feature trace: results above, performed through the compiled public CLI on the PR implementation. ## Compatibility, safety, and release - Breaking changes or migration steps: no existing invocation needs migration. New optional fields and a new application-node kind are documented above. Existing resource defaults remain unchanged. - Real Hopper/Ghidra, browser, or OS coverage: not exercised; this changes static Electron/JavaScript analysis. Linux, Windows, Bun, and actual Notion UI behavior were not independently exercised locally. - Package or release metadata impact: none. - Security, privacy, process, or containment review: source is parsed inertly; IPC pairing remains inferred and guards are retained. No proprietary fixtures, credentials, local evaluation output, or build artifacts are committed. ## 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-31 00:56:13 +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#4870
No description provided.