mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
251 lines
8.5 KiB
TypeScript
251 lines
8.5 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { analyzeJavaScriptStaticSource as analyzeUnknownHostSource } from "../../../tests/fixtures/javascriptStaticAnalysis.js";
|
|
import { analyzeCommonJsStaticFixture as analyzeJavaScriptStaticSource } from "../../../tests/fixtures/javascriptCommonJsAnalysis.js";
|
|
|
|
it("retains an unknown-host Electron request as a candidate without claiming IPC identity", () => {
|
|
const analysis = analyzeUnknownHostSource(
|
|
'const { BrowserWindow } = require("electron"); new BrowserWindow().webContents.ipc.handle("x", () => {});',
|
|
);
|
|
expect(analysis.electron.ipc).toEqual([
|
|
expect.objectContaining({
|
|
channel: "x",
|
|
operation: "handle",
|
|
loader_authority: "candidate",
|
|
}),
|
|
]);
|
|
expect(analysis.analysis_coverage?.applicability.electron).toBe("unknown");
|
|
expect(analysis.references).toContainEqual(
|
|
expect.objectContaining({
|
|
specifier: "electron",
|
|
loader_authority: "candidate",
|
|
}),
|
|
);
|
|
expect(
|
|
analysis.analysis_coverage?.semantics.unknown_findings,
|
|
).toBeGreaterThan(0);
|
|
});
|
|
|
|
it.each([
|
|
["webContents", "window.webContents.ipc"],
|
|
["main frame", "window.webContents.mainFrame.ipc"],
|
|
] as const)(
|
|
"retains a proven %s IPC handler beside the global control",
|
|
(_, receiver) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { BrowserWindow, ipcMain } = require("electron");
|
|
const window = new BrowserWindow();
|
|
ipcMain.handle("global-control", () => "global");
|
|
${receiver}.handle("scoped-channel", () => "scoped");
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.browser_windows).toHaveLength(1);
|
|
expect(analysis.electron.ipc).toContainEqual(
|
|
expect.objectContaining({
|
|
side: "main",
|
|
operation: "handle",
|
|
channel: "global-control",
|
|
handler_kind: "inline-function",
|
|
}),
|
|
);
|
|
expect(analysis.electron.ipc).toContainEqual(
|
|
expect.objectContaining({
|
|
side: "main",
|
|
operation: "handle",
|
|
channel: "scoped-channel",
|
|
handler_kind: "inline-function",
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
|
|
it("does not label lookalike or shadowed receivers as Electron IPC", () => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { BrowserWindow, ipcMain } = require("electron");
|
|
ipcMain.handle("global-control", () => "global");
|
|
const fake = { webContents: { ipc: { handle() {} } } };
|
|
fake.webContents.ipc.handle("fake", () => {});
|
|
function shadow(BrowserWindow) {
|
|
const window = new BrowserWindow();
|
|
window.webContents.ipc.handle("shadowed", () => {});
|
|
}
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.ipc.map(({ channel }) => channel)).toEqual([
|
|
"global-control",
|
|
]);
|
|
});
|
|
|
|
it.each([
|
|
[
|
|
'const contents = window.webContents; contents.ipc.handle("same", () => {});',
|
|
"web-contents",
|
|
],
|
|
[
|
|
'const frame = window.webContents.mainFrame; frame.ipc.handle("same", () => {});',
|
|
"main-frame",
|
|
],
|
|
[
|
|
'const contents = window.webContents; const frame = contents.mainFrame; const ipc = frame.ipc; ipc.handle("same", () => {});',
|
|
"main-frame",
|
|
],
|
|
])("retains a proven scoped alias: %s", (call, kind) => {
|
|
const findings =
|
|
analyzeJavaScriptStaticSource(`const { BrowserWindow } = require("electron");
|
|
const window = new BrowserWindow(); ${call}`).electron.ipc;
|
|
expect(findings).toHaveLength(1);
|
|
expect(findings[0]?.receiver).toEqual({
|
|
kind,
|
|
owner_location: {
|
|
start: { line: 2, column: 19 },
|
|
end: { line: 2, column: 38 },
|
|
},
|
|
});
|
|
});
|
|
|
|
it("keeps the shared owner's conservative omission after an aliased receiver call", () => {
|
|
const findings =
|
|
analyzeJavaScriptStaticSource(`const { BrowserWindow } = require("electron");
|
|
const window = new BrowserWindow();
|
|
const ipc = window.webContents.mainFrame.ipc;
|
|
ipc.handle("first", () => {});
|
|
window.webContents.mainFrame.ipc.on("after-escape", () => {});`).electron
|
|
.ipc;
|
|
expect(findings.map(({ channel }) => channel)).toEqual(["first"]);
|
|
});
|
|
|
|
it.each([
|
|
"window.webContents = replacement;",
|
|
"window.webContents.ipc = replacement;",
|
|
"window.webContents.ipc.handle = replacement;",
|
|
"const alias = window; alias.webContents.ipc.handle = replacement;",
|
|
"const contents = window.webContents; contents.ipc = replacement;",
|
|
"BrowserWindow.prototype.webContents = replacement;",
|
|
])("omits a mutated receiver: %s", (mutation) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { BrowserWindow } = require("electron");
|
|
const window = new BrowserWindow();
|
|
${mutation}
|
|
window.webContents.ipc.handle("changed", () => {});
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.ipc).toEqual([]);
|
|
});
|
|
|
|
it.each(["webContents.ipc", "webContents.mainFrame.ipc"])(
|
|
"does not split one dotted member key into receiver segments: %s",
|
|
(key) => {
|
|
expect(
|
|
analyzeJavaScriptStaticSource(`const {BrowserWindow} = require("electron");
|
|
const window = new BrowserWindow(); window[${JSON.stringify(key)}].handle("fake", () => {});`)
|
|
.electron.ipc,
|
|
).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
["BrowserView", "webContents.ipc", "web-contents"],
|
|
["WebContentsView", "webContents.mainFrame.ipc", "main-frame"],
|
|
] as const)(
|
|
"retains a proven %s %s handler through the view's own contents",
|
|
(constructor, surface, kind) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { ${constructor} } = require("electron");
|
|
const owner = new ${constructor}({});
|
|
owner.${surface}.handle("view-channel", () => {});
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.browser_windows).toMatchObject([
|
|
{ window_constructor: constructor, options_status: "object-literal" },
|
|
]);
|
|
expect(analysis.electron.ipc).toMatchObject([
|
|
{
|
|
side: "main",
|
|
operation: "handle",
|
|
channel: "view-channel",
|
|
receiver: { kind },
|
|
},
|
|
]);
|
|
},
|
|
);
|
|
|
|
it.each(["webContents.ipc", "webContents.mainFrame.ipc", "loadURL"])(
|
|
"omits a BaseWindow %s receiver that the runtime does not own",
|
|
(surface) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { BaseWindow } = require("electron");
|
|
const base = new BaseWindow({ width: 800 });
|
|
base.${surface}.handle("inert", () => {});
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.ipc).toEqual([]);
|
|
expect(analysis.electron.browser_windows).toMatchObject([
|
|
{
|
|
window_constructor: "BaseWindow",
|
|
options_status: "object-literal",
|
|
web_preferences_status: "missing",
|
|
preload_path: null,
|
|
},
|
|
]);
|
|
},
|
|
);
|
|
|
|
it("keeps a lookalike construction unproven for a view constructor", () => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { WebContentsView } = require("electron");
|
|
function shadow(WebContentsView) {
|
|
const view = new WebContentsView({ webPreferences: { sandbox: true } });
|
|
view.webContents.ipc.handle("shadowed", () => {});
|
|
}
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.browser_windows).toEqual([]);
|
|
expect(analysis.electron.ipc).toEqual([]);
|
|
});
|
|
|
|
it.each(["BrowserView", "WebContentsView"])(
|
|
"reads literal webPreferences on a %s construction",
|
|
(constructor) => {
|
|
expect(
|
|
analyzeJavaScriptStaticSource(`
|
|
const { ${constructor} } = require("electron");
|
|
const owner = new ${constructor}({ webPreferences: { preload: "./p.js", sandbox: true } });
|
|
`).electron.browser_windows,
|
|
).toMatchObject([
|
|
{
|
|
window_constructor: constructor,
|
|
web_preferences_status: "object-literal",
|
|
web_preferences: [
|
|
{ name: "preload", value: { status: "literal", value: "./p.js" } },
|
|
{ name: "sandbox", value: { status: "literal", value: true } },
|
|
],
|
|
preload_path: "./p.js",
|
|
preload_resolution_context: "module-specifier",
|
|
},
|
|
]);
|
|
},
|
|
);
|
|
|
|
it.each(["BaseWindow"])(
|
|
"does not report a webPreferences surface a %s never accepts",
|
|
(constructor) => {
|
|
const analysis = analyzeJavaScriptStaticSource(`
|
|
const { ${constructor} } = require("electron");
|
|
const owner = new ${constructor}({ webPreferences: { preload: "./p.js", sandbox: true } });
|
|
`);
|
|
expect(analysis.parse_error_count).toBe(0);
|
|
expect(analysis.electron.browser_windows).toMatchObject([
|
|
{
|
|
window_constructor: constructor,
|
|
options_status: "object-literal",
|
|
web_preferences_status: "missing",
|
|
web_preferences: [],
|
|
preload_path: null,
|
|
},
|
|
]);
|
|
expect(
|
|
analysis.role_paths.filter(({ role }) => role === "preload"),
|
|
).toEqual([]);
|
|
},
|
|
);
|