fix(javascript): recognize unchanged var and let Electron aliases

This commit is contained in:
morluto 2026-10-10 13:56:40 +08:00
parent e4a8e0b728
commit 0d2082d36b
2 changed files with 67 additions and 3 deletions

View File

@ -352,9 +352,17 @@ const bindingOrigin = (
? binding.directOrigins[0]
: undefined;
const [initializer] = binding.initializers;
if (binding.mutable || binding.initializers.length !== 1) return undefined;
const origin = semanticRequireOrigin(initializer?.node, state);
const projection = initializer?.projection ?? [];
// A var/let declaration permits writes without proving one occurred.
// Require one unconditional initializer and no actual binding writes.
if (
initializer === undefined ||
binding.initializers.length !== 1 ||
binding.definitions.some(({ kind }) => kind === "assignment") ||
state.conditionalInitializers.has(initializer.node)
)
return undefined;
const origin = semanticRequireOrigin(initializer.node, state);
const projection = initializer.projection;
return origin === undefined ||
!projection.every((key): key is string => typeof key === "string")
? undefined

View File

@ -116,6 +116,18 @@ describe("static Electron application analysis", () => {
expect(handledChannels.sort()).toEqual(["rea:read", "rea:write"]);
});
it("maps unchanged var and let require aliases while excluding reassigned bindings", async () => {
const root = await requireAliasFixtureDirectory();
const result = await reconstructJavaScriptArtifact({ input_path: root });
expect(result.electron_summary.ipc).toMatchObject({
main_handlers: 2,
paired_renderer_transmissions: 2,
unpaired_literal_renderer_transmissions: 0,
});
});
it("returns a tagged cancellation without executing application code", async () => {
const root = await fixtureDirectory();
const controller = new AbortController();
@ -326,6 +338,50 @@ const fixtureDirectory = async (): Promise<string> => {
return root;
};
const requireAliasFixtureDirectory = async (): Promise<string> => {
const root = await createTestTempDirectory("rea-electron-require-aliases-");
await Promise.all([
writeFile(
join(root, "package.json"),
JSON.stringify({
name: "rea-electron-require-aliases",
main: "main.cjs",
}),
),
writeFile(
join(root, "main.cjs"),
String.raw`
var { ipcMain: main } = require("electron");
let listener = require("electron/main").ipcMain;
main.handle("rea:read", async () => "value");
listener.on("rea:write", () => undefined);
let replaced = require("electron").ipcMain;
replaced = { handle() {} };
replaced.handle("rea:reassigned", () => undefined);
var assignedLater;
assignedLater.handle("rea:before-assignment", () => undefined);
assignedLater = require("electron").ipcMain;
if (globalThis.registerIpc) {
var conditional = require("electron").ipcMain;
}
conditional.handle("rea:conditional", () => undefined);
`,
),
writeFile(
join(root, "preload.cjs"),
String.raw`
const { ipcRenderer } = require("electron");
ipcRenderer.invoke("rea:read");
ipcRenderer.send("rea:write", "value");
`,
),
]);
return root;
};
const renamedBindingFixtureDirectory = async (): Promise<string> => {
const root = await createTestTempDirectory("rea-electron-renamed-");
await Promise.all([