[GH-ISSUE #7227] [Bug] Module-level webPreferences constants are reported as dynamic when the window is created inside a function #4307

Open
opened 2026-10-31 00:48:46 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @jiwonsudo on GitHub (Oct 26, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7227

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 reports web_preferences_status: "dynamic" and no preferences when a webPreferences (or whole options) object is a module-level const and the BrowserWindow is created inside a function or callback. This is the usual createWindow() pattern, so security-relevant settings like nodeIntegration: true and contextIsolation: false are not reported.

The same object is resolved when the window is created at module top level, or when the const is declared inside the same function.

Reproduction

main.js:

const { app, BrowserWindow } = require("electron");

const webPreferences = { nodeIntegration: true, contextIsolation: false };

function createWindow() {
  return new BrowserWindow({ width: 800, height: 600, webPreferences });
}

app.whenReady().then(createWindow);

package.json: {"name":"demo","version":"1.0.0","main":"main.js"}

Run: rea analyze-javascript-application ./demo --format json

Result:

  • summary.explicit_web_preferences: 0
  • browser-window web_preferences_status: "dynamic", web_preferences: []

I also checked these variants on the same commit:

Where the window is created Result
module top level, const declared above object-literal
same function as the const object-literal
function that reads a module-level const dynamic
app.whenReady().then(() => ...) callback reading a module-level const dynamic
function reading a module-level whole options const dynamic

Expected behavior

A function can only finish reading an outer const after its declaration has run, so the window should report the constant's preferences, as it already does for same-scope constants. It should stay dynamic when the object may be changed by another activation (writes or escapes elsewhere, repeated calls, exported bindings).

Impact and scope

Static Electron analysis misses nodeIntegration, contextIsolation, sandbox and preload for windows created in functions with module-level option constants. An agent reading the result cannot see risky settings that are written in plain literals. The analysis is deterministic and every run I made on Linux gave the same result. Not tested on bundled (Webpack) output.

REA version

main at e7f986c9 (rea-agents 6.8.0 source)

Runtime and platform

Node v24.22.1, Linux x64

Target and artifact context

Small extracted Electron app directory, shown above.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

summary.explicit_web_preferences = 0
web_preferences_status = dynamic
web_preferences = []

Typed outcome or recovery expected

No response

Analysis or suspected cause

Source-derived: optionObjectCandidate in src/domain/javascript/electronStaticOptionObjects.ts requires semanticBindingAtRead(...).hasDefiniteInitializer. That is false for a read inside a nested function, because the read and the declaration are in different bodies. #4366 / #4400 added same-scope constant resolution and #3536 scoped it to same-scope constants, so this case is not covered yet. I have a fix ready and will open a PR.

Additional context

No response

Originally created by @jiwonsudo on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7227 ### 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` reports `web_preferences_status: "dynamic"` and no preferences when a `webPreferences` (or whole options) object is a module-level `const` and the `BrowserWindow` is created inside a function or callback. This is the usual `createWindow()` pattern, so security-relevant settings like `nodeIntegration: true` and `contextIsolation: false` are not reported. The same object is resolved when the window is created at module top level, or when the const is declared inside the same function. ### Reproduction main.js: ```js const { app, BrowserWindow } = require("electron"); const webPreferences = { nodeIntegration: true, contextIsolation: false }; function createWindow() { return new BrowserWindow({ width: 800, height: 600, webPreferences }); } app.whenReady().then(createWindow); ``` package.json: `{"name":"demo","version":"1.0.0","main":"main.js"}` Run: `rea analyze-javascript-application ./demo --format json` Result: - `summary.explicit_web_preferences`: `0` - browser-window `web_preferences_status`: `"dynamic"`, `web_preferences`: `[]` I also checked these variants on the same commit: | Where the window is created | Result | |---|---| | module top level, const declared above | object-literal | | same function as the const | object-literal | | function that reads a module-level const | dynamic | | `app.whenReady().then(() => ...)` callback reading a module-level const | dynamic | | function reading a module-level whole options const | dynamic | ### Expected behavior A function can only finish reading an outer `const` after its declaration has run, so the window should report the constant's preferences, as it already does for same-scope constants. It should stay `dynamic` when the object may be changed by another activation (writes or escapes elsewhere, repeated calls, exported bindings). ### Impact and scope Static Electron analysis misses `nodeIntegration`, `contextIsolation`, `sandbox` and `preload` for windows created in functions with module-level option constants. An agent reading the result cannot see risky settings that are written in plain literals. The analysis is deterministic and every run I made on Linux gave the same result. Not tested on bundled (Webpack) output. ### REA version main at e7f986c9 (rea-agents 6.8.0 source) ### Runtime and platform Node v24.22.1, Linux x64 ### Target and artifact context Small extracted Electron app directory, shown above. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence ``` summary.explicit_web_preferences = 0 web_preferences_status = dynamic web_preferences = [] ``` ### Typed outcome or recovery expected _No response_ ### Analysis or suspected cause Source-derived: `optionObjectCandidate` in `src/domain/javascript/electronStaticOptionObjects.ts` requires `semanticBindingAtRead(...).hasDefiniteInitializer`. That is false for a read inside a nested function, because the read and the declaration are in different bodies. #4366 / #4400 added same-scope constant resolution and #3536 scoped it to same-scope constants, so this case is not covered yet. I have a fix ready and will open a PR. ### Additional context _No response_
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#4307
No description provided.