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" }), ]), }), ); }, );