[GH-ISSUE #5958] [Bug] Webpack loaders without an exports return produce false Electron window facts #3629

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

Originally created by @coygeek on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5958

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application attributes a BrowserWindow construction to Electron through a Webpack-style loader that never returns exports on its cache-miss path. Both a loader with only the early cache-hit return and a loader with no return statements produce one BrowserWindow. Each analysis succeeds and the window observation carries authority: "ast-static-analysis", state: "observed", and confidence: "exact".

Executing these authored fixtures separately in Node with a minimal Electron stub shows that both loaders return undefined to the consumer and throw before the constructor runs. Restoring return module.exports makes the same consumer construct one window.

Reproduction

Use a built checkout of the revision below. Set REA_CHECKOUT to its root. Create cache-hit-only/main.js with this source:

(function () {
  var modules = {
    45840: function (module) {
      module.exports = require("electron");
    },
    1: function (module, exports, n) {
      new (n(45840).BrowserWindow)({});
    }
  }, cache = {};
  function n(key) {
    var cached = cache[key];
    if (cached !== undefined) return cached.exports;
    var module = (cache[key] = { exports: {} });
    modules[key](module, module.exports, n);
  }
  n(1);
})();

Create cache-hit-only/package.json containing:

{"name":"bootstrap-fixture","version":"1.0.0","main":"main.js"}

Create two sibling directories containing the same files:

  • canonical: add return module.exports; immediately after the dispatch call.
  • no-returns: remove if (cached !== undefined) return cached.exports; from the first fixture.

From their parent directory, run the public CLI for all three fixtures:

for variant in canonical cache-hit-only no-returns; do
  node "$REA_CHECKOUT/scripts/rea.mjs" analyze-javascript-application "./$variant" --json > "cli-$variant.json" 2> "cli-$variant.stderr"
done
python3 - <<'PYTHON'
import json
for name in ['canonical', 'cache-hit-only', 'no-returns']:
    with open(f'cli-{name}.json') as stream:
        value = json.load(stream)['normalized_result']
    print(name, value['summary']['browser_windows'])
PYTHON

All three CLI calls were executed successfully. REA parses the files as inert source; it does not execute them or require an installed Electron runtime. The Node-vm comparison described below was a separate controlled execution.

Expected behavior

The canonical loader should retain one Electron BrowserWindow. The two fallthrough loaders must not establish that n(45840) returns Electron, so the BrowserWindow consumer must remain unresolved instead of contributing an exact Electron window observation. The original literal require("electron") source observation should remain available.

The integrity proof contract requires loader(key) to return the selected factory's exports and refuses a proof when that is unproven. This is an identity error, independent of whether static analysis proves runtime guards pass.

Impact and scope

P2: successful analysis returns a false exact Electron boundary observation for these malformed or transformed bootstrap shapes. The normal loader control remains correct. Prevalence in shipped applications was not tested. No supported option that corrects the identity while preserving the analyzed source bytes was demonstrated; editing the source is only a discriminating control.

REA version

6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

macOS 27.0.1 arm64, Node.js v26.12.0; built source-checkout runtime.

Target and artifact context

Synthetic JavaScript application directories with a literal module table, empty cache, Electron external factory, and BrowserWindow consumer. No real Electron application or native analysis provider was used. The separate Node-vm execution supplied a minimal require stub returning a counter-based BrowserWindow class.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed CLI results:

canonical 1
cache-hit-only 1
no-returns 1

Separately, the same generated source strings were passed to Node's runInNewContext, with require returning {BrowserWindow: class { constructor() { constructed++; } }}:

canonical: constructed=1; no error
cache-hit-only: constructed=0; TypeError: Cannot read properties of undefined (reading 'BrowserWindow')
no-returns: constructed=0; TypeError: Cannot read properties of undefined (reading 'BrowserWindow')

The cache-hit-only CLI result includes a browser-window node with an observation whose evidence has authority: "ast-static-analysis", state: "observed", confidence: "exact", and coverage.status: "complete". Both fallthrough variants also return one window from the compiled analyzeJavaScriptStaticSource producer.

Typed outcome or recovery expected

Refuse the bootstrap integrity proof when a normal completion path returns no exports. That should leave the bundled-call Electron identity unresolved, preserve unrelated source facts, and keep application analysis available. A nonempty list of explicit returns alone cannot reject the cache-hit-only variant.

Analysis or suspected cause

Source-derived cause: loaderShape collects explicit ReturnStatement arguments and accepts returns.every(...). An empty list passes vacuously, and a valid cache-hit return does not account for the miss path falling off the end. The consumer then treats the bootstrap as proven and retains its literal external mapping.

Additional context

The relevant integrity and bundled-require files were unchanged on upstream main at 25e1a24a3ed3671a0148757555f18d16c29d936a. MCP was not separately executed. The evidence establishes the public CLI failure and shared producer behavior on synthetic inputs, without claiming a real-application reproduction.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5958 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `analyze-javascript-application` attributes a BrowserWindow construction to Electron through a Webpack-style loader that never returns exports on its cache-miss path. Both a loader with only the early cache-hit return and a loader with no return statements produce one BrowserWindow. Each analysis succeeds and the window observation carries `authority: "ast-static-analysis"`, `state: "observed"`, and `confidence: "exact"`. Executing these authored fixtures separately in Node with a minimal Electron stub shows that both loaders return `undefined` to the consumer and throw before the constructor runs. Restoring `return module.exports` makes the same consumer construct one window. ### Reproduction Use a built checkout of the revision below. Set `REA_CHECKOUT` to its root. Create `cache-hit-only/main.js` with this source: ```js (function () { var modules = { 45840: function (module) { module.exports = require("electron"); }, 1: function (module, exports, n) { new (n(45840).BrowserWindow)({}); } }, cache = {}; function n(key) { var cached = cache[key]; if (cached !== undefined) return cached.exports; var module = (cache[key] = { exports: {} }); modules[key](module, module.exports, n); } n(1); })(); ``` Create `cache-hit-only/package.json` containing: ```json {"name":"bootstrap-fixture","version":"1.0.0","main":"main.js"} ``` Create two sibling directories containing the same files: - `canonical`: add `return module.exports;` immediately after the dispatch call. - `no-returns`: remove `if (cached !== undefined) return cached.exports;` from the first fixture. From their parent directory, run the public CLI for all three fixtures: ```sh for variant in canonical cache-hit-only no-returns; do node "$REA_CHECKOUT/scripts/rea.mjs" analyze-javascript-application "./$variant" --json > "cli-$variant.json" 2> "cli-$variant.stderr" done python3 - <<'PYTHON' import json for name in ['canonical', 'cache-hit-only', 'no-returns']: with open(f'cli-{name}.json') as stream: value = json.load(stream)['normalized_result'] print(name, value['summary']['browser_windows']) PYTHON ``` All three CLI calls were executed successfully. REA parses the files as inert source; it does not execute them or require an installed Electron runtime. The Node-vm comparison described below was a separate controlled execution. ### Expected behavior The canonical loader should retain one Electron BrowserWindow. The two fallthrough loaders must not establish that `n(45840)` returns Electron, so the BrowserWindow consumer must remain unresolved instead of contributing an exact Electron window observation. The original literal `require("electron")` source observation should remain available. The [integrity proof contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/javascript/javascriptBootstrapIntegrity.ts#L31-L38) requires `loader(key)` to return the selected factory's exports and refuses a proof when that is unproven. This is an identity error, independent of whether static analysis proves runtime guards pass. ### Impact and scope P2: successful analysis returns a false exact Electron boundary observation for these malformed or transformed bootstrap shapes. The normal loader control remains correct. Prevalence in shipped applications was not tested. No supported option that corrects the identity while preserving the analyzed source bytes was demonstrated; editing the source is only a discriminating control. ### REA version 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform macOS 27.0.1 arm64, Node.js v26.12.0; built source-checkout runtime. ### Target and artifact context Synthetic JavaScript application directories with a literal module table, empty cache, Electron external factory, and BrowserWindow consumer. No real Electron application or native analysis provider was used. The separate Node-vm execution supplied a minimal `require` stub returning a counter-based BrowserWindow class. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed CLI results: ```text canonical 1 cache-hit-only 1 no-returns 1 ``` Separately, the same generated source strings were passed to Node's `runInNewContext`, with `require` returning `{BrowserWindow: class { constructor() { constructed++; } }}`: ```text canonical: constructed=1; no error cache-hit-only: constructed=0; TypeError: Cannot read properties of undefined (reading 'BrowserWindow') no-returns: constructed=0; TypeError: Cannot read properties of undefined (reading 'BrowserWindow') ``` The cache-hit-only CLI result includes a `browser-window` node with an observation whose evidence has `authority: "ast-static-analysis"`, `state: "observed"`, `confidence: "exact"`, and `coverage.status: "complete"`. Both fallthrough variants also return one window from the compiled `analyzeJavaScriptStaticSource` producer. ### Typed outcome or recovery expected Refuse the bootstrap integrity proof when a normal completion path returns no exports. That should leave the bundled-call Electron identity unresolved, preserve unrelated source facts, and keep application analysis available. A nonempty list of explicit returns alone cannot reject the cache-hit-only variant. ### Analysis or suspected cause Source-derived cause: [loaderShape](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/javascript/javascriptBootstrapIntegrity.ts#L88-L129) collects explicit `ReturnStatement` arguments and accepts `returns.every(...)`. An empty list passes vacuously, and a valid cache-hit return does not account for the miss path falling off the end. [The consumer](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/javascript/javascriptBundledRequireOrigin.ts#L358-L372) then treats the bootstrap as proven and retains its literal external mapping. ### Additional context The relevant integrity and bundled-require files were unchanged on upstream main at `25e1a24a3ed3671a0148757555f18d16c29d936a`. MCP was not separately executed. The evidence establishes the public CLI failure and shared producer behavior on synthetic inputs, without claiming a real-application reproduction.
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#3629
No description provided.