Files
reverse-engineering-anything/src/domain/javascript/electronStaticReceiverAdmission.test.ts
T
morlutoandShuaixinHuang b46f4fcebd fix(javascript): preserve loader authority for exact suffixes (#9477)
* fix(javascript): require exact loader suffix case

* fix(javascript): share exact loader mode across import resolution

* test(process): align symlink fixture with filesystem checkpoints

* test(javascript): normalize the module resolution oracle path separators

* test(frida): assert the CLI spawn Evidence contract

* test(mcp): use an unsupported analysis-view parent

* test(browser): separate source-location line bounds from parity

* fix(javascript): preserve uncertain loader provenance

* test(javascript): cover uncertain require resolution

* fix(javascript): retain conditional Electron evidence

* fix(javascript): preserve timed semantic provenance

* fix(javascript): retain conditional builtin effects

* test(javascript): distinguish bundled host authority

* fix(javascript): preserve nested loader invocation identity

* test(javascript): qualify Electron fixture authority

* fix(javascript): preserve loader invocation authority

* fix(javascript): respect source order for resolver escapes

* fix(javascript): check nested loader callable mutations

* fix(javascript): retain conditional constructor requests

* fix(reference): retain constructor loader relationships

* fix(reference): follow nested loader callable paths

* refactor(javascript): share callable mutation interpretation

* fix(javascript): require exact package loader suffixes

* refactor(javascript): unify conditional relation evidence

* refactor(javascript): unify conditional node evidence

* fix(javascript): retain unknown authority for mixed case sources

* fix(javascript): preserve loader uncertainty for dotfiles

* fix(reference): classify javascript suffix dotfiles

* test(reference): cover suffix dotfiles through cli

* fix(javascript): retain equivalent conditional built-in origins

* fix(javascript): preserve wrapped resolver operation

* fix(javascript): retain nested conditional built-in receivers

* test(javascript): declare CommonJS in native addon fixtures

* fix(javascript): retain wrapped resolver candidates

* style: format merged native command schema

* fix(javascript): preserve uncertain loader authority

* test(javascript): require candidate preload evidence

* test(javascript): cover reference loader member writes

---------

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-11-09 13:21:24 +08:00

84 lines
3.0 KiB
TypeScript

import { expect, it } from "vitest";
import { inspectElectronIpcNode } from "./electronStaticAnalysisIpc.js";
import { resolveElectronBrowserWindowReceiver } from "./electronStaticRenderer.js";
import { createJavaScriptRpcScan } from "./javascriptRpcEndpoints.js";
import {
indexJavaScriptBindingsWithReceiversSteps,
indexJavaScriptElectronIdentitySteps,
} from "./javascriptSemanticAnalysis.js";
import { createSemanticExpressionShapeResolver } from "./javascriptSemanticExpressionShapes.js";
import {
completeSemanticSteps as complete,
traverseJavaScriptAst,
} from "./javascriptSemanticTraversal.js";
import { parseJavaScriptSource } from "./javascriptSourceParser.js";
import {
createJavaScriptAnalysisAccumulator,
type JavaScriptFindingContext,
} from "./javascriptStaticAnalysisState.js";
const inspect = (source: string, path?: string) => {
const file = parseJavaScriptSource(source, path);
if (file === null) throw new Error("Fixture must parse");
const index = complete(
indexJavaScriptBindingsWithReceiversSteps(file.program),
);
complete(index.indexAmbientWritesSteps());
const identity = complete(
indexJavaScriptElectronIdentitySteps(file.program, index.bindings),
);
let mutationIndexCalls = 0;
const original = index.indexMemberMutationFactsSteps.bind(index);
index.indexMemberMutationFactsSteps = function* () {
mutationIndexCalls += 1;
yield* original();
};
const context: JavaScriptFindingContext = {
source,
accumulator: createJavaScriptAnalysisAccumulator(source.length),
vitePreloadDependencyTable: null,
electronBindings: identity.bindings,
electronExportIntact: identity.intactExport,
rpcScan: createJavaScriptRpcScan(
source,
index.receivers.rpcClientRoots,
index,
),
expressionShapes: createSemanticExpressionShapeResolver(index.bindings),
browserWindowReceiver: (node, site, suffix) =>
resolveElectronBrowserWindowReceiver(node, site, suffix, context, index),
};
traverseJavaScriptAst(file, {
enter: (node) => inspectElectronIpcNode(node, context),
});
return { findings: context.accumulator.ipc, mutationIndexCalls };
};
it.each([
[
"ordinary EventEmitter",
`import {EventEmitter} from "node:events";
const emitter = new EventEmitter(); emitter.on("event", listener);`,
],
[
"unsupported BrowserWindow path",
`const {BrowserWindow} = require("electron");
const window = new BrowserWindow(); window.on("ready", listener);`,
],
])("does not request whole-file mutation facts for %s", (_, source) => {
const result = inspect(source);
expect(result.findings).toEqual([]);
expect(result.mutationIndexCalls).toBe(0);
});
it("still checks mutations for a source-proven scoped Electron receiver", () => {
const result = inspect(
`const {BrowserWindow} = require("electron");
const window = new BrowserWindow(); window.webContents.ipc.handle("channel", () => {});`,
"fixture.cts",
);
expect(result.findings).toHaveLength(1);
expect(result.mutationIndexCalls).toBeGreaterThan(0);
});