Files
rea/tests/boundary/filesystem/javascript-applications/javascriptApplicationEmptyLiterals.test.ts
T
7ad75bdc80 fix: restore JavaScript analysis and current regression checks (#3080)
Restore source-position-aware ambient aliases, scalar env/argv reads, candidate
endpoint worker data, Chrome DOMSnapshot empty strings, and accumulated Ghidra
LaunchSupport diagnostics. Align regression fixtures with declared current
contracts, while preserving identity, provenance, cancellation, and cleanup
checks. Assert workflow caveats independently at both established locations.

Validation: original five source failures reproduced on main; 307 focused tests
passed with one platform skip, 22 fixture follow-up tests and 22 workflow/MCP
tests passed; full static and docs checks passed. Real public http-server CLI
analyses preserve identical artifact identity, statistics, and 581-token selected
output. Real Chromium confirms restored DOM form destinations; isolated real
service-worker verification passes on main and the change. The complete local
browser run retained an unclassified later marker timeout; current-head CI
provides complete real-browser and recovery lane results. Native IDA/Ghidra
execution and complete local suite execution are not claimed.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:17:54 +08:00

437 lines
15 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { Ajv2020 } from "ajv/dist/2020.js";
import { expect, it } from "vitest";
import { z } from "zod";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
import { javascriptApplicationAnalysisResultSchema } from "../../../../src/domain/javascript/javascriptApplicationAnalysis.js";
import { matchJavaScriptApplicationVersions } from "../../../../src/domain/javascript/javascriptApplicationVersionKeys.js";
import { findApplicationFeatureSeeds } from "../../../../src/domain/javascript/javascriptFeatureSeed.js";
import {
applicationFeatureSeedSchema,
traceApplicationFeatureInputSchema,
} from "../../../../src/domain/javascript/javascriptFeatureTraceSchemas.js";
import { analyzeJavaScriptApplication } from "../../../support/javascriptApplicationScope.js";
const analyzeSource = async (
source: string,
extraFiles: Readonly<Record<string, string>> = {},
) => {
const inputPath = await createTestTempDirectory("rea-js-empty-literal-");
await writeFile(join(inputPath, "app.js"), `${source}\n`);
for (const [path, text] of Object.entries(extraFiles))
await writeFile(join(inputPath, path), text);
const result = await analyzeJavaScriptApplication({
input_path: inputPath,
format: "directory",
});
if (!result.ok)
throw new Error(`Expected analysis success: ${result.error.message}`);
return javascriptApplicationAnalysisResultSchema.parse(
result.value.normalized_result,
);
};
it("retains empty literal evidence across the application boundary surfaces", async () => {
const sources = [
"fetch('');",
"new WebSocket('');",
"navigator.serviceWorker.register('');",
"new Worker('');",
"const x = require('');",
"import y from ''; y();",
"import('');",
"localStorage.getItem('');",
"localStorage.setItem('', 1);",
"indexedDB.open('');",
"win.loadFile('');",
"win.loadURL('');",
"app.get('', handler);",
"process.on('', () => 1);",
"const { EventEmitter } = require('events'); new EventEmitter().on('', () => 1);",
"const { spawn } = require('child_process'); spawn('/bin/x').on('', () => 1);",
"const { ipcMain } = require('electron'); ipcMain.handle('', () => 1);",
"const { ipcRenderer } = require('electron'); ipcRenderer.invoke('');",
"const { contextBridge } = require('electron'); contextBridge.exposeInMainWorld('', {});",
];
const output = await analyzeSource(sources.join("\n"));
expect(output.graph.nodes).toEqual(
expect.arrayContaining([
expect.objectContaining({ kind: "endpoint" }),
expect.objectContaining({ kind: "worker" }),
expect.objectContaining({ kind: "ipc-channel" }),
expect.objectContaining({ kind: "context-bridge-api" }),
]),
);
expect(output.semantic_graph?.nodes).toEqual(
expect.arrayContaining([
expect.objectContaining({
kind: "request",
properties: expect.objectContaining({ endpoint: "" }),
}),
expect.objectContaining({
kind: "event",
properties: expect.objectContaining({ event_name: "" }),
}),
]),
);
});
/** Endpoint identity keys tag literal values apart from candidates. */
const literalEndpointKey = (value: string): string =>
JSON.stringify(["literal", value]);
it("keeps bridge entry paths stable through worker validation and API seed matching", async () => {
const output = await analyzeSource(`
const { contextBridge, ipcRenderer } = require("electron");
const literal = () => ipcRenderer.send("literal");
const nested = () => ipcRenderer.send("nested");
contextBridge.exposeInMainWorld("desktop", {
"nested.write": literal,
nested: { write: nested },
});
`);
const semanticPathIds = new Set(
(output.semantic_graph?.nodes ?? [])
.map(({ properties }) => properties.path_id)
.filter((pathId): pathId is string => typeof pathId === "string"),
);
const applicationPathIds = new Set(
output.graph.nodes.flatMap(({ observations }) =>
observations
.map(({ properties }) => properties.path_id)
.filter((pathId): pathId is string => typeof pathId === "string"),
),
);
expect(semanticPathIds).toEqual(applicationPathIds);
const matches = findApplicationFeatureSeeds(
output.graph.nodes,
applicationFeatureSeedSchema.parse({
kind: "api",
value: "desktop.nested.write",
entry_path: ["desktop", "nested.write"],
}),
);
expect(matches).toHaveLength(1);
expect(
output.graph.nodes
.find(({ node_id: nodeId }) => nodeId === matches[0]?.node_id)
?.observations.some(
({ properties }) =>
Array.isArray(properties.entry_path) &&
properties.entry_path.join("\0") === "desktop\0nested.write",
),
).toBe(true);
expect(
findApplicationFeatureSeeds(
output.graph.nodes,
applicationFeatureSeedSchema.parse({
kind: "api",
value: "desktop.nested.write",
}),
),
).toHaveLength(2);
});
it("analyzes a plain long-line loader without package metadata", async () => {
const output = await analyzeSource(
`var Module = {}; fetch(""); new Worker(""); var payload = "${"x".repeat(65_536)}";`,
{ "game-data.js": "Module.data = { ready: true };" },
);
const nodes = output.graph.nodes.filter(
({ kind }) => kind === "endpoint" || kind === "worker",
);
expect(nodes.map(({ kind }) => kind).sort()).toEqual(["endpoint", "worker"]);
for (const node of nodes) {
expect(node.identity).toMatchObject({
strategy: "artifact-local-key",
key: node.kind === "endpoint" ? literalEndpointKey("") : "",
});
expect(node.observations.map(({ label }) => label)).toEqual([null]);
}
});
it("keeps exact values for empty literals without synthetic application labels", async () => {
const output = await analyzeSource(
"fetch(''); fetch('\"\"'); fetch('/api'); process.on('', () => 1);",
);
const endpoints = output.graph.nodes
.filter(({ kind }) => kind === "endpoint")
.map(({ identity, observations }) => ({
key: identity.strategy === "artifact-local-key" ? identity.key : null,
labels: observations.map(({ label }) => label),
values: observations.map(({ properties }) => properties.value),
}));
// The empty endpoint has no display label, so it cannot be confused with
// a literal two-quote endpoint; both keep exact identities.
expect(endpoints).toEqual(
expect.arrayContaining([
{ key: literalEndpointKey(""), labels: [null], values: [""] },
{ key: literalEndpointKey('""'), labels: ['""'], values: ['""'] },
{ key: literalEndpointKey("/api"), labels: ["/api"], values: ["/api"] },
]),
);
expect(endpoints).toHaveLength(3);
expect(
output.semantic_graph?.nodes
.filter(({ kind }) => kind === "event")
.map(({ label, properties }) => ({
label,
eventName: properties.event_name,
})),
).toEqual([{ label: '""', eventName: "" }]);
});
it.each([
{ version: 3, sources: [""], names: [], mappings: "AAAA" },
{ version: 3, sourceRoot: null, sources: [""], names: [], mappings: "AAAA" },
{ version: 3, sourceRoot: "", sources: [""], names: [], mappings: "AAAA" },
{
version: 3,
sections: [
{
offset: { line: 0, column: 0 },
map: { version: 3, sources: [""], names: [], mappings: "AAAA" },
},
],
},
])("projects an empty source-map source name: %j", async (map) => {
const output = await analyzeSource("//# sourceMappingURL=app.js.map", {
"app.js.map": JSON.stringify(map),
});
const originals = output.graph.nodes
.filter(({ kind }) => kind === "source-module")
.map(({ identity, observations }) => ({
originalSource:
identity.strategy === "source-map-original"
? identity.original_source
: null,
sources: observations.map(
({ source_map_reference }) => source_map_reference?.source_name,
),
}));
expect(originals).toEqual([{ originalSource: "", sources: [""] }]);
});
it("keeps empty and two-quote source-map names distinct", async () => {
const output = await analyzeSource("//# sourceMappingURL=app.js.map", {
"app.js.map": JSON.stringify({
version: 3,
sources: ["", '""'],
names: [],
mappings: "AAAA,CCAA",
}),
});
expect(
output.graph.nodes
.filter(({ kind }) => kind === "source-module")
.map(({ identity }) =>
identity.strategy === "source-map-original"
? identity.original_source
: null,
)
.sort(),
).toEqual(["", '""']);
});
it("does not match an empty endpoint with a literal two-quote seed", async () => {
const output = await analyzeSource("fetch(''); fetch('\"\"');");
const endpoints = new Map(
output.graph.nodes
.filter(({ kind }) => kind === "endpoint")
.map((node) => [
node.node_id,
node.identity.strategy === "artifact-local-key"
? node.identity.key
: null,
]),
);
const matches = findApplicationFeatureSeeds(output.graph.nodes, {
kind: "string",
value: '""',
match: "exact",
case_sensitive: true,
}).filter(({ node_id: nodeId }) => endpoints.has(nodeId));
expect(matches.map(({ node_id: nodeId }) => endpoints.get(nodeId))).toEqual([
literalEndpointKey('""'),
]);
});
const IPC_SOURCE =
"const { ipcMain } = require('electron'); ipcMain.handle('', () => 1); ipcMain.handle('\"\"', () => 2);";
const ipcChannels = (
nodes: Awaited<ReturnType<typeof analyzeSource>>["graph"]["nodes"],
) =>
new Map(
nodes
.filter(({ kind }) => kind === "ipc-channel")
.map((node) => [node.node_id, node.observations[0]?.properties.channel]),
);
it("selects an empty IPC channel with an exact empty seed", async () => {
const output = await analyzeSource(IPC_SOURCE);
const channels = ipcChannels(output.graph.nodes);
const matches = findApplicationFeatureSeeds(output.graph.nodes, {
kind: "channel",
value: "",
match: "exact",
case_sensitive: true,
});
expect(matches.map(({ node_id: nodeId }) => channels.get(nodeId))).toEqual([
"",
]);
});
const seedSchema = traceApplicationFeatureInputSchema.shape.seed;
const validateAdvertisedSeed = new Ajv2020({
strict: false,
validateFormats: false,
}).compile(z.toJSONSchema(seedSchema, { io: "input" }));
it.each([
[{ kind: "channel", value: "", match: "exact" }, true],
[{ kind: "string", value: "", match: "exact" }, true],
[{ kind: "channel", value: "/x" }, true],
[{ kind: "string", value: "" }, false],
[{ kind: "channel", value: "" }, false],
[{ kind: "channel", value: "", match: "contains" }, false],
[{ kind: "node-id", value: "", match: "exact" }, false],
[{ kind: "api", value: "desktop.nested.write" }, true],
[
{
kind: "api",
value: "desktop.nested.write",
entry_path: ["desktop", "nested.write"],
},
true,
],
[{ kind: "channel", value: "channel", entry_path: ["channel"] }, false],
] as const)(
"validates seed %j at runtime and in the advertised schema",
(seed, accepted) => {
expect(seedSchema.safeParse(seed).success).toBe(accepted);
expect(validateAdvertisedSeed(seed)).toBe(accepted);
},
);
it("pairs the same empty IPC channel across application versions", async () => {
const left = await analyzeSource(IPC_SOURCE);
const right = await analyzeSource(`${IPC_SOURCE} // v2`);
const leftChannels = ipcChannels(left.graph.nodes);
const rightChannels = ipcChannels(right.graph.nodes);
const matching = matchJavaScriptApplicationVersions(
left.graph.nodes,
right.graph.nodes,
);
expect(
matching.pairs
.filter(({ left: node }) => node.kind === "ipc-channel")
.map(({ left: leftNode, right: rightNode }) => [
leftChannels.get(leftNode.node_id),
rightChannels.get(rightNode.node_id),
]),
).toEqual(
expect.arrayContaining([
["", ""],
['""', '""'],
]),
);
expect(matching.unmatchedLeft.map(({ kind }) => kind)).not.toContain(
"ipc-channel",
);
});
it("selects an empty context-bridge API with an exact empty seed", async () => {
const output = await analyzeSource(
"const { contextBridge } = require('electron'); contextBridge.exposeInMainWorld('', {}); contextBridge.exposeInMainWorld('\"\"', {});",
);
const apis = new Map(
output.graph.nodes
.filter(({ kind }) => kind === "context-bridge-api")
.map((node) => [node.node_id, node.observations[0]?.properties.api_key]),
);
const matches = findApplicationFeatureSeeds(output.graph.nodes, {
kind: "api",
value: "",
match: "exact",
case_sensitive: true,
});
expect(matches.map(({ node_id: nodeId }) => apis.get(nodeId))).toEqual([""]);
});
it("pairs indexed source-map leaves by raw source name and root", async () => {
const map = (value: number) =>
JSON.stringify({
version: 3,
sections: ["one", "two"].map((name, line) => ({
offset: { line, column: 0 },
map: {
version: 3,
sourceRoot: `../${name}`,
sources: ["same.js"],
sourcesContent: [`export const value = ${String(value)};`],
names: [],
mappings: "AAAA",
},
})),
});
const left = await analyzeSource("//# sourceMappingURL=app.js.map", {
"app.js.map": map(1),
});
const right = await analyzeSource("//# sourceMappingURL=app.js.map", {
"app.js.map": map(2),
});
const matching = matchJavaScriptApplicationVersions(
left.graph.nodes,
right.graph.nodes,
);
const originals = matching.pairs.filter(
({ left: node }) => node.kind === "source-module",
);
expect(originals).toHaveLength(2);
for (const pair of originals) {
expect(pair).toMatchObject({
basis: "source-map-identity",
confidence: "high",
});
expect(pair.left.identity).toMatchObject({
original_source: "same.js",
source_root: expect.any(String),
});
if (pair.left.identity.strategy !== "source-map-original")
throw new Error("Expected source identity");
expect(pair.right.identity).toMatchObject({
source_root: pair.left.identity.source_root,
});
}
});
it("reports malformed source roots without manufacturing an original path", async () => {
const output = await analyzeSource("//# sourceMappingURL=app.js.map", {
"app.js.map": JSON.stringify({
version: 3,
sourceRoot: 42,
sources: ["a.js"],
names: [],
mappings: "AAAA",
}),
});
expect(
output.graph.nodes.filter(({ kind }) => kind === "source-module"),
).toEqual([]);
expect(
output.graph.nodes.flatMap(({ observations }) => observations),
).toContainEqual(
expect.objectContaining({
properties: expect.objectContaining({
limitation: "Source map sourceRoot must be a string or null.",
}),
evidence: expect.objectContaining({ state: "unavailable" }),
}),
);
});