Files
reverse-engineering-anything/src/domain/javascript/electronWrappedCallPaths.test.ts
T
Liu Jie 17488bd639 fix(javascript): preserve uncertainty in compound assignment results (#4342)
* fix(javascript): preserve uncertainty in compound assignment results

* fix(javascript): retain callable logical assignment alternatives

* fix(javascript): read prior module export assignment values

* fix: keep mutable module export assignment candidates conservative

* fix: preserve mixed receiver values and wrapped assignment targets

* fix: carry mutable export uncertainty through copied values
2026-10-17 00:24:09 +08:00

1413 lines
49 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { analyzeJavaScriptStaticSource } from "./javascriptStaticAnalysis.js";
const bundle = (entry: string) => `(() => {
const modules = {
2(module) { module.exports = require("electron"); },
5(module,exports,load) {
const electron=load(2);
Object.defineProperty(exports,"__esModule",{value:true});
exports.sender = function(channel) {return (...args)=>send(channel,...args)};
function send(channel,...args) {electron.ipcRenderer.send(channel,...args)}
exports.events = {addListener(channel,handler) {
const guarded = (event,...args) => {if (allowed(event)) handler(event,...args)};
return electron.ipcMain.addListener(channel,guarded);
}};
},
9(module,exports,load) {const electron=load(2), helper=load(5); ${entry}}
};
function load(id) {const m={exports:{}};modules[id].call(m.exports,m,m.exports,load);return m.exports;}
load(9);
})();`;
describe("wrapped Electron control-flow alternatives", () => {
it.each(["flag ? copy : cut", "flag ? copy : unknownHandler", "choose()"])(
"does not select one IPC handler from %s",
(handler) => {
const result = analyzeJavaScriptStaticSource(`
const e = require("electron");
const copy = event => event.sender.copy();
const cut = event => event.sender.cut();
function choose() { if (flag) return copy; }
function register(channel, handler) { e.ipcMain.on(channel, handler); }
register("test:copy", ${handler});
`);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({
channel: "test:copy",
side: "main",
handler_kind: "dynamic-expression",
resolution_basis: "wrapper-substitution",
}),
);
expect(result.electron.call_paths ?? []).toEqual([]);
expect(result.parse_status).toBe("partial");
},
);
it.each(["'ready'", "false", "0", "''", "({})", "(()=>{})"])(
"short-circuits nullish fallback for %s in callbacks and seed calls",
(left) => {
for (const source of [
`e.ipcMain.on("test:copy", event => { ${left} ?? event.sender.copy(); });`,
`${left} ?? e.ipcMain.on("test:copy", event => event.sender.copy());`,
`e.ipcMain.on("test:copy", event => { const selected = ${left} ?? (()=>event.sender.copy()); selected(); });`,
])
expect(
analyzeJavaScriptStaticSource(`const e=require("electron");${source}`)
.electron.call_paths ?? [],
).toEqual([]);
},
);
it.each(["null", "undefined", "void 0"])(
"follows nullish fallback for %s",
(left) => {
const result = analyzeJavaScriptStaticSource(`const e=require("electron");
${left} ?? e.ipcMain.on("test:copy", event => { ${left} ?? event.sender.copy(); });`);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
terminal: "ipc-event.sender.copy",
conditions: [],
}),
);
},
);
it("retains a nullish guard for unknown fallback rather than a falsy guard", () => {
for (const source of [
`e.ipcMain.on("test:copy", event => { value ?? event.sender.copy(); });`,
`value ?? e.ipcMain.on("test:copy", event => event.sender.copy());`,
]) {
const result = analyzeJavaScriptStaticSource(
`const e=require("electron");${source}`,
);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
terminal: "ipc-event.sender.copy",
conditions: [
expect.objectContaining({
expression: "(value) === null || (value) === undefined",
}),
],
}),
);
}
});
it.each([
"if (flag) return copy; else return cut; return dead;",
"if (flag) { return copy; } return cut;",
"if (flag) { if (other) return copy; else return cut; } else return cut; return dead;",
])("retains every possible factory return: %s", (body) => {
const result = analyzeJavaScriptStaticSource(`const e=require("electron");
const copy=()=>e.ipcRenderer.send("test:copy"), cut=()=>e.ipcRenderer.send("test:cut"), dead=()=>e.ipcRenderer.send("test:dead");
function choose(){${body}}
e.contextBridge.exposeInMainWorld("desktop", {copy:choose()});`);
expect(
[
...new Set(result.electron.call_paths?.map((path) => path.channel)),
].sort(),
).toEqual(["test:copy", "test:cut"]);
const paths = result.electron.call_paths ?? [];
expect(
paths
.find(({ channel }) => channel === "test:copy")
?.conditions.map(({ expression }) => expression),
).toContain("flag");
expect(
paths
.filter(({ channel }) => channel === "test:cut")
.some((path) =>
path.conditions.some(({ expression }) => expression === "!(flag)"),
),
).toBe(true);
});
});
it("preserves an observed empty contextBridge API object", () => {
const result = analyzeJavaScriptStaticSource(`
const electron = require("electron");
electron.contextBridge.exposeInMainWorld("desktop", {});
`);
expect(result.electron.context_bridge_apis).toContainEqual(
expect.objectContaining({
api_key: "desktop",
api_status: "object-literal",
members: [],
unknown_members: 0,
}),
);
expect(result.electron.context_bridge_apis?.[0]).not.toHaveProperty(
"resolution_basis",
);
expect(result.parse_status).toBe("complete");
});
it.each([
[
"explicit nested paths after an unknown spread",
`{ ...extra, a: { b: 1 }, c: 2 }`,
["a", "a.b", "c"],
1,
"dynamic",
],
[
"a direct object literal",
`{ a: { b: 1 }, c: 2 }`,
["a", "a.b", "c"],
0,
"object-literal",
],
["a dynamic API argument", "getApi()", [], 1, "dynamic"],
] as const)(
"preserves directly observed nested bridge members for %s",
(_name, api, members, unknownMembers, apiStatus) => {
const result = analyzeJavaScriptStaticSource(`
const { contextBridge } = require("electron");
contextBridge.exposeInMainWorld("api", ${api});
`);
expect(result.electron.context_bridge_apis).toContainEqual(
expect.objectContaining({
api_key: "api",
api_status: apiStatus,
members,
unknown_members: unknownMembers,
}),
);
},
);
describe("wrapped contextBridge exposure methods", () => {
it("retains the original expression for a dynamic isolated world id", () => {
const result = analyzeJavaScriptStaticSource(
bundle(
`const worldId = selectWorld(); electron.contextBridge.exposeInIsolatedWorld(worldId, "isolatedApi", {ping: true});`,
),
);
// The proven Webpack bootstrap resolves `load(2)` directly, so the complete
// direct observation is retained instead of a wrapper substitution.
expect(result.electron.context_bridge_apis).toEqual([
expect.objectContaining({
world: "isolated",
world_id: {
status: "dynamic",
value: null,
expression: "worldId",
},
api_key: "isolatedApi",
api_status: "object-literal",
members: ["ping"],
unknown_members: 0,
module_key: "9",
module_runtime: "webpack-bootstrap@0",
}),
]);
expect(result.electron.context_bridge_apis[0]).not.toHaveProperty(
"resolution_basis",
);
expect(result.parse_status).toBe("partial");
});
it("does not report a conditionally selected world as unconditional", () => {
const result = analyzeJavaScriptStaticSource(
bundle(
`if (useIsolated) electron.contextBridge.exposeInIsolatedWorld(1001, "isolatedApi", {ping: true});`,
),
);
expect(result.electron.context_bridge_apis).not.toContainEqual(
expect.objectContaining({
api_key: "isolatedApi",
resolution_basis: "wrapper-substitution",
}),
);
expect(result.parse_status).toBe("partial");
});
it("does not report conditionally resolved world ids as unconditional", () => {
const result = analyzeJavaScriptStaticSource(
bundle(
`const worldId = useFirstWorld ? 1001 : 1002; electron.contextBridge.exposeInIsolatedWorld(worldId, "isolatedApi", {ping: true});`,
),
);
expect(result.electron.context_bridge_apis).not.toContainEqual(
expect.objectContaining({
api_key: "isolatedApi",
resolution_basis: "wrapper-substitution",
}),
);
expect(result.parse_status).toBe("partial");
});
});
it("connects exposed closures to IPC sends and text clipboard calls", () => {
const result = analyzeJavaScriptStaticSource(
bundle(`
const text = value => navigator.clipboard.writeText(value);
const api={copy:helper.sender("example:copy"),copyText:text};
electron.contextBridge.exposeInMainWorld("desktop",api);
`),
);
expect(result.electron.context_bridge_apis).toMatchObject([
{ api_key: "desktop", members: ["copy", "copyText"] },
]);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({
side: "renderer",
channel: "example:copy",
operation: "send",
}),
);
expect(result.electron.call_paths).toEqual(
expect.arrayContaining([
expect.objectContaining({
entry: "desktop.copy",
channel: "example:copy",
terminal: "electron.ipcRenderer.send",
}),
expect.objectContaining({
entry: "desktop.copyText",
terminal: "navigator.clipboard.writeText",
}),
]),
);
const ipcFinding = result.electron.ipc.find(
({ channel }) => channel === "example:copy",
);
const ipcPath = result.electron.call_paths?.find(
({ entry }) => entry === "desktop.copy",
);
expect(ipcFinding).toBeDefined();
expect(ipcPath?.steps.at(-1)).toBeDefined();
expect(ipcFinding?.location).toEqual(ipcPath?.steps.at(-1)?.location);
});
it("resolves a helper factory listed after its bundle consumer", () => {
const result = analyzeJavaScriptStaticSource(`(() => {
const modules = {
2(module) { module.exports = require("electron"); },
9(module,exports,load) {
const electron=load(2),helper=load(5);
electron.contextBridge.exposeInMainWorld("desktop", {copy:helper.sender("after:copy")});
},
5(module,exports,load) {
const electron=load(2);
exports.sender=channel=>()=>electron.ipcRenderer.send(channel);
}
};
function load(id) { const m={exports:{}}; modules[id].call(m.exports,m,m.exports,load); return m.exports; }
load(9);
})();`);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry: "desktop.copy",
channel: "after:copy",
terminal: "electron.ipcRenderer.send",
}),
);
});
it("substitutes guarded listener callbacks and preserves guard evidence", () => {
const result = analyzeJavaScriptStaticSource(
bundle(
`helper.events.addListener("example:copy",event=>event.sender.copy());`,
),
);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({
side: "main",
channel: "example:copy",
operation: "on",
}),
);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry_kind: "ipc-handler",
channel: "example:copy",
terminal: "ipc-event.sender.copy",
conditions: [expect.objectContaining({ expression: "allowed(event)" })],
}),
);
const ipcFinding = result.electron.ipc.find(
({ channel, operation }) =>
channel === "example:copy" && operation === "on",
);
const ipcStep = result.electron.call_paths
?.find(({ entry_kind }) => entry_kind === "ipc-handler")
?.steps.find(({ kind }) => kind === "ipc");
expect(ipcFinding).toBeDefined();
expect(ipcStep).toBeDefined();
expect(ipcFinding?.location).toEqual(ipcStep?.location);
});
it("retains distinct wrapper invocation ranges for one IPC API source", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const copy=event=>event.sender.copy();
function register(channel,handler){e.ipcMain.on(channel,handler);}
register("same:channel",copy);
register("same:channel",copy);
`);
const findings = result.electron.ipc.filter(
({ resolution_basis, channel }) =>
resolution_basis === "wrapper-substitution" && channel === "same:channel",
);
expect(findings).toHaveLength(2);
expect(findings[0]?.location).toEqual(findings[1]?.location);
expect(findings[0]?.invocation_location).toBeDefined();
expect(findings[1]?.invocation_location).toBeDefined();
expect(findings[0]?.invocation_location).not.toEqual(
findings[1]?.invocation_location,
);
});
it("keeps one wrapper registration when one invocation reaches it twice", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const copy=event=>event.sender.copy();
function register(channel,handler){e.ipcMain.on(channel,handler);}
function twice(channel){register(channel,copy);register(channel,copy);}
twice("same:invocation");
`);
expect(
result.electron.ipc.filter(
({ resolution_basis, channel }) =>
resolution_basis === "wrapper-substitution" &&
channel === "same:invocation",
),
).toHaveLength(1);
});
it("keeps a directly observed renderer operation singular when tracing its exposed callback", () => {
const result = analyzeJavaScriptStaticSource(`
const {contextBridge,ipcRenderer}=require("electron");
contextBridge.exposeInMainWorld("desktop",{ping:()=>ipcRenderer.invoke("ping")});
`);
expect(result.electron.ipc).toHaveLength(1);
expect(result.electron.ipc[0]).toMatchObject({
channel: "ping",
operation: "invoke",
});
expect(result.electron.ipc[0]).not.toHaveProperty("resolution_basis");
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({ entry: "desktop.ping", channel: "ping" }),
);
});
it("does not identify overwritten Electron namespaces or shadowed require", () => {
for (const source of [
`const e=require("electron");e.ipcMain={on(){}};e.ipcMain.on("bad",event=>event.sender.copy());`,
`function require(){return {ipcMain:{on(){}}}};const e=require("electron");e.ipcMain.on("bad",event=>event.sender.copy());`,
])
expect(
analyzeJavaScriptStaticSource(source).electron.call_paths ?? [],
).toEqual([]);
});
it("preserves guarded alternatives in direct exposed object literals", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const copy=()=>e.ipcRenderer.send("copy");
const cut=()=>e.ipcRenderer.send("cut");
e.contextBridge.exposeInMainWorld("desktop", {action: flag ? copy : cut});
`);
expect(result.electron.call_paths).toEqual(
expect.arrayContaining([
expect.objectContaining({
entry: "desktop.action",
channel: "copy",
conditions: [expect.objectContaining({ expression: "flag" })],
}),
expect.objectContaining({
entry: "desktop.action",
channel: "cut",
conditions: [expect.objectContaining({ expression: "!(flag)" })],
}),
]),
);
});
it("preserves bridge member segments and excludes prototype setters", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const write=()=>e.ipcRenderer.send("write");
e.contextBridge.exposeInMainWorld("desktop", {
"nested.write": write,
nested: { write },
__proto__: { inherited: write },
["__proto__"]: { own: write },
});
`);
expect(result.electron.call_paths).toEqual(
expect.arrayContaining([
expect.objectContaining({
entry: "desktop.nested.write",
entry_path: ["desktop", "nested.write"],
}),
expect.objectContaining({
entry: "desktop.nested.write",
entry_path: ["desktop", "nested", "write"],
}),
expect.objectContaining({
entry: "desktop.__proto__.own",
entry_path: ["desktop", "__proto__", "own"],
}),
]),
);
expect(result.electron.call_paths).not.toEqual(
expect.arrayContaining([
expect.objectContaining({
entry_path: ["desktop", "__proto__", "inherited"],
}),
]),
);
});
it("follows an explicit object-literal prototype lookup in a bridge wrapper", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const prototype={send:()=>e.ipcRenderer.send("through-prototype")};
const object={__proto__:prototype};
e.contextBridge.exposeInMainWorld("desktop", {
action:()=>object.__proto__.send(),
});
`);
expect(result.electron.call_paths).toEqual(
expect.arrayContaining([
expect.objectContaining({
entry_path: ["desktop", "action"],
channel: "through-prototype",
}),
]),
);
});
it("does not spread a large nested bridge member list into function arguments", () => {
const members = Array.from(
{ length: 125_000 },
(_, index) => `m${index}(){}`,
).join(",");
const source = `const e=require("electron");e.contextBridge.exposeInMainWorld("desktop",{nested:{${members}}});`;
expect(() => analyzeJavaScriptStaticSource(source)).not.toThrow();
});
it("reports nested bridge depth truncation while retaining shallow paths", () => {
const nested =
Array.from({ length: 130 }, (_, index) => `level${index}:{`).join("") +
`deep:()=>e.ipcRenderer.send("deep")` +
"}".repeat(130);
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
e.contextBridge.exposeInMainWorld("desktop", {
shallow:()=>e.ipcRenderer.send("shallow"), ${nested}
});
`);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({ entry: "desktop.shallow", channel: "shallow" }),
);
expect(result.limitations.join(" ")).toContain("depth 128");
});
it("preserves conditions when a wrapper dispatches through conditional external methods", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const send=flag ? e.ipcRenderer.send : e.ipcRenderer.invoke;
e.contextBridge.exposeInMainWorld("desktop", {action:()=>send("channel")});
`);
expect(result.electron.call_paths).toEqual(
expect.arrayContaining([
expect.objectContaining({
entry: "desktop.action",
channel: "channel",
terminal: "electron.ipcRenderer.send",
conditions: [expect.objectContaining({ expression: "flag" })],
}),
expect.objectContaining({
entry: "desktop.action",
channel: "channel",
terminal: "electron.ipcRenderer.invoke",
conditions: [expect.objectContaining({ expression: "!(flag)" })],
}),
]),
);
expect(
result.electron.ipc.filter(
({ resolution_basis, channel }) =>
resolution_basis === "wrapper-substitution" && channel === "channel",
),
).toEqual([]);
expect(result.parse_status).toBe("partial");
});
it("keeps conditional IPC channels unknown instead of reporting them unconditionally", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const channel=flag ? "copy" : "cut";
e.contextBridge.exposeInMainWorld("desktop", {action:()=>e.ipcRenderer.send(channel)});
`);
expect(result.electron.call_paths ?? []).toEqual([]);
expect(result.parse_status).toBe("partial");
});
it("retains enclosing branch predicates without emitting unconditional wrapper findings", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
function expose(key,api){e.contextBridge.exposeInMainWorld(key,api);}
function register(channel,handler){e.ipcMain.on(channel,handler);}
if(flag){
expose("conditional",{copy:()=>navigator.clipboard.writeText("copy")});
register("only:flag",event=>event.sender.copy());
}
`);
expect(result.electron.context_bridge_apis).not.toContainEqual(
expect.objectContaining({
api_key: "conditional",
resolution_basis: "wrapper-substitution",
}),
);
expect(result.electron.ipc).not.toContainEqual(
expect.objectContaining({
channel: "only:flag",
resolution_basis: "wrapper-substitution",
}),
);
for (const entry of ["conditional.copy", "only:flag"])
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry,
conditions: [expect.objectContaining({ expression: "flag" })],
}),
);
expect(result.parse_status).toBe("partial");
});
it("preserves the raw bridge observation for a guarded object value", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const api=flag ? {copy:()=>e.ipcRenderer.send("copy")} : {};
e.contextBridge.exposeInMainWorld("desktop", api);
`);
expect(result.electron.context_bridge_apis).toContainEqual(
expect.objectContaining({
api_key: "desktop",
api_status: "dynamic",
}),
);
expect(result.parse_status).toBe("partial");
});
it("preserves the source expression for a guarded bridge world key", () => {
const result = analyzeJavaScriptStaticSource(`
const e=require("electron");
const world=flag ? "desktop" : "shell";
e.contextBridge.exposeInMainWorld(world, {copy:()=>e.ipcRenderer.send("copy")});
`);
expect(result.electron.context_bridge_apis).toContainEqual(
expect.objectContaining({ api_key: null, api_key_expression: "world" }),
);
expect(result.parse_status).toBe("partial");
});
it.each(["const", "var"])(
"does not use a %s callback initializer before exposure",
(declaration) => {
const result = analyzeJavaScriptStaticSource(
bundle(`
const electron=load(2);
electron.contextBridge.exposeInMainWorld("desktop", {copy: callback});
${declaration} callback=()=>electron.ipcRenderer.send("late");
`),
);
expect(result.electron.call_paths ?? []).toEqual([]);
expect(result.parse_status).toBe("partial");
},
);
it("does not use an original Electron module factory after its slot is replaced", () => {
const result = analyzeJavaScriptStaticSource(
bundle(`
modules[2] = function(module) {
module.exports = {contextBridge:{exposeInMainWorld(){}},ipcRenderer:{send(){}}};
};
const electron=load(2);
electron.contextBridge.exposeInMainWorld("desktop", {copy:()=>electron.ipcRenderer.send("fake")});
`),
);
expect(result.electron.call_paths ?? []).toEqual([]);
});
it.each(["({})", "load(2)"])(
"does not emit the impossible logical-RHS terminal after %s",
(left) => {
const result = analyzeJavaScriptStaticSource(
bundle(`
electron.ipcMain.on("guard", event => { ${left} || event.sender.copy(); });
`),
);
expect(result.electron.call_paths ?? []).toEqual([]);
},
);
it.each([
["flag -= 1", true],
["flag = 0", false],
["flag -= step", true],
["flag += 0", true],
["flag &= 2", true],
["flag &&= 0", true],
["flag ||= 0", true],
["flag ??= 0", true],
] as const)(
"preserves contextual IPC candidates for assignment test %s",
(test, uncertain) => {
const result = analyzeJavaScriptStaticSource(`
const {contextBridge, ipcRenderer} = require("electron");
function action(step) {
let flag = 1;
if ((${test})) ipcRenderer.send("channel-then");
else ipcRenderer.send("channel-else");
}
contextBridge.exposeInMainWorld("desktop", {action});
`);
expect(result.electron.ipc.map(({ channel }) => channel).sort()).toEqual([
"channel-else",
"channel-then",
]);
const paths = result.electron.call_paths
?.filter(({ entry }) => entry === "desktop.action")
.map(({ channel, conditions, resolution }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
resolution,
}))
.sort((a, b) => String(a.channel).localeCompare(String(b.channel)));
expect(paths).toEqual([
{
channel: "channel-else",
conditions: uncertain ? [`!(${test})`] : [],
resolution: "candidate",
},
...(uncertain
? [
{
channel: "channel-then",
conditions: [test],
resolution: "candidate",
},
]
: []),
]);
},
);
it.each([
["exports.flag = 1; exports.flag -= 1", "helper.flag", true],
["exports.flag = 0", "helper.flag", false],
[
"module.exports = {flag: 1}; module.exports += {flag: 0}",
"helper.flag",
true,
],
["module.exports = {flag: 0}", "helper.flag", false],
] as const)(
"preserves assignment semantics through module writes: %s",
(writes, test, uncertain) => {
const source = bundle(`
electron.contextBridge.exposeInMainWorld("desktop", {action() {
if (${test}) electron.ipcRenderer.send("channel-then");
else electron.ipcRenderer.send("channel-else");
}});
`).replace("exports.sender =", `${writes}; exports.sender =`);
const result = analyzeJavaScriptStaticSource(source);
const paths = result.electron.call_paths
?.filter(({ entry }) => entry === "desktop.action")
.map(({ channel, conditions }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
}))
.sort((a, b) => String(a.channel).localeCompare(String(b.channel)));
expect(paths).toEqual([
{ channel: "channel-else", conditions: uncertain ? [`!(${test})`] : [] },
...(uncertain ? [{ channel: "channel-then", conditions: [test] }] : []),
]);
},
);
it.each([
["cb ||=", ["!(cb)"]],
["cb &&=", ["cb"]],
["cb ??=", ["(cb) === null || (cb) === undefined"]],
["({cb: false}).cb ||=", []],
["({cb: true}).cb &&=", []],
["({cb: null}).cb ??=", []],
] as const)(
"retains the reachable callable RHS of %s",
(assignment, conditions) => {
const result = analyzeJavaScriptStaticSource(`
const {contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld("desktop", {action(cb) {
(${assignment} () => ipcRenderer.send("rhs"))();
}});
`);
expect(
result.electron.call_paths?.map(
({ channel, conditions, resolution }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
resolution,
}),
),
).toEqual([{ channel: "rhs", conditions, resolution: "candidate" }]);
},
);
it.each([
"({cb: true}).cb ||=",
"({cb: false}).cb &&=",
"({cb: false}).cb ??=",
])("does not invoke the skipped callable RHS of %s", (assignment) => {
const result = analyzeJavaScriptStaticSource(`
const {contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld("desktop", {action() {
(${assignment} () => ipcRenderer.send("skipped"))();
}});
`);
expect(result.electron.call_paths ?? []).toEqual([]);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({ channel: "skipped" }),
);
});
it.each(["||=", "??="])(
"retains the selected left callable of %s",
(operator) => {
const result = analyzeJavaScriptStaticSource(`
const {contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld("desktop", {action() {
(({cb: () => ipcRenderer.send("left")}).cb ${operator} () => ipcRenderer.send("right"))();
}});
`);
expect(
result.electron.call_paths?.map(({ channel, conditions }) => ({
channel,
conditions,
})),
).toEqual([{ channel: "left", conditions: [] }]);
},
);
it.each([
["||=", "!(exports.action)"],
["&&=", "exports.action"],
["??=", "(exports.action) === null || (exports.action) === undefined"],
])(
"retains a guarded namespace callable assigned with %s",
(operator, condition) => {
const source = bundle(`
electron.contextBridge.exposeInMainWorld("desktop", {action: helper.action});
`).replace(
"exports.sender =",
`exports.action ${operator} () => electron.ipcRenderer.send("rhs"); exports.sender =`,
);
const result = analyzeJavaScriptStaticSource(source);
expect(
result.electron.call_paths?.map(({ channel, conditions }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
})),
).toEqual([{ channel: "rhs", conditions: [condition] }]);
},
);
// A synthetic module record establishes identity, not the value of its mutable
// exports slot. These are guarded candidates, including when runtime evaluation
// might skip the RHS; they must not claim execution or select a stale value.
const expectGuardedRoot = (
source: string,
operator: string,
target = "module.exports",
channel = "rhs",
) => {
const result = analyzeJavaScriptStaticSource(source);
const condition =
operator === "&&="
? target
: operator === "||="
? `!(${target})`
: `(${target}) === null || (${target}) === undefined`;
expect(
result.electron.call_paths
?.filter(({ entry }) => entry === "desktop.action")
.map(({ channel, conditions, resolution }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
resolution,
})) ?? [],
).toEqual([{ channel, conditions: [condition], resolution: "candidate" }]);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({ channel }),
);
};
const rootFixture = (body: string, expose = true) =>
bundle(
expose
? `electron.contextBridge.exposeInMainWorld("desktop", helper);`
: "",
).replace("exports.sender =", `${body}; exports.sender =`);
it.each([
["null", "||="],
["0", "||="],
["false", "??="],
["null", "??="],
["void 0", "??="],
["true", "&&="],
["null", "&&="],
["({action: () => electron.ipcRenderer.send('prior')})", "||="],
["({action: () => electron.ipcRenderer.send('prior')})", "??="],
["({action: () => electron.ipcRenderer.send('prior')})", "&&="],
])(
"keeps mutable root result uncertain after %s with %s",
(prior, operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = ${prior}; module.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")}`,
),
operator,
);
},
);
it.each(["||=", "??=", "&&="])(
"keeps a guarded candidate without a prior root write for %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`module.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")}`,
),
operator,
);
},
);
it.each(["||=", "??=", "&&="])(
"keeps unknown prior root replacement guarded for %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = unknownValue; module.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")}`,
),
operator,
);
},
);
it.each(["||=", "??="])(
"does not claim a known prior root callable for %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = () => electron.ipcRenderer.send("prior");
const selected = (module.exports ${operator} () => electron.ipcRenderer.send("rhs"));
electron.contextBridge.exposeInMainWorld("desktop", {action: selected});`,
false,
),
operator,
);
},
);
it.each(["||=", "??="])(
"does not borrow a later root write when evaluating %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = null;
const selected = (module.exports ${operator} {action: () => electron.ipcRenderer.send("selected")});
module.exports = {action: () => electron.ipcRenderer.send("later")};
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
operator,
"module.exports",
"selected",
);
},
);
it.each(["if (flag) module.exports = null;", "module[key] = null;"])(
"keeps ambiguous earlier root writes uncertain: %s",
(prior) => {
expectGuardedRoot(
rootFixture(
`${prior}
const selected = (module.exports ||= () => electron.ipcRenderer.send("rhs"));
electron.contextBridge.exposeInMainWorld("desktop", {action: selected});`,
false,
),
"||=",
);
},
);
it.each([
["null", "const alias = module; alias.exports = {};", "&&="],
["{}", "const alias = module; alias.exports = null;", "||="],
["{}", "const alias = module; alias.exports = null;", "??="],
["null", "Object.assign(module, {exports: {}});", "&&="],
["null", "replaceExports(module);", "&&="],
["null", "const box = {holder: module}; box.holder.exports = {};", "&&="],
[
"null",
"const {holder: alias} = {holder: module}; alias.exports = {};",
"&&=",
],
])(
"keeps guarded root candidates after escape: %s; %s; %s",
(prior, escape, operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = ${prior}; ${escape} module.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")}`,
),
operator,
);
},
);
it("keeps root candidates guarded in the presence of a shadowed module parameter", () => {
expectGuardedRoot(
rootFixture(`function unrelated(module) {replaceExports(module);}
module.exports = null; module.exports ||= {action: () => electron.ipcRenderer.send("rhs")}`),
"||=",
);
});
it.each([
"module.exports()",
"module.exports?.()",
"module.exports``",
"(module.exports)()",
"(module.exports as Function)()",
"(module.exports!)()",
"const fn = module.exports; fn()",
"(0, module.exports)()",
])(
"keeps mutable root candidates guarded across invocation: %s",
(invocation) => {
expectGuardedRoot(
rootFixture(`module.exports = function() {this.exports = null;};
${invocation}; module.exports ||= {action: () => electron.ipcRenderer.send("rhs")}`),
"||=",
);
},
);
it.each([
"module.exports = null; exports = {};",
"module.exports = null; module.exports = module.exports;",
])(
"does not infer a root value from detached or self-referential writes: %s",
(prior) => {
expectGuardedRoot(
rootFixture(
`${prior} module.exports ||= {action: () => electron.ipcRenderer.send("rhs")}`,
),
"||=",
);
},
);
it.each(["||=", "??=", "&&="])(
"keeps aliased logical assignment targets guarded for %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`const alias = module;
const selected = (alias.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")});
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
operator,
"alias.exports",
);
},
);
it.each(["||=", "??=", "&&="])(
"keeps conditional synthetic receivers guarded for %s",
(operator) => {
for (const receiver of [
"(flag ? module : module)",
"(flag ? {exports: true} : module)",
"(flag ? module : {exports: true})",
]) {
expectGuardedRoot(
rootFixture(
`module.exports = null;
const selected = (${receiver}.exports ${operator} {action: () => electron.ipcRenderer.send("rhs")});
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
operator,
`${receiver}.exports`,
);
}
},
);
it.each(["||=", "??=", "&&="])(
"preserves ordinary values beside synthetic receivers for %s",
(operator) => {
for (const moduleFirst of [false, true]) {
const ordinary =
operator === "&&="
? "{exports: false}"
: "{exports: () => electron.ipcRenderer.send('left')}";
const receiver = moduleFirst
? `(flag ? module : ${ordinary})`
: `(flag ? ${ordinary} : module)`;
const target = `${receiver}.exports`;
const result = analyzeJavaScriptStaticSource(
rootFixture(
`const selected = (${target} ${operator} () => electron.ipcRenderer.send("right"));
electron.contextBridge.exposeInMainWorld("desktop", {action: selected});`,
false,
),
);
const paths = result.electron.call_paths
?.map(({ channel, conditions, resolution }) => ({
channel,
conditions: conditions.map(({ expression }) => expression),
resolution,
}))
.sort((a, b) => String(a.channel).localeCompare(String(b.channel)));
expect(paths).toEqual([
...(operator === "&&="
? []
: [
{
channel: "left",
conditions: [
moduleFirst ? "!(flag)" : "flag",
operator === "||="
? target
: `(${target}) !== null && (${target}) !== undefined`,
],
resolution: "candidate",
},
]),
{
channel: "right",
conditions: [
operator === "&&="
? target
: operator === "||="
? `!(${target})`
: `(${target}) === null || (${target}) === undefined`,
],
resolution: "candidate",
},
]);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({ channel: "right" }),
);
if (operator !== "&&=")
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({ channel: "left" }),
);
}
},
);
it.each(["(module)", "(module as any)", "(module!)"])(
"recognizes wrapped synthetic receivers: %s",
(receiver) => {
expectGuardedRoot(
rootFixture(
`const selected = (${receiver}.exports ||= {action: () => electron.ipcRenderer.send("rhs")});
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
"||=",
`${receiver}.exports`,
);
},
);
it.each(["||=", "??=", "&&="])(
"preserves uncertainty through module exports snapshots for %s",
(operator) => {
for (const [setup, target] of [
["", "({exports: module.exports}).exports"],
["const snapshot = module.exports;", "({exports: snapshot}).exports"],
["", "[module.exports][0]"],
]) {
expectGuardedRoot(
rootFixture(
`module.exports = null; ${setup}
const selected = (${target} ${operator} {action: () => electron.ipcRenderer.send("rhs")});
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
operator,
target,
);
}
},
);
it.each(["saved", "!saved"])(
"keeps primitive root snapshot branch uncertainty for %s",
(test) => {
const result = analyzeJavaScriptStaticSource(
rootFixture(
`module.exports = null; const saved = module.exports;
electron.contextBridge.exposeInMainWorld("desktop", {action() {
if (${test}) electron.ipcRenderer.send("then");
else electron.ipcRenderer.send("else");
}});`,
false,
),
);
expect(
result.electron.call_paths
?.map(({ channel, conditions }) => ({
channel,
conditions: conditions.map((c) => c.expression),
}))
.sort((a, b) => String(a.channel).localeCompare(String(b.channel))),
).toEqual([
{ channel: "else", conditions: [`!(${test})`] },
{ channel: "then", conditions: [test] },
]);
},
);
it.each(["||", "??", "&&"])(
"keeps primitive root logical snapshots uncertain for %s",
(operator) => {
expectGuardedRoot(
rootFixture(
`module.exports = null; const saved = module.exports;
const selected = saved ${operator} {action: () => electron.ipcRenderer.send("rhs")};
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
`${operator}=`,
"saved",
);
},
);
it.each(["module.exports", "({saved: module.exports}).saved"])(
"preserves namespace members through plain logical selection of %s",
(target) => {
for (const operator of ["||", "??"]) {
const result = analyzeJavaScriptStaticSource(
rootFixture(
`exports.action = () => electron.ipcRenderer.send("left");
const selected = ${target} ${operator} {};
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
);
expect(
result.electron.call_paths?.map(({ channel, conditions }) => ({
channel,
conditions: conditions.map((c) => c.expression),
})),
).toEqual([
{
channel: "left",
conditions: [
operator === "||"
? target
: `(${target}) !== null && (${target}) !== undefined`,
],
},
]);
}
},
);
it("preserves later member writes through a saved module exports reference", () => {
const result = analyzeJavaScriptStaticSource(
rootFixture(
`const saved = module.exports;
exports.action = () => electron.ipcRenderer.send("later-member");
electron.contextBridge.exposeInMainWorld("desktop", saved);`,
false,
),
);
expect(
result.electron.call_paths?.map(({ channel, conditions }) => ({
channel,
conditions,
})),
).toEqual([{ channel: "later-member", conditions: [] }]);
});
it.each(["||=", "??=", "&&="])(
"recognizes whole-target wrappers for %s",
(operator) => {
for (const target of [
"(module.exports as any)",
"(module.exports!)",
"(module.exports satisfies any)",
"(module.exports)",
]) {
expectGuardedRoot(
rootFixture(
`module.exports = null;
const selected = (${target} ${operator} {action: () => electron.ipcRenderer.send("rhs")});
electron.contextBridge.exposeInMainWorld("desktop", selected);`,
false,
),
operator,
target.slice(1, -1),
);
const ordinary = target.replace(
"module.exports",
`({exports: ${operator === "&&=" ? "false" : "true"}}).exports`,
);
const result =
analyzeJavaScriptStaticSource(`const e = require("electron");
e.contextBridge.exposeInMainWorld("desktop", {action() {
(${ordinary} ${operator} () => e.ipcRenderer.send("skipped"))();
}});`);
expect(result.electron.call_paths ?? []).toEqual([]);
expect(result.electron.ipc).toContainEqual(
expect.objectContaining({ channel: "skipped" }),
);
}
},
);
it.each(["||=", "??="])(
"keeps an ordinary object's exports slot precise for %s",
(operator) => {
const result = analyzeJavaScriptStaticSource(`const e=require("electron");
function action(){(({exports: true}).exports ${operator} () => e.ipcRenderer.send("skipped"))();}
e.contextBridge.exposeInMainWorld("desktop",{action});`);
expect(result.electron.call_paths ?? []).toEqual([]);
},
);
it("does not follow a statically false callback branch", () => {
const result = analyzeJavaScriptStaticSource(
bundle(
`helper.events.addListener("example:copy",event=>{if(false)event.sender.copy()});`,
),
);
expect(result.electron.call_paths ?? []).toEqual([]);
});
it("retains early rejection guards and excludes calls after definite returns", () => {
const guarded = analyzeJavaScriptStaticSource(
`const e=require("electron");e.ipcMain.on("guard",event=>{if(!allowed(event))return;event.sender.copy()});`,
);
expect(guarded.electron.call_paths).toContainEqual(
expect.objectContaining({
conditions: [
expect.objectContaining({ expression: "!(!allowed(event))" }),
],
}),
);
for (const body of [
"return;event.sender.copy()",
"if(true)return;event.sender.copy()",
"if(false)event.sender.copy()",
])
expect(
analyzeJavaScriptStaticSource(
`const e=require("electron");e.ipcMain.on("dead",event=>{${body}});`,
).electron.call_paths ?? [],
).toEqual([]);
expect(
analyzeJavaScriptStaticSource(
`const e=require("electron");if(false)e.ipcMain.on("dead",event=>event.sender.copy());`,
).electron.call_paths ?? [],
).toEqual([]);
});
it("rejects dynamic channels, computed overwrites, unknown spreads and reassigned callback parameters", () => {
for (const entry of [
`helper.events.addListener(runtimeChannel,event=>event.sender.copy());`,
`electron[runtimeKey]={};electron.ipcMain.on("bad",event=>event.sender.copy());`,
`const api={copy:helper.sender("bad"),...unknownApi};electron.contextBridge.exposeInMainWorld("desktop",api);`,
`helper.events.addListener("bad",event=>{event=other;event.sender.copy()});`,
])
expect(
analyzeJavaScriptStaticSource(bundle(entry)).electron.call_paths ?? [],
).toEqual([]);
});
it("resolves renamed default and namespace import helpers from their syntax", () => {
const result = analyzeJavaScriptStaticSource(
bundle(`
const defaultImport=x=>x&&x.__esModule?x:{default:x};
function namespaceImport(x){if(x&&x.__esModule)return x;return unknownWrap(x)}
const E=defaultImport(load(2)),H=namespaceImport(load(5));
E.default.contextBridge.exposeInMainWorld("renamed",{copy:(0,H.sender)("test:renamed")});
`),
);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry: "renamed.copy",
channel: "test:renamed",
terminal: "electron.ipcRenderer.send",
}),
);
expect(
result.electron.call_paths
?.flatMap((p) => p.steps)
.every((step) => step.label.length > 0),
).toBe(true);
});
it("resolves an empty bootstrap module key through an exported Electron bridge", () => {
const result = analyzeJavaScriptStaticSource(`(() => {
const modules = {
""(module) { module.exports = require("electron"); },
9(module, exports, load) {
const electron = load("");
electron.contextBridge.exposeInMainWorld("desktop", {
copy: () => electron.ipcRenderer.send("test:empty-module"),
});
},
};
function load(id) {
const module = { exports: {} };
modules[id].call(module.exports, module, module.exports, load);
return module.exports;
}
load(9);
})();`);
expect(result.bundler_registrations).toMatchObject([
{
entry_module_keys: ["9"],
modules: [{ module_key: "" }, { module_key: "9" }],
},
]);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry: "desktop.copy",
channel: "test:empty-module",
terminal: "electron.ipcRenderer.send",
}),
);
});
it("does not trust helper names or follow returns after a definite throw", () => {
for (const helper of [
`function __toESM() {return {ipcRenderer:{send(){}}}}`,
`function __toESM(value) {throw Error("inert");return value;}`,
`function __toESM(value) {if(true){throw Error("inert")}return value;}`,
]) {
const result = analyzeJavaScriptStaticSource(`
const electron = require("electron");
${helper}
const wrapped = __toESM(electron);
electron.contextBridge.exposeInMainWorld("desktop",{
copy:()=>wrapped.ipcRenderer.send("test:copy")
});
`);
expect(result.electron.call_paths ?? []).toEqual([]);
}
});
describe("wrapped Electron API identity", () => {
it("does not restore rejected Electron identities through wrapper substitution", () => {
for (const source of [
`const e=require("electron");e.ipcMain={on(){}};e.ipcMain.on("bad",event=>event.sender.copy());`,
`function require(){return {ipcMain:{on(){}}}};const e=require("electron");e.ipcMain.on("bad",event=>event.sender.copy());`,
`const e=require("electron");const ipc=e.ipcRenderer,alias=ipc;delete alias.send;function send(channel){ipc.send(channel)}send("bad");`,
`require("electron/renderer").ipcRenderer={};const e=require("electron");function send(channel){e.ipcRenderer.send(channel)}send("bad");`,
`const e=require("electron");[e.ipcRenderer]=replacements;function send(channel){e.ipcRenderer.send(channel)}send("bad");`,
`const e=require("electron");for(e.contextBridge of replacements){}function expose(api){e.contextBridge.exposeInMainWorld("api",api)}expose({copy:()=>navigator.clipboard.writeText("bad")});`,
`const e=require("electron");const ipc=e.ipcRenderer;Reflect.deleteProperty(ipc,"send");function send(channel){ipc.send(channel)}send("bad");`,
]) {
const { electron } = analyzeJavaScriptStaticSource(source);
expect(electron.call_paths ?? []).toEqual([]);
expect(electron.ipc).toEqual([]);
expect(electron.context_bridge_apis).toEqual([]);
}
});
it("keeps unaffected Electron methods available after a sibling method write", () => {
const { electron } = analyzeJavaScriptStaticSource(`
const e=require("electron");
delete e.ipcRenderer.send;
function invoke(channel){e.ipcRenderer.invoke(channel)}
invoke("good");
`);
expect(electron.ipc).toContainEqual(
expect.objectContaining({ channel: "good", operation: "invoke" }),
);
});
});
it.each([false, true])(
"preserves a guarded bridge entry sharing a registration with an unconditional member (reverse=%s)",
(reverse) => {
const members = ["always:register", "conditional:flag?register:()=>{}"];
if (reverse) members.reverse();
const result = analyzeJavaScriptStaticSource(`const e=require("electron");
function register(){e.ipcMain.on("bridge:register",()=>{});}
e.contextBridge.exposeInMainWorld("api",{${members.join(",")}});`);
expect(result.electron.call_paths).toContainEqual(
expect.objectContaining({
entry_kind: "bridge-member",
entry: "api.conditional",
terminal: "electron.ipcMain.on",
conditions: expect.arrayContaining([
expect.objectContaining({ expression: "flag" }),
]),
}),
);
},
);