Files
reverse-engineering-anything/src/domain/javascript/electronModuleInterop.test.ts
T
9d8e42bdd6 fix(javascript): follow actual receivers in nested intrinsic calls (#1912)
Consume arguments per generic function invocation rather than property-chain
depth. Preserve the callable receiver through call/apply, Reflect.apply and
bound invocation. Cover nested mutations and fresh targets through source
analysis and inert production CLI/MCP fixtures.

Fixes #1909.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-12 01:22:26 +08:00

663 lines
31 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
import { ROLLDOWN_MODULE_INTEROP } from "./javascriptModuleInterop.fixture.js";
const WINDOW =
'new electron.BrowserWindow({webPreferences:{preload:"./preload.js"}});';
const IMPORT = 'let electron = require("electron");';
const WRAP = "electron = __toESM(electron, 1);";
// The upstream runtime uses control-body braces that its bundled printer omits.
// https://github.com/rolldown/rolldown/blob/22a0ab13732d81f2559ad2b7064b688699324889/crates/rolldown/src/runtime/runtime-base.js
const BLOCK_MODULE_INTEROP = ROLLDOWN_MODULE_INTEROP.replace(
'if (from && typeof from === "object" || typeof from === "function") for',
'if (from && typeof from === "object" || typeof from === "function") { for',
)
.replace(
"if (!__hasOwnProp.call(to, key) && key !== except) __defProp",
"if (!__hasOwnProp.call(to, key) && key !== except) { __defProp",
)
.replace(" });", " });\n }")
.replace(" return to;", " }\n return to;");
describe("Electron module interop identity", () => {
it("preserves named APIs through the released Rolldown namespace conversion", () => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
${IMPORT} ${WRAP} ${WINDOW}
new electron.BrowserWindow({webPreferences:{preload:runtimePath}});
electron.ipcMain.handle("ping", () => "pong");
electron.contextBridge.exposeInMainWorld("api", {ping:true});
`);
expect(analysis.electron.browser_windows).toMatchObject([
{ preload_path: "./preload.js" },
{ preload_path: null },
]);
expect(analysis.electron.ipc).toMatchObject([
{ channel: "ping", side: "main" },
]);
expect(analysis.electron.context_bridge_apis).toMatchObject([
{ api_key: "api" },
]);
});
it("follows lexical identities when helper and parameter names change", () => {
const source = `${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`
.replaceAll("__toESM", "namespaceOf")
.replaceAll("__copyProps", "copyNamed")
.replaceAll("mod", "inputModule")
.replaceAll("isNodeMode", "nodeMode")
.replaceAll("target", "namespaceTarget")
.replaceAll("from", "sourceObject");
expect(
analyzeJavaScriptStaticSource(source).electron.browser_windows,
).toMatchObject([{ preload_path: "./preload.js" }]);
});
it("preserves the same operations through control-body braces", () => {
expect(
analyzeJavaScriptStaticSource(
`${BLOCK_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toMatchObject([{ preload_path: "./preload.js" }]);
});
it("keeps additional operations inside a control block unresolved", () => {
const altered = BLOCK_MODULE_INTEROP.replace(
"{ __defProp",
"{ from = unrelated; __defProp",
);
expect(
analyzeJavaScriptStaticSource(`${altered}\n${IMPORT} ${WRAP} ${WINDOW}`)
.electron.browser_windows,
).toEqual([]);
});
});
const BOUND_INTRINSIC_MUTATIONS: [string, string][] = [
[
"intrinsic changed through aliased prebound arguments",
`const objectAlias = Object; const key = "create"; const define = Object.defineProperty; const mutate = define.bind(null, objectAlias, key); mutate({value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed through nested prebinding",
`const first = Object.defineProperty.bind(null, Object); const second = first.bind(null, "create"); second({value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"a replaced bound mutator bind method is unknown",
`const first = Object.defineProperty.bind(null, Object); first.bind = customBind; const second = first.bind(null, "create"); second({value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed through a computed prebound call",
`const mutate = Object.defineProperty.bind(null, Object, "create"); mutate["call"](null, {value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed through a computed prebound apply",
`const mutate = Object.defineProperty.bind(null, Object, "create"); mutate["apply"](null, [{value:customCreate}]); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"a replaced bind intrinsic makes a prebound mutator unknown",
`Function.prototype.bind = customBind; const mutate = Object.defineProperty.bind(null, Object, "create"); mutate({value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
];
describe("Electron module interop uncertainty", () => {
it.each([
...BOUND_INTRINSIC_MUTATIONS,
[
"intrinsic changed through an unresolved mutator target",
`Object.defineProperty(getTarget(), "create", {value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via dynamic apply",
`const args = [Object, "getOwnPropertyNames", {value:() => []}]; Object.defineProperty.apply(null, args); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via spread call",
`const args = [Object, "getOwnPropertyNames", {value:() => []}]; Object.defineProperty.call(null, ...args); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via Reflect.apply",
`const args = [Object, "getOwnPropertyNames", {value:() => []}]; Reflect.apply(Object.defineProperty, null, args); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via spread Reflect.apply",
`const args = [null, [Object, "create", {value:customCreate}]]; Reflect.apply(Object.defineProperty, ...args); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via call",
`Object.defineProperty.call(null, Object, "getOwnPropertyNames", {value:() => []}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed via apply",
`Object.defineProperty.apply(null, [Object, "getOwnPropertyNames", {value:() => []}]); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed with defineProperty",
`Object.defineProperty(Object, "getOwnPropertyNames", {value:() => []}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed with Reflect.set",
`Reflect.set(Object, "getOwnPropertyNames", () => []); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed with an aliased mutator",
`const change = Object.defineProperty; change(Object, "create", {value:customCreate}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed with assign",
`Object.assign(Object, {getOwnPropertyNames:() => []}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"intrinsic changed with defineProperties",
`Object.defineProperties(Function.prototype, {bind:{value:customBind}}); ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"overwritten Object global",
`Object = customObject; ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"overwritten intrinsic through an alias",
`const objectAlias = Object; objectAlias.create = customCreate; ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"overwritten intrinsic through globalThis",
`globalThis.Object.create = customCreate; ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"helper called before initialization",
`${IMPORT} ${WRAP} ${ROLLDOWN_MODULE_INTEROP}`,
],
[
"fake helper",
`${ROLLDOWN_MODULE_INTEROP}\n__toESM = (mod) => ({BrowserWindow:Fake}); ${IMPORT} ${WRAP}`,
],
[
"changed getter",
`${ROLLDOWN_MODULE_INTEROP.replace("from[k]", "to[k]")}\n${IMPORT} ${WRAP}`,
],
[
"replaced copy helper",
`${ROLLDOWN_MODULE_INTEROP}\n__copyProps = () => ({}); ${IMPORT} ${WRAP}`,
],
[
"replaced intrinsic alias",
`${ROLLDOWN_MODULE_INTEROP}\n__defProp = () => ({}); ${IMPORT} ${WRAP}`,
],
[
"shadowed Object",
`const Object = customObject; ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"overwritten intrinsic",
`Object.create = customCreate; ${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP}`,
],
[
"overwritten alias call",
`${ROLLDOWN_MODULE_INTEROP}\n__hasOwnProp.call = customCall; ${IMPORT} ${WRAP}`,
],
[
"overwritten Function bind",
`${ROLLDOWN_MODULE_INTEROP}\nFunction.prototype.bind = customBind; ${IMPORT} ${WRAP}`,
],
[
"conditional conversion",
`${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} if (flag) { ${WRAP} }`,
],
[
"compound assignment",
`${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} electron ||= __toESM(electron, 1);`,
],
[
"other input",
`${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} electron = __toESM(other, 1);`,
],
[
"later overwrite",
`${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} electron = unrelated;`,
],
[
"overwritten constructor",
`${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} electron.BrowserWindow = Fake;`,
],
])("does not infer Electron identity after %s", (_name, setup) => {
expect(
analyzeJavaScriptStaticSource(`${setup}\n${WINDOW}`).electron
.browser_windows,
).toEqual([]);
});
});
describe("Electron module interop helper results", () => {
it.each([
'const {BrowserWindow} = __toESM(require("electron"), 1); new BrowserWindow({});',
'const {BrowserWindow: Window} = __toESM(require("electron"), 1); new Window({});',
'const {default: {BrowserWindow}} = __toESM(require("electron"), 1); new BrowserWindow({});',
'const Window = __toESM(require("electron"), 1).BrowserWindow; new Window({});',
])("preserves a proven result projection: %s", (source) => {
expect(
analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}\n${source}`)
.electron.browser_windows,
).toHaveLength(1);
});
it.each([
'const {[key]: Window} = __toESM(require("electron"), 1); new Window({});',
'const {...electron} = __toESM(require("electron"), 1); new electron.BrowserWindow({});',
'let {BrowserWindow} = __toESM(require("electron"), 1); BrowserWindow = Fake; new BrowserWindow({});',
])("keeps an uncertain result projection unresolved: %s", (source) => {
expect(
analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}\n${source}`)
.electron.browser_windows,
).toEqual([]);
});
it.each([
"",
", 0",
", false",
", true",
", 1",
', "node"',
', ""',
", null",
])("preserves named exports with a literal node mode: %s", (mode) => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
const fresh = __toESM(require("electron")${mode});
new fresh.BrowserWindow({});
const {BrowserWindow} = __toESM(require("electron")${mode});
new BrowserWindow({});
let electron = require("electron");
electron = __toESM(electron${mode}); ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(3);
});
it.each(["", ", 0", ", false"])(
"does not assume a module-valued default with node mode: %s",
(mode) => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
const original = require("electron");
original.__esModule = true;
original.default = {BrowserWindow: Fake};
const {default: {BrowserWindow}} = __toESM(require("electron")${mode});
new BrowserWindow({});
const other = __toESM(require("electron")${mode}).default;
new other.BrowserWindow({});
new (__toESM(require("electron")${mode}).default.BrowserWindow)({});
`);
expect(analysis.electron.browser_windows).toEqual([]);
},
);
it.each(["__toESM", "identity"])(
"rejects side-effecting extra arguments to %s",
(helper) => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
function identity(mod) { return mod; }
const electron = ${helper}(require("electron"), mutate()); ${WINDOW}
`);
expect(analysis.electron.browser_windows).toEqual([]);
},
);
it.each([
"const __toESM = () => ({BrowserWindow:FakeWindow});",
"const __toESM = async (mod) => mod;",
"function* __toESM(mod) { return mod; }",
"const __toESM = (this && this.__toESM) || ((mod) => mod);",
])("keeps an unproven fresh result unresolved: %s", (helper) => {
const analysis = analyzeJavaScriptStaticSource(`${helper}
const electron = __toESM(require("electron"), 1); ${WINDOW}
new (__toESM(require("electron"), 1)).BrowserWindow({});
`);
expect(analysis.electron.browser_windows).toEqual([]);
});
it("preserves a proven fresh namespace and a pure identity helper", () => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
const electron = __toESM(require("electron"), 1); ${WINDOW}
new (__toESM(require("electron"), 1)).BrowserWindow({});
function passthrough(value) {return value;}
const plain = passthrough(require("electron"));
new plain.BrowserWindow({});
`);
expect(analysis.electron.browser_windows).toHaveLength(3);
});
});
describe("Electron module interop pattern writes", () => {
it.each([
"({Object} = replacements);",
"[Function] = replacements;",
"({nested:{Object}} = replacements);",
"({replacement:Object = customObject} = replacements);",
"({...Object} = replacements);",
"[...Function] = replacements;",
"for ({Object} of replacements) {}",
"for ([Function] in replacements) {}",
])("rejects an intrinsic replaced through %s", (write) => {
const analysis = analyzeJavaScriptStaticSource(`
${write} ${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.parse_error_count).toBe(0);
expect(analysis.electron.browser_windows).toEqual([]);
});
it.each([
"({Object:unrelated} = replacements);",
"function decoy(Object) {({Object} = replacements);}",
])("preserves intrinsic identity for an unrelated target: %s", (write) => {
const analysis = analyzeJavaScriptStaticSource(`
${write} ${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.parse_error_count).toBe(0);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
it("preserves captured operations after later property destructuring", () => {
const analysis = analyzeJavaScriptStaticSource(`
${ROLLDOWN_MODULE_INTEROP} ({create:Object.create} = replacements);
${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
});
describe("Electron module interop intrinsic captures", () => {
it.each([
"var captured = Object.defineProperty;",
"const {defineProperty:captured} = Object;",
"var first = Object.defineProperty; const captured = first;",
])("retains the original function capture through %s", (capture) => {
const runtime = ROLLDOWN_MODULE_INTEROP.replace(
"__defProp = Object.defineProperty",
"__defProp = captured",
);
const analysis = analyzeJavaScriptStaticSource(`
${capture} Object.defineProperty = custom;
${runtime} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
it.each(["var", "const"])(
"keeps an intrinsic alias read before its %s initializer unresolved",
(kind) => {
const runtime = ROLLDOWN_MODULE_INTEROP.replace(
"__defProp = Object.defineProperty",
"__defProp = later",
);
const analysis = analyzeJavaScriptStaticSource(`
${runtime} ${kind} later = Object.defineProperty;
${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toEqual([]);
},
);
it("requires the helper's intrinsic binding to initialize before conversion", () => {
const runtime = ROLLDOWN_MODULE_INTEROP.replace(
"var __defProp = Object.defineProperty;",
"",
);
const analysis = analyzeJavaScriptStaticSource(`
var captured = Object.defineProperty;
${runtime} ${IMPORT} ${WRAP} ${WINDOW}
var __defProp = captured;
`);
expect(analysis.electron.browser_windows).toEqual([]);
});
});
describe("Electron module interop capture and scope", () => {
it.each(["metadata", "alias"])(
"recognizes a hoisted function target through %s",
(target) => {
const analysis = analyzeJavaScriptStaticSource(`
const alias = metadata;
Object.defineProperty(${target}, "x", {value:1});
function metadata() {}
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
},
);
it.each([
"var metadata = function () {};",
"const metadata = () => {};",
"class metadata {}",
])("does not hoist fresh targets from %s", (declaration) => {
const analysis = analyzeJavaScriptStaticSource(`
Object.defineProperty(metadata, "x", {value:1});
${declaration}
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toEqual([]);
});
it.each([
'const {defineProperty:change} = Object; change(Object,"create",{value:customCreate});',
"const objectAlias = Object; const {assign:change} = objectAlias; change(Object,{create:customCreate});",
'const {set:change} = Reflect; change(Object,"create",customCreate);',
'const {Object:{defineProperty:change}} = globalThis; change(Object,"create",{value:customCreate});',
])("rejects a projected intrinsic mutator alias: %s", (write) => {
const source = `${write} ${ROLLDOWN_MODULE_INTEROP}
${IMPORT} ${WRAP} ${WINDOW}
`.replaceAll("__toESM", "namespaceOf");
const analysis = analyzeJavaScriptStaticSource(source);
expect(analysis.parse_error_count).toBe(0);
expect(analysis.electron.browser_windows).toEqual([]);
});
it.each([
"const metadata = {};",
"const metadata = [];",
"const initial = {}; const metadata = initial;",
"const metadata = () => {};",
"const metadata = function () {};",
"const metadata = async () => {};",
"const metadata = function* () {};",
"const metadata = class {};",
"const metadata = /local/;",
"function metadata() {}",
"class metadata {}",
])(
"keeps a fresh mutator target separate from intrinsic identity: %s",
(setup) => {
const analysis = analyzeJavaScriptStaticSource(`
${setup} Object.defineProperty(metadata,"x",{value:1});
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
},
);
it.each([
"const metadata = getMetadata();",
"let metadata = {}; metadata = Object;",
"Object.defineProperty(metadata,'x',{value:1}); const metadata = {};",
"const metadata = {intrinsic:Object}; Object.defineProperty(metadata.intrinsic,'create',{value:customCreate});",
"const metadata = class {static {Object.create = customCreate;}};",
"@(() => Object) class metadata {}",
])("keeps an unproven mutator target unresolved: %s", (setup) => {
const analysis = analyzeJavaScriptStaticSource(`
${setup} Object.defineProperty(metadata,"x",{value:1});
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toEqual([]);
});
it("does not reject unrelated intrinsic property definitions", () => {
const analysis = analyzeJavaScriptStaticSource(`
Object.defineProperty(Object, "unrelated", {value:1});
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
it("keeps opaque targets in a generic helper separate from outer module identity", () => {
const analysis = analyzeJavaScriptStaticSource(`
function genericAssign() { Object.assign.apply(null, arguments); }
${ROLLDOWN_MODULE_INTEROP} ${IMPORT} ${WRAP} ${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
it("rejects an opaque intrinsic mutation in the namespace's own callable", () => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
function openWindow(args) {
Object.defineProperty.apply(null, args);
${IMPORT} ${WRAP} ${WINDOW}
}
`);
expect(analysis.electron.browser_windows).toEqual([]);
});
it("preserves captured intrinsics across later vendor polyfills and callable writes", () => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
Object.create = customCreate;
${IMPORT} ${WRAP}
function runLoop(key) { globalThis[key] = this; }
function polyfill() { Object.create = customCreate; }
${WINDOW}
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
it("keeps a shadowed namespace separate from the converted module", () => {
const analysis = analyzeJavaScriptStaticSource(`${ROLLDOWN_MODULE_INTEROP}
${IMPORT} ${WRAP} ${WINDOW}
function decoy(electron) { ${WINDOW} }
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});
});
describe("Electron module interop generic invocations", () => {
it.each([
'const change = Function.prototype.call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const call = Function.prototype.call; const change = call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const change = Function.prototype.call.bind(Object.defineProperty,null,Object,"create"); change({value:customCreate});',
'Function.prototype.call.apply(Object.defineProperty,[null,Object,"create",{value:customCreate}]);',
'Reflect.apply(Function.prototype.call,Object.defineProperty,[null,Object,"create",{value:customCreate}]);',
'const change = Function.prototype.apply.bind(Object.defineProperty); change(null,[Object,"create",{value:customCreate}]);',
'Reflect.apply(Function.prototype.apply,Object.defineProperty,[null,[Object,"create",{value:customCreate}]]);',
'const first = Function.prototype.call.bind(Object.defineProperty,null,Object,"create"); const change = first.bind(fake); change({value:customCreate});',
"const change = Function.prototype.call.bind(Object.defineProperty); change(null,...args);",
"const change = Function.prototype.apply.bind(Object.defineProperty); change(null,args);",
'const change = Function.prototype.call.bind(Object.defineProperty,null,...args); change(metadata,"x",{value:1});',
'Function.prototype.call.call.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'Object.defineProperty.call.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'Function.prototype.apply.apply(Object.defineProperty,[null,[Object,"create",{value:customCreate}]]);',
'const change = Object.defineProperty.call.bind(Object.defineProperty); change(null,Object,"create",{value:customCreate});',
'const change = Function.prototype.call.call.bind(Object.defineProperty,null,Object,"create"); change({value:customCreate});',
])("follows the callable receiver of a generic invocation: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toEqual([]);
});
it.each([
'const change = Function.prototype.call.bind(Object.defineProperty); change(null,metadata,"x",{value:1});',
'const change = Function.prototype.call.bind(Object.defineProperty,null,metadata,"x"); change({value:1});',
'Function.prototype.call.call(Object.defineProperty,null,metadata,"x",{value:1});',
'const change = Function.prototype.apply.bind(Object.defineProperty); change(null,[metadata,"x",{value:1}]);',
'Reflect.apply(Function.prototype.apply,Object.defineProperty,[null,[metadata,"x",{value:1}]]);',
'Function.prototype.call.call.call(Object.defineProperty,null,metadata,"x",{value:1});',
'Function.prototype.apply.apply(Object.defineProperty,[null,[metadata,"x",{value:1}]]);',
'const change = Object.defineProperty.call.bind(Object.defineProperty); change(null,metadata,"x",{value:1});',
])("preserves fresh targets through a generic invocation: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`const metadata = {}; ${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toHaveLength(1);
});
});
describe("Electron module interop bound mutators", () => {
it.each([
'const change = Object.defineProperty.bind(null); change(Object,"create",{value:customCreate});',
'const change = Object.defineProperty.bind(null,Object,"create"); change({value:customCreate});',
'const first = Object.defineProperty.bind(null,Object); const change = first; change("create",{value:customCreate});',
'const change = Object.defineProperty.bind(null,Object); change.call(null,"create",{value:customCreate});',
'const change = Object.defineProperty.bind(null,Object); change.apply(null,["create",{value:customCreate}]);',
'const change = Object.defineProperty.bind(null,Object); Reflect.apply(change,null,["create",{value:customCreate}]);',
'Object.defineProperty.bind(null,Object,"create")({value:customCreate});',
'const change = Object.defineProperty.bind(null,...args); change(Object,"create",{value:customCreate});',
'const metadata = {}; Object.defineProperty.call(...args,metadata,"x",{value:1});',
'const metadata = {}; Object.defineProperty.apply(...args,[metadata,"x",{value:1}]);',
'const change = Function.prototype.call.bind(Object.defineProperty,null); change(Object,"create",{value:customCreate});',
'const first = Function.prototype.call.bind(Object.defineProperty,null); const change = first.bind(customReceiver,Object,"create"); change({value:customCreate});',
'const change = Function.prototype.apply.bind(Object.defineProperty,null); change([Object,"create",{value:customCreate}]);',
'Function.prototype.call.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'Function.prototype.apply.call(Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
'const uncurry = Function.prototype.call.bind(Function.prototype.call); uncurry(Object.defineProperty,null,Object,"create",{value:customCreate});',
'const change = Function.prototype.bind.call(Object.defineProperty,null); change(Object,"create",{value:customCreate});',
'const bind = Function.prototype.call.bind(Function.prototype.bind); const change = bind(Object.defineProperty,null); change(Object,"create",{value:customCreate});',
'const change = Function.prototype.bind.apply(Object.defineProperty,[null,Object,"create"]); change({value:customCreate});',
'const change = Reflect.apply(Function.prototype.bind,Object.defineProperty,[null,Object,"create"]); change({value:customCreate});',
'const first = Object.defineProperty.bind(null,Object); const change = Function.prototype.bind.call(first,null,"create"); change({value:customCreate});',
'Reflect.apply = Object.defineProperty; Reflect.apply(Object,"create",{value:customCreate});',
'const apply = Reflect.apply; Reflect.apply = Object.defineProperty; Reflect.apply(Object,"create",{value:customCreate});',
'const change = Reflect.set.bind(null,Object,"create"); change(customCreate);',
'const change = Object.defineProperty.call; change.call(Object.defineProperty,null,Object,"create",{value:customCreate});',
'const {apply:change} = Object.defineProperty; change.call(Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
'const change = Reflect.apply.call; change.call(Reflect.apply,null,Object.defineProperty,null,[Object,"create",{value:customCreate}]);',
'Reflect.set({},"create",customCreate,Object);',
'const metadata = {}; Reflect.set(metadata,"create",customCreate,Object);',
'const change = Reflect.set.bind(null,{},"create",customCreate); change(Object);',
'Reflect.apply(Reflect.set,null,[{},"create",customCreate,Object]);',
'Reflect.set({},"create",customCreate,...receivers);',
'Reflect.set({},"create",...valuesAndReceivers);',
"Reflect.set({},...keysValuesAndReceivers);",
'Reflect.set({},"create",customCreate,receiver);',
])("rejects intrinsic writes through a bound mutator: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toEqual([]);
});
it.each([
'const change = Object.defineProperty.bind(null); change(metadata,"x",{value:1});',
'const change = Object.defineProperty.bind(null,metadata,"x"); change({value:1});',
'const first = Object.defineProperty.bind(null,metadata); const change = first.bind(null,"x"); change({value:1});',
'const change = Function.prototype.call.bind(Object.defineProperty,null); change(metadata,"x",{value:1});',
'const change = Function.prototype.apply.bind(Object.defineProperty,null); change([metadata,"x",{value:1}]);',
'const change = Function.prototype.bind.call(Object.defineProperty,null,metadata,"x"); change({value:1});',
'const bind = Function.prototype.call.bind(Function.prototype.bind); const change = bind(Object.defineProperty,null,metadata,"x"); change({value:1});',
'const change = Function.prototype.bind.apply(Object.defineProperty,[null,metadata,"x"]); change({value:1});',
'const change = Reflect.apply(Function.prototype.bind,Object.defineProperty,[null,metadata,"x"]); change({value:1});',
'const apply = Reflect.apply; Reflect.apply = replacement; apply(Object.defineProperty,null,[metadata,"x",{value:1}]);',
'const change = Object.defineProperty.bind(null,Object,"create");',
'Object.defineProperty.call(null,metadata,"x",{value:1});',
'Object.defineProperty.apply(null,[metadata,"x",{value:1}]);',
'const change = Object.defineProperty.call; change.call(Object.defineProperty,null,metadata,"x",{value:1});',
'const {apply:change} = Object.defineProperty; change.call(Object.defineProperty,null,[metadata,"x",{value:1}]);',
'Reflect.set(metadata,"x",1,{});',
'const receiver = {}; Reflect.set({},"x",1,receiver);',
])("keeps a fresh bound target or unused binding separate: %s", (write) => {
expect(
analyzeJavaScriptStaticSource(
`const metadata = {}; ${write}\n${ROLLDOWN_MODULE_INTEROP}\n${IMPORT} ${WRAP} ${WINDOW}`,
).electron.browser_windows,
).toHaveLength(1);
});
});
it("keeps unrelated exports after an aliased prebound key write", () => {
const analysis = analyzeJavaScriptStaticSource(`
const electron = require("electron");
const key = "Unrelated";
const mutate = Object.defineProperty.bind(null, electron, key);
mutate({value: custom});
new electron.BrowserWindow({});
`);
expect(analysis.electron.browser_windows).toHaveLength(1);
});