import { expect } from "vitest"; import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; import { cliTest } from "../../support/cli/cliFixture.js"; import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js"; import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js"; cliTest.for(["CLI", "stdio MCP"] as const)( "preserves hoisted Electron initialization uncertainty through %s", async (surface, { cli }) => { const root = await createTestTempDirectory("rea-electron-initialization-"); await writeFixtureFiles(root, { "package.json": JSON.stringify({ main: "early.cjs" }), "early.cjs": ` const { BrowserWindow } = require("electron"); createWindow(); const webPreferences = { nodeIntegration: true }; function createWindow() { return new BrowserWindow({ webPreferences }); } require("./late.cjs"); require("./early-loop.cjs"); require("./late-loop.cjs"); throw new Error("must not execute source"); `, "late.cjs": ` const { BrowserWindow } = require("electron"); function createWindow() { return new BrowserWindow({ webPreferences }); } const webPreferences = { nodeIntegration: true }; createWindow(); throw new Error("must not execute source"); `, "early-loop.cjs": ` const { BrowserWindow } = require("electron"); createWindow(); const webPreferences = { nodeIntegration: true }; function createWindow() { for (const item of [0]) return new BrowserWindow({ webPreferences }); } throw new Error("must not execute source"); `, "late-loop.cjs": ` const { BrowserWindow } = require("electron"); const webPreferences = { nodeIntegration: true }; function createWindow() { for (const item of [0]) return new BrowserWindow({ webPreferences }); } createWindow(); throw new Error("must not execute source"); `, }); const result = await analyzeJavaScriptDirectory(surface, cli, root); expect(result.statistics.parse_failures).toBe(0); const windows = result.graph.nodes.filter( ({ kind }) => kind === "browser-window", ); expect(windows).toHaveLength(4); const observations = windows.flatMap(({ observations }) => observations); const inSource = (source: string) => observations.filter( ({ evidence }) => evidence.location.available && evidence.location.value.kind === "source-range" && evidence.location.value.source === source, ); for (const source of ["early.cjs", "early-loop.cjs"]) { expect(inSource(source)).toHaveLength(1); expect(inSource(source)[0]?.properties).toMatchObject({ web_preferences_status: "dynamic", web_preferences: [], }); } for (const source of ["late.cjs", "late-loop.cjs"]) { expect(inSource(source)).toHaveLength(1); expect(inSource(source)[0]?.properties).toMatchObject({ web_preferences_status: "object-literal", web_preferences: [ { name: "nodeIntegration", value: { status: "literal", value: true }, }, ], }); } }, ); cliTest.for([ ["CLI", false], ["stdio MCP", false], ["CLI", true], ["stdio MCP", true], ["CLI", "cached-eval"], ["stdio MCP", "cached-eval"], ["CLI", "constructed-function"], ["stdio MCP", "constructed-function"], ["CLI", "erased-type"], ["stdio MCP", "erased-type"], ["CLI", "module-eval"], ["stdio MCP", "module-eval"], ["CLI", "bound-constructor"], ["stdio MCP", "bound-constructor"], ["CLI", "reflect-constructor"], ["stdio MCP", "reflect-constructor"], ["CLI", "prototype-effect"], ["stdio MCP", "prototype-effect"], ["CLI", "assign-effect"], ["stdio MCP", "assign-effect"], ["CLI", "this-constructor"], ["stdio MCP", "this-constructor"], ["CLI", "alias-prototype"], ["stdio MCP", "alias-prototype"], ["CLI", "alias-assign"], ["stdio MCP", "alias-assign"], ["CLI", "assignment-pattern"], ["stdio MCP", "assignment-pattern"], ["CLI", "iteration-pattern"], ["stdio MCP", "iteration-pattern"], ["CLI", "reflect-read"], ["stdio MCP", "reflect-read"], ["CLI", "catch-read"], ["stdio MCP", "catch-read"], ["CLI", "heritage"], ["stdio MCP", "heritage"], ["CLI", "computed-key"], ["stdio MCP", "computed-key"], ["CLI", "tagged-function"], ["stdio MCP", "tagged-function"], ["CLI", "array-prototype"], ["stdio MCP", "array-prototype"], ] as const)( "preserves classic global initialization uncertainty through %s (%s)", async ([surface, alias], { cli }) => { const root = await createTestTempDirectory( "rea-electron-global-initialization-", ); const activation = alias === "reflect-read" ? "Reflect.get(globalThis, 'createWindow')();" : alias === "catch-read" ? "try { throw globalThis; } catch ({createWindow:start}) { start(); }" : alias === "heritage" ? "class C extends (this.Function('createWindow()')(), Object) {}" : alias === "computed-key" ? "({ [this.eval('createWindow()')]() {} });" : alias === "tagged-function" ? "Function`createWindow()`();" : alias === "array-prototype" ? "Object.setPrototypeOf(Array.prototype, globalThis); [].createWindow();" : alias === "this-constructor" ? "this['Function']('createWindow()')();" : alias === "alias-prototype" ? "const changed = {}; const indirect = changed; Object.setPrototypeOf(changed, globalThis); indirect.createWindow();" : alias === "alias-assign" ? "const changed = {}; const indirect = changed; Object.assign(changed, globalThis); indirect.createWindow();" : alias === "bound-constructor" ? "const F = Function.bind(null); new F('createWindow()')();" : alias === "reflect-constructor" ? "Reflect.construct(Function, ['createWindow()'])();" : alias === "prototype-effect" ? "const changed = {}; Object.setPrototypeOf(changed, globalThis); changed.createWindow();" : alias === "assign-effect" ? "const changed = {}; Object.assign(changed, globalThis); changed.createWindow();" : alias === "assignment-pattern" ? "let start; ({createWindow:start}=globalThis); start();" : alias === "iteration-pattern" ? "for(const {createWindow:start} of [globalThis]) start();" : alias === "module-eval" ? "eval('createWindow()');" : alias === "erased-type" ? "globalThis.createWindow();" : alias === "constructed-function" ? "new Function('createWindow()')();" : alias === "cached-eval" ? "const noop = () => {}; function unused() { noop(); } (0, eval)('createWindow()');" : alias ? "root.createWindow();" : "globalThis.createWindow();"; const source = (early: boolean) => ` ${alias === "erased-type" ? "import type { T } from './types';" : alias === "module-eval" ? "export {};" : ""} ${alias === "module-eval" ? 'import { BrowserWindow } from "electron";' : 'const { BrowserWindow } = require("electron");'} ${alias ? "const root = globalThis;" : ""} ${alias === "reflect-read" || alias === "catch-read" ? "Object.extra = 1; function unusedArrayRead() { [].createWindow; }" : ""} const unrelated = { createWindow() {} }; function unusedReceiverRead() { unrelated.createWindow; } for (const ignored of [1, 2]) { unrelated.createWindow; } ${early ? activation : ""} const webPreferences = { nodeIntegration: true }; function createWindow() { return new BrowserWindow({ webPreferences }); } ${early || alias === "module-eval" ? "" : activation} createWindow(); throw new Error("must not execute source"); `; const extension = alias === "erased-type" ? "ts" : alias === "module-eval" ? "mjs" : "js"; const earlyName = `early.${extension}`; const lateName = `late.${extension}`; await writeFixtureFiles(root, { [earlyName]: source(true), [lateName]: source(false), "index.html": ``, }); const result = await analyzeJavaScriptDirectory(surface, cli, root); expect(result.statistics.parse_failures).toBe(0); const windows = result.graph.nodes.filter( ({ kind }) => kind === "browser-window", ); expect(windows).toHaveLength(2); const inSource = (name: string) => windows .flatMap(({ observations }) => observations) .filter( ({ evidence }) => evidence.location.available && evidence.location.value.kind === "source-range" && evidence.location.value.source === name, ); expect(inSource(earlyName)).toHaveLength(1); expect(inSource(earlyName)[0]?.properties).toMatchObject({ web_preferences_status: "dynamic", web_preferences: [], }); expect(inSource(lateName)).toHaveLength(1); expect(inSource(lateName)[0]?.properties).toMatchObject({ web_preferences_status: "object-literal", web_preferences: [ { name: "nodeIntegration", value: { status: "literal", value: true } }, ], }); }, ); cliTest.for(["CLI", "stdio MCP"] as const)( "preserves aliased prototype, reflected and parameter activation through %s", async (surface, { cli }) => { const root = await createTestTempDirectory( "rea-electron-prototype-parameters-", ); const activations = { callerThis: "function boot() { this.createWindow(); } boot();", boundThis: "function boot() { this.createWindow(); } boot.call(globalThis);", numericPrototype: "Object.assign(Object.prototype, globalThis); (0).createWindow();", booleanPrototype: "Object.assign(Object.prototype, globalThis); true.createWindow();", regexpPrototype: "Object.assign(Object.prototype, globalThis); /x/.createWindow();", nestedGlobalPrototype: "Object.setPrototypeOf(globalThis.window.Array.prototype, globalThis); [].createWindow();", taggedCatch: "function fail() { throw globalThis; } try { fail``; } catch ({createWindow:start}) { start(); }", getterCatch: "const box = { get value() { throw globalThis; } }; try { box.value; } catch ({createWindow:start}) { start(); }", reflectedApply: "const args = [globalThis, 'createWindow']; Reflect.get.apply(Reflect, args)();", projectedReceiver: "const { root } = { root: globalThis, createWindow() {} }; const early = root.createWindow; early();", assignedReceiver: "let root; ({ root } = { root: globalThis, createWindow() {} }); const early = root.createWindow; early();", nestedReceiver: "const { nested: { root } } = { nested: { root: globalThis }, createWindow() {} }; root.createWindow();", catchPropagated: "function fail() { throw globalThis; } try { fail(); } catch ({ createWindow: start }) { start(); }", catchNested: "function fail() { throw globalThis; } try { if (true) fail(); } catch ({ createWindow: start }) { start(); }", objectPrototype: "Object.assign(Object.prototype, globalThis); ({}).createWindow();", stringPrototype: "Object.setPrototypeOf(String.prototype, globalThis); ''.createWindow();", lexicalThisPrototype: "Object.setPrototypeOf(this.Array.prototype, globalThis); [].createWindow();", reflectedTemplate: "globalThis[`Reflect`].get(globalThis, 'createWindow')();", reflectedOptional: "Reflect.get?.(globalThis, 'createWindow')();", reflectedNamespace: "const R = Reflect; R.get(globalThis, 'createWindow')();", reflectedMethod: "const read = Reflect.get; read(globalThis, 'createWindow')();", reflectedBound: "const read = Reflect.get.bind(Reflect); read(globalThis, 'createWindow')();", array: "const receiver = []; const alias = receiver; Object.setPrototypeOf(Array.prototype, globalThis); alias.createWindow();", function: "const receiver = function(){}; const alias = receiver; Object.setPrototypeOf(Function.prototype, globalThis); alias.createWindow();", parameter: "function boot({ createWindow: start } = {}) { start(); } boot(globalThis);", }; const files: Record = {}; for (const [kind, activation] of Object.entries(activations)) { for (const early of [true, false]) { files[`${kind}-${early ? "early" : "late"}.js`] = ` const { BrowserWindow } = require("electron"); ${early ? activation : ""} const webPreferences = { nodeIntegration: true }; function createWindow() { return new BrowserWindow({ webPreferences }); } ${early ? "" : activation} createWindow(); throw new Error("must not execute source"); `; } } files["index.html"] = Object.keys(files) .map((name) => ``) .join(""); await writeFixtureFiles(root, files); const result = await analyzeJavaScriptDirectory(surface, cli, root); expect(result.statistics.parse_failures).toBe(0); const windows = result.graph.nodes.filter( ({ kind }) => kind === "browser-window", ); expect(windows).toHaveLength(50); for (const source of Object.keys(files).filter((name) => name.endsWith(".js"), )) { const observations = windows .flatMap(({ observations }) => observations) .filter( ({ evidence }) => evidence.location.available && evidence.location.value.kind === "source-range" && evidence.location.value.source === source, ); expect(observations).toHaveLength(1); const definite = source.endsWith("-late.js") && !["parameter-", "callerThis-", "boundThis-"].some((prefix) => source.startsWith(prefix), ); expect(observations[0]?.properties).toMatchObject( definite ? { web_preferences_status: "object-literal", web_preferences: [ { name: "nodeIntegration", value: { status: "literal", value: true }, }, ], } : { web_preferences_status: "dynamic", web_preferences: [] }, ); } }, );