test: consolidate JavaScript and provider fixture coverage

This commit is contained in:
morluto committed 2026-11-10 16:32:53 -05:00
1 parent 439c197c3f
commit 3c4be703cd
16 files changed
+23 -223

No files matched your search

+2 -9
View File
@@ -454,7 +454,7 @@ describe("dyld resolution outcomes: deriving path and weak-load outcomes", () =>
);
});
it("derives unresolved and earlier-candidate findings separately for weak loads", async () => {
it("reports earlier-rpath misses under environment uncertainty", async () => {
const trace = await traceDylibLoading(
memoryView({
[MAIN]: executable({
@@ -462,14 +462,7 @@ describe("dyld resolution outcomes: deriving path and weak-load outcomes", () =>
"@executable_path/../Overrides",
"@executable_path/../Frameworks",
],
dependencies: [
dependency("@rpath/libfound.dylib"),
dependency("@rpath/libgone.dylib", {
command: "LC_LOAD_WEAK_DYLIB",
weak: true,
}),
dependency("@rpath/libmissing.dylib"),
],
dependencies: [dependency("@rpath/libfound.dylib")],
dyld_environment: ["DYLD_LIBRARY_PATH=/tmp"],
}),
"Contents/Frameworks/libfound.dylib": parsed(slice()),
@@ -1178,15 +1178,7 @@ it("keeps distinct exposed renderer listener callsites distinct", () => {
result.electron.call_paths?.filter(({ channel }) => channel === "pong"),
).toHaveLength(2);
});
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");
@@ -73,24 +73,4 @@ describe("JavaScript semantic analysis: logical assignment", () => {
"unknown",
);
});
it("reproduces the issue #2646 inputs without an exact literal", () => {
for (const operator of ["||=", "??="]) {
const { binding, returned } = observe(
`function read(x) { x ${operator} 2; return x; }`,
);
expect(binding).not.toEqual({ status: "literal", value: 2 });
expect(returned).not.toEqual({ status: "literal", value: 2 });
}
const readOr = (x: number) => {
x ||= 2;
return x;
};
const readNullish = (x: number) => {
x ??= 2;
return x;
};
expect(readOr(5)).toBe(5);
expect(readNullish(5)).toBe(5);
});
});
@@ -874,21 +874,6 @@ describe("property mutation collection on minified bundles", () => {
],
});
}, 30000);
it("indexes many returned object methods instead of rescanning the object", () => {
const methods = Array.from(
{ length: 2000 },
(_, index) => `m${index}() { return shared; }`,
).join(",");
const writes = Array.from(
{ length: 2000 },
(_, index) => `box.m${index}().p${index} = ${index};`,
).join(" ");
const value = resultValue(
`const shared = { token: "TOKEN" }; const box = { ${methods} }; ${writes} return shared.token;`,
);
expect(value?.status).toBe("unknown");
}, 30000);
});
describe("results of methods on an escaped receiver", () => {
@@ -8,9 +8,9 @@ import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
*
* Measured on Node 24.22.1: `"pipe"`, `"overlapped"`, `"inherit"` and `"ignore"` start a
* child, while `"ipc"`, `"PIPE"`, `"pipe "`, `"pipeline"`, `"stream"` and `"stdio"` throw
* `ERR_INVALID_ARG_VALUE`. The set is exact and case-sensitive. `fork` appends `"ipc"` to a
* string and splits it, so an empty string becomes the lone element `"ipc"`, which `fork`
* refuses — `spawn(..., {stdio: ""})` still starts a child.
* `ERR_INVALID_ARG_VALUE`. The set is exact and case-sensitive. `fork` refuses an empty string in its
* own stdio-string validation; it does not split the string or append `"ipc"`.
* `spawn(..., {stdio: ""})` still starts a child.
*/
const spawnsFor = (body: string) =>
analyzeJavaScriptSemantics(
@@ -78,30 +78,6 @@ it.each(cases)(
},
);
it.each(["call", "apply"] as const)(
"does not treat an owned local %s replacement as a loader invocation",
(method) => {
const args = method === "call" ? "null,'node:path'" : "null,['node:path']";
const analysis = analyzeJavaScriptStaticSource(
`${named}const load=make(import.meta.url);load.${method}=()=>({kind:'local'});load.${method}(${args});`,
);
expect(analysis.parse_error_count).toBe(0);
expect(
analysis.references.filter(({ kind }) => kind === "require"),
).toEqual([]);
},
);
it("keeps uncertainty for conditional call-member replacement", () => {
const analysis = analyzeJavaScriptStaticSource(
`${named}const load=make(import.meta.url);if(flag)load.call=()=>({kind:'local'});load.call(null,'node:path');`,
);
expect(analysis.parse_error_count).toBe(0);
expect(analysis.references.filter(({ kind }) => kind === "require")).toEqual([
expect.objectContaining({ kind: "require", specifier: null }),
]);
});
it("uses the restored call member after a local replacement", () => {
const source = `${named}const load=make(import.meta.url);load.call=()=>({kind:'local'});load.call=Function.prototype.call;load.call(null,'node:path');`;
const analysis = analyzeJavaScriptStaticSource(source);
-26
View File
@@ -491,32 +491,6 @@ describe("historical source graph validation", () => {
],
}),
).toThrow(/normalized and inventoried/u);
expect(() =>
createHistoricalSourceGraph({
...graphInput(),
inventory_state: "partial",
entries: graphInput().entries.map((entry) =>
entry.kind === "symlink"
? { ...entry, target: "/etc/passwd", target_state: "external" }
: entry,
),
}),
).not.toThrow();
expect(() =>
createHistoricalSourceGraph({
...graphInput(),
inventory_state: "partial",
entries: graphInput().entries.map((entry) =>
entry.kind === "symlink"
? {
...entry,
target: "<outside-root>",
target_state: "external",
}
: entry,
),
}),
).toThrow(/target path does not match/u);
expect(() =>
createHistoricalSourceGraph({
...graphInput(),
+1 -10
View File
@@ -1,9 +1,6 @@
import { describe, expect, it } from "vitest";
import {
ghidraDisabledAnalyzersSchema,
MAX_GHIDRA_DISABLED_ANALYZER_NAMES,
} from "../config/ghidraDisabledAnalyzers.js";
import { MAX_GHIDRA_DISABLED_ANALYZER_NAMES } from "../config/ghidraDisabledAnalyzers.js";
import { parseConfig } from "../config/parseConfig.js";
const INSTALL = "/opt/ghidra_12.1.4_PUBLIC";
@@ -81,10 +78,4 @@ describe("REA_GHIDRA_DISABLED_ANALYZERS", () => {
],
});
});
it("admits the documented Objective-C analyzer name", () => {
expect(
ghidraDisabledAnalyzersSchema.parse("Objective-C Message Analyzer"),
).toEqual(["Objective-C Message Analyzer"]);
});
});
-21
View File
@@ -1534,27 +1534,6 @@ describe("Ghidra JVM native directory admission", () => {
});
});
it("accepts a JDK whose os.arch selects the validated native decompiler directory", async () => {
const result = await inspectGhidraInstallation(
{
resourceOwner: inspectionResources.owner,
environment: {},
installDir: INSTALL,
platform: "darwin",
architecture: "arm64",
},
host({
probeJava: () =>
javaProbeOk<GhidraJavaObservation>({
...JAVA,
osArch: "aarch64",
osName: "Mac OS X",
}),
}),
);
expect(result.status).toBe("available");
});
it("rejects a successful Java probe that omits os.arch", async () => {
const { osArch: _observedArchitecture, ...withoutArchitecture } = JAVA;
const result = await inspectGhidraInstallation(
@@ -5117,17 +5117,6 @@ describe("Ghidra disabled analyzer applicability per program", () => {
}
expect(counts).toEqual({ starts: 1, calls: 0, closes: 1 });
});
it("fails closed on an analyzer report the profile did not commit", async () => {
const { counts, client } = await analyzerSession(
undefined,
analyzerReport(["Objective-C Message Analyzer", "not_applicable"]),
);
const failed = await client.execute("health", {});
expect(failed.ok).toBe(false);
if (!failed.ok) expect(failed.error._tag).toBe("ProviderAdapterError");
expect(counts).toEqual({ starts: 1, calls: 0, closes: 1 });
});
});
type Handshake = Partial<GhidraSessionInfo>;
-31
View File
@@ -3,7 +3,6 @@ import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createIdaTarget } from "../../tests/fixtures/idaMcp.js";
import { readIdaConfiguration } from "./IdaConfiguration.js";
import { decodeIdaToolResult } from "./IdaMcpConnection.js";
import type { RetainedResourceScope } from "../application/AnalysisProvider.js";
import { OwnedFileHandle } from "../filesystem/OwnedFileHandle.js";
import * as regularFile from "../filesystem/RegularFile.js";
@@ -252,36 +251,6 @@ describe("IDA registration descriptor ownership", () => {
});
});
describe("IDA MCP result decoding", () => {
it("parses FastMCP wrappers, structured dictionaries, text, arrays, and explicit errors", () => {
expect(
decodeIdaToolResult({
content: [],
structuredContent: { result: [{ address: "0x1" }] },
}),
).toEqual([{ address: "0x1" }]);
expect(
decodeIdaToolResult({
content: [],
structuredContent: { address: "0x1", name: "f" },
}),
).toEqual({ address: "0x1", name: "f" });
expect(
decodeIdaToolResult({ content: [{ type: "text", text: "int f() {}" }] }),
).toBe("int f() {}");
expect(
decodeIdaToolResult({ content: [{ type: "text", text: '{"data":[]}' }] }),
).toEqual({ data: [] });
expect(() =>
decodeIdaToolResult({
content: [{ type: "text", text: "No function at 0x1" }],
isError: true,
}),
).toThrow("No function at 0x1");
expect(() => decodeIdaToolResult({ content: "invalid" })).toThrow();
});
});
it.each(["env", "headers"] as const)(
"preserves all explicit IDA %s keys",
async (field) => {
-12
View File
@@ -620,16 +620,4 @@ describe("headless IDA document identity", () => {
expect((await client.close()).ok).toBe(true);
}
});
it("lists exactly the admitted program", async () => {
const { client, target } = await session();
try {
const result = await client.execute("list_documents", {});
if (!result.ok) throw result.error;
expect(result.value.result).toEqual([target.path]);
} finally {
expect((await client.close()).ok).toBe(true);
}
});
});
+10
View File
@@ -75,6 +75,15 @@ describe("upstream IDA tool result decoding", () => {
);
});
it("decodes scalar wrappers and legacy text without dropping structured dictionaries", () => {
expect(
decodeIdaToolResult({
content: [],
structuredContent: { result: [{ address: "0x1" }] },
}),
).toEqual([{ address: "0x1" }]);
expect(
decodeIdaToolResult({ content: [{ type: "text", text: '{"data":[]}' }] }),
).toEqual({ data: [] });
expect(
decodeIdaToolResult({
content: [],
@@ -707,6 +716,7 @@ it("keeps error, truncation and native-text precedence ahead of structured JSON
it("requires a valid content array even when IDA supplies structured evidence", () => {
for (const content of [
"invalid",
undefined,
null,
{},
+4 -20
View File
@@ -29,26 +29,6 @@ it("names the Wakaru configuration requirement when it is not configured", async
});
});
it("rejects an exhausted shared execution budget before launching the next command", async () => {
await expect(
runWakaruCommand({
command: "/selected/wakaru",
limiter: "/usr/bin/prlimit",
arguments: ["--version"],
cwd: "/tmp/unused",
environment: {},
deadline: performance.now() - 1,
launcher: async () => {
throw new Error("An expired budget must not launch");
},
}),
).rejects.toMatchObject({
_tag: "AnalysisTimeoutError",
operation: "recover_javascript_sources",
timeoutMs: RECOVERY_LIMITS.timeoutMs,
});
});
it("projects the fixed Wakaru deadline without an editable setting", async () => {
const refusal: unknown = await runWakaruCommand({
command: "/selected/wakaru",
@@ -63,6 +43,10 @@ it("projects the fixed Wakaru deadline without an editable setting", async () =>
}).catch((cause: unknown) => cause);
if (!(refusal instanceof AnalysisTimeoutError))
throw new Error("Expected the Wakaru deadline");
expect(refusal).toMatchObject({
operation: "recover_javascript_sources",
timeoutMs: RECOVERY_LIMITS.timeoutMs,
});
const projected = projectAnalysisError(refusal);
expect(projected).toMatchObject({
code: "provider_timeout",
@@ -290,7 +290,7 @@ describe("upstream source-map point evidence", () => {
});
describe("source-map layout admission", () => {
it.each(["ACAA", "AAAAC", "g", "!", "AA", "D"])(
it.each(["AAAAC", "g", "!", "AA", "D"])(
"rejects malformed VLQ/components/indexes: %s",
(mappings) => {
expect(() => trace({ ...leaf, mappings })).toThrow();
+1 -11
View File
@@ -458,20 +458,10 @@ it("keeps signed zero and non-finite float and complex constants JSON-safe", ()
it("truncates string previews without introducing a lone surrogate", () => {
const prefix = "x".repeat(79);
const report = parse(
withConsts([
marshalUnicode(Buffer.from(`${prefix}😀tail`)),
marshalUnicode(
Buffer.concat([
Buffer.from(prefix),
Buffer.from("eda080", "hex"),
Buffer.from("tail"),
]),
),
]),
withConsts([marshalUnicode(Buffer.from(`${prefix}😀tail`))]),
);
expect(root(report).consts).toEqual([
{ kind: "str", bytes: 87, truncated: true, value: prefix },
{ kind: "str", bytes: 86, truncated: true, value: `${prefix}\ud800` },
]);
});