Files
reverse-engineering-anything/src/config.test.ts
T
morluto cb4e5cfbff feat(analysis): make resource budgets recoverable across CLI and MCP (#9706)
* refactor(analysis): centralize resource budgets and recovery facts

* feat(analysis): expose ASAR and Ghidra runtime admission controls

* feat(artifacts): expose reader admission and preserve limited recognition

* feat(javascript): expose recoverable semantic precision budgets

* feat(web): add recoverable source-map budgets and compact retained views

* feat(web): make historical capture decoding budgets recoverable

* fix(desktop): enforce selected view policies across capture and export

* feat(runtime): make Frida and LLDB retention budgets selectable

* feat(native): expose diagnostic budgets and preserve exhaustion causes

* feat(managed): expose recoverable snapshot and representation budgets

* feat(metadata): make Go and Rust inspection budgets recoverable

* feat(wasm): expose representation and producer admissions

* feat(evm): expose per-call worker resource budgets

* feat(android): make resource and string-pool omissions recoverable

* feat(producers): expose command and recorded-capture budgets

* docs(analysis): record recovery contracts and integrated validation

* fix(docs): link calibration oracle from published verification pages

* fix(docs): restore desktop task anchors and receipt links

* test(analysis): align fixtures with selectable budget contracts

* fix(analysis): remove source-location projector dependency cycle

* test(analysis): refresh budget recovery and verifier regressions

* test(analysis): preserve format and publication recovery expectations

* test(windows): align conformance with owned runtime admission

* fix(desktop): preserve selected alias kinds in exported state

* test(analysis): align provider fixtures with recoverable budgets

* test(desktop): validate emitted alias metadata structurally

* fix(ghidra): classify bounded Windows endpoint refusals

* test(cli): assert selectable artifact admission recovery

* fix(cli): preserve capture provenance in environment builds
2026-11-10 05:11:20 -05:00

648 lines
20 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
defaultHopperLauncherPath,
inspectNativeAnalysisConfiguration,
parseConfig,
} from "./config/parseConfig.js";
import {
parseEnvironmentValues,
parseJebMcpTimeoutMs,
} from "./config/environment.js";
import { projectAnalysisError } from "./domain/analysisErrorProjection.js";
it("still rejects a NUL in an unresolved expression", () => {
const result = parseEnvironmentValues(
{ REA_GHIDRA_STARTUP_TIMEOUT_MS: "${TIMEOUT}\0private" },
new Set(["REA_GHIDRA_STARTUP_TIMEOUT_MS"]),
);
expect(result.ok).toBe(false);
if (!result.ok) expect(JSON.stringify(result.error)).not.toContain("private");
});
it.each([
["REA_ASAR_MAX_LISTING_BYTES", "9007199254740992"],
["REA_GHIDRA_VALUE_FLOW_MAX_OPERATIONS", "0"],
["REA_GHIDRA_VALUE_FLOW_MAX_INPUTS_PER_OPERATION", "2147483648"],
["REA_GHIDRA_VALUE_FLOW_MAX_EDGES", "1.5"],
["REA_GHIDRA_DECOMPILE_MAX_PAYLOAD_MIB", "2048"],
])(
"rejects invalid %s at launch with its exact setting identity",
(setting, value) => {
const parsed = parseConfig({ [setting]: value });
if (parsed.ok) throw new Error("Invalid resource policy accepted");
expect(parsed.error.settings).toContainEqual(
expect.objectContaining({ setting }),
);
},
);
it("keeps the caller's opt-in Ghidra checkpoint directory and leaves it disabled by default", () => {
const selected = parseConfig({
REA_GHIDRA_CHECKPOINT_DIR: "/tmp/checkpoints with spaces",
});
expect(selected.ok && selected.value.ghidraCheckpointDirectory).toBe(
"/tmp/checkpoints with spaces",
);
const defaults = parseConfig({});
expect(
defaults.ok && defaults.value.ghidraCheckpointDirectory,
).toBeUndefined();
expect(parseConfig({ REA_GHIDRA_CHECKPOINT_DIR: "relative" }).ok).toBe(false);
});
it.each([
["REA_GHIDRA_PROJECT_PATH", "/tmp/selected.gpr"],
["REA_GHIDRA_SEED_FILE", "/tmp/seeds.json"],
["REA_GHIDRA_LANGUAGE_ID", "x86:LE:64:default"],
["REA_GHIDRA_NATIVEAOT_JAR", "/tmp/extension.jar"],
["REA_GHIDRA_DISABLED_ANALYZERS", "Stack"],
])(
"refuses %s with checkpoints rather than silently dropping the selected input",
(setting, value) => {
const result = parseConfig({
REA_GHIDRA_CHECKPOINT_DIR: "/tmp/checkpoints",
[setting]: value,
});
if (result.ok) throw new Error("conflicting inputs accepted");
expect(result.error.message).toContain("checkpoints");
expect(result.error).toMatchObject({
settings: [expect.objectContaining({ setting })],
});
},
);
it("reports a configuration failure for a NUL in Hopper loader arguments", () => {
const result = parseConfig({
HOPPER_LOADER_ARGS_JSON: JSON.stringify(["--option", "value\0suffix"]),
});
if (result.ok)
throw new Error("NUL argv cannot be passed to a child process");
expect(result.error).toMatchObject({
settings: [{ setting: "HOPPER_LOADER_ARGS_JSON" }],
});
expect(result.error.message).toContain("NUL");
});
it.each([
["unknown value", { CALLER_SETTING: "private\0value" }],
["unknown name", { ["CALLER\0SETTING"]: "private value" }],
["known path", { HOPPER_TARGET_PATH: "private\0path" }],
] as const)(
"rejects a NUL in an environment %s without exposing its value",
(_name, environment) => {
for (const result of [
parseEnvironmentValues(environment),
parseConfig(environment),
]) {
if (result.ok) throw new Error("Expected invalid environment");
expect(result.error.settings).toHaveLength(1);
expect(result.error.settings[0]?.constraint).toContain("NUL");
expect(JSON.stringify(projectAnalysisError(result.error))).not.toContain(
"private",
);
}
},
);
it("accepts ordinary custom environment strings and omitted values", () => {
expect(
parseEnvironmentValues({
CALLER_SETTING: "\nvalue=one;二\t",
EMPTY_SETTING: "",
OMITTED_SETTING: undefined,
}).ok,
).toBe(true);
});
describe("runtime configuration", () => {
it("preserves a selected MCP input allowance for deferred startup and refuses disabling values", () => {
expect(parseConfig({ REA_MCP_MAX_INPUT_ELEMENTS: "8192" })).toMatchObject({
ok: true,
value: { mcpMaxInputElements: 8192 },
});
const invalid = parseConfig({ REA_MCP_MAX_INPUT_ELEMENTS: "Infinity" });
expect(invalid.ok).toBe(false);
if (invalid.ok)
throw new Error(
"Infinite MCP input allowance must not be accepted from the environment",
);
expect(invalid.error.settings).toEqual([
{
setting: "REA_MCP_MAX_INPUT_ELEMENTS",
constraint: expect.stringContaining("positive decimal safe integer"),
},
]);
});
it("allows target-free startup and applies runtime defaults", () => {
const empty = parseConfig({});
expect(empty.ok).toBe(true);
if (empty.ok) {
expect(empty.value.hopperTargetPath).toBeUndefined();
expect(empty.value.analysisProvider).toBe("auto");
expect(empty.value.hopperStartupTimeoutMs).toBe(120_000);
}
const result = parseConfig({ HOPPER_TARGET_PATH: "/usr/bin/true" });
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value.hopperTargetKind).toBe("executable");
expect(result.value.hopperLoaderArgs).toEqual([]);
expect(result.value.logLevel).toBe("info");
expect(result.value.referenceSourcePolicy).toEqual({
secretPatterns: [],
});
}
});
it("parses one shared provider selector and rejects unstable IDs", () => {
expect(
parseConfig({
REA_ANALYSIS_PROVIDER: "ida",
REA_IDA_MCP_CONFIG: "/tmp/ida-mcp.json",
}),
).toMatchObject({
ok: true,
value: { analysisProvider: "ida", idaMcpConfigPath: "/tmp/ida-mcp.json" },
});
expect(parseConfig({ REA_IDA_MCP_CONFIG: "relative.json" }).ok).toBe(false);
expect(parseConfig({ REA_ANALYSIS_PROVIDER: "ghidra" })).toMatchObject({
ok: true,
value: { analysisProvider: "ghidra" },
});
expect(parseConfig({ REA_ANALYSIS_PROVIDER: "auto" })).toMatchObject({
ok: true,
value: { analysisProvider: "auto" },
});
for (const invalid of ["", "Auto", "ghidra_1", "ghidra "])
expect(parseConfig({ REA_ANALYSIS_PROVIDER: invalid }).ok).toBe(false);
});
it("shares native admission with doctor and ignores the JEB-only timeout", () => {
expect(inspectNativeAnalysisConfiguration({})).toEqual({ issues: [] });
expect(
inspectNativeAnalysisConfiguration({ REA_JEB_MCP_TIMEOUT_MS: "abc" }),
).toEqual({ issues: [] });
expect(parseConfig({ REA_JEB_MCP_TIMEOUT_MS: "abc" }).ok).toBe(true);
const rejected = inspectNativeAnalysisConfiguration({
REA_ANALYSIS_PROVIDER: "Hopper",
REA_GHIDRA_STARTUP_TIMEOUT_MS: "abc",
});
expect(rejected.issues.map(({ setting }) => setting)).toEqual([
"REA_ANALYSIS_PROVIDER",
"REA_GHIDRA_STARTUP_TIMEOUT_MS",
]);
expect(
parseConfig({
REA_ANALYSIS_PROVIDER: "Hopper",
REA_GHIDRA_STARTUP_TIMEOUT_MS: "abc",
}).ok,
).toBe(false);
});
it("preserves the timeout's schema cause when aggregate admission stops at NUL", () => {
const inspected = inspectNativeAnalysisConfiguration({
CALLER_SETTING: "private\0value",
REA_HOPPER_STARTUP_TIMEOUT_MS: "0",
});
expect(inspected.issues).toEqual([
{
setting: "CALLER_SETTING",
constraint:
"Environment variable names and values must not contain NUL bytes",
},
]);
const timeout = inspected.configuredHopperStartupTimeout;
if (timeout === undefined || timeout.ok)
throw new Error("Expected the configured timeout's independent refusal");
expect(timeout.error.cause).toMatchObject({
name: "ZodError",
issues: [{ message: "must be a positive decimal integer" }],
});
expect(timeout.error.settings).toEqual([
{
setting: "REA_HOPPER_STARTUP_TIMEOUT_MS",
constraint: "must be a positive decimal integer",
},
]);
});
it("parses absolute BYO Ghidra and optional Java paths", () => {
expect(
parseConfig({
GHIDRA_INSTALL_DIR: "/opt/ghidra_12.1.4_PUBLIC",
JAVA_HOME: "/usr/lib/jvm/jdk-21",
}),
).toMatchObject({
ok: true,
value: {
ghidraInstallDir: "/opt/ghidra_12.1.4_PUBLIC",
ghidraJavaHome: "/usr/lib/jvm/jdk-21",
},
});
expect(parseConfig({ GHIDRA_INSTALL_DIR: "relative/ghidra" }).ok).toBe(
false,
);
expect(parseConfig({ JAVA_HOME: "relative/jdk" }).ok).toBe(false);
});
it("parses an optional absolute BYO ilspycmd path", () => {
expect(
parseConfig({ REA_ILSPY_CMD_PATH: "/home/user/.dotnet/tools/ilspycmd" }),
).toMatchObject({
ok: true,
value: {
ilspyCmdPath: "/home/user/.dotnet/tools/ilspycmd",
},
});
expect(parseConfig({ REA_ILSPY_CMD_PATH: "relative/ilspycmd" }).ok).toBe(
false,
);
});
});
describe("runtime target configuration", () => {
it("rejects invalid target kinds with actionable environment diagnostics", () => {
const result = parseConfig({ HOPPER_TARGET_KIND: "archive" });
if (result.ok) throw new Error("Expected invalid target kind");
expect(result.error.message).toContain("Invalid REA environment");
});
it("parses database kind and loader arguments", () => {
expect(
parseConfig({
HOPPER_LAUNCHER_PATH: "/custom/hopper",
REA_ANALYSIS_PROVIDER: "hopper",
HOPPER_TARGET_PATH: "/fixture/sample.hop",
HOPPER_TARGET_KIND: "database",
HOPPER_LOADER_ARGS_JSON: '["-l","FAT","--aarch64","-l","Mach-O"]',
}),
).toMatchObject({
ok: true,
value: {
hopperLauncherPath: "/custom/hopper",
analysisProvider: "hopper",
hopperTargetPath: "/fixture/sample.hop",
hopperTargetKind: "database",
hopperLoaderArgs: ["-l", "FAT", "--aarch64", "-l", "Mach-O"],
logLevel: "info",
referenceSourcePolicy: {
secretPatterns: [],
},
},
});
});
});
describe("runtime collection configuration", () => {
it("preserves empty loader arguments and rejects empty exclusion patterns", () => {
expect(
parseConfig({ HOPPER_LOADER_ARGS_JSON: '["", " ", "--option"]' }),
).toMatchObject({
ok: true,
value: { hopperLoaderArgs: ["", " ", "--option"] },
});
expect(parseConfig({ REA_REFERENCE_SECRET_PATTERNS_JSON: '[""]' }).ok).toBe(
false,
);
});
it.each(["not-json", "{}", '"just a string"', '["ok",1]'])(
"rejects invalid loader args: %s",
(encoded) => {
const result = parseConfig({
HOPPER_TARGET_PATH: "/tmp/a",
HOPPER_LOADER_ARGS_JSON: encoded,
});
if (result.ok)
throw new Error("expected malformed loader arguments to fail");
expect(result.error.message).toContain(
encoded === "not-json" ? "valid JSON" : "array of strings",
);
},
);
it("parses supported log levels and rejects unknown levels", () => {
const configured = parseConfig({ REA_LOG_LEVEL: "debug" });
expect(configured.ok && configured.value.logLevel).toBe("debug");
expect(parseConfig({ REA_LOG_LEVEL: "verbose" }).ok).toBe(false);
});
it("parses reference source secret patterns", () => {
const result = parseConfig({
REA_REFERENCE_SECRET_PATTERNS_JSON: '["*.env", "*.pem", "secrets/"]',
});
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value.referenceSourcePolicy).toEqual({
secretPatterns: ["*.env", "*.pem", "secrets/"],
});
}
});
it.each(["not-json", "{}", '["*.ok", 1]'])(
"rejects invalid reference source secret patterns: %s",
(encoded) => {
expect(
parseConfig({ REA_REFERENCE_SECRET_PATTERNS_JSON: encoded }).ok,
).toBe(false);
},
);
});
describe("configuration failure diagnostics", () => {
it.each(["1", "900000", "2147483647"])(
"accepts Hopper startup deadline %s without changing Ghidra's deadline",
(value) => {
expect(
parseConfig({ REA_HOPPER_STARTUP_TIMEOUT_MS: value }),
).toMatchObject({
ok: true,
value: {
hopperStartupTimeoutMs: Number(value),
ghidraStartupTimeoutMs: 330_000,
},
});
},
);
it.each(["", "0", "-1", "1.5", "1e3", "invalid", "2147483648"])(
"rejects malformed Hopper startup deadline %j with its setting",
(value) => {
const result = parseConfig({ REA_HOPPER_STARTUP_TIMEOUT_MS: value });
if (result.ok) throw new Error("Expected invalid startup deadline");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "configuration_invalid",
category: "invalid_input",
details: {
settings: [
{
setting: "REA_HOPPER_STARTUP_TIMEOUT_MS",
constraint: expect.any(String),
},
],
},
});
},
);
it.each([
"",
" ",
"0",
"-5",
"abc",
"1.5",
"1e3",
"0x100",
"2147483648",
"9007199254740991",
])(
"rejects malformed Ghidra startup deadline %j with its setting and constraint",
(value) => {
const result = parseConfig({ REA_GHIDRA_STARTUP_TIMEOUT_MS: value });
if (result.ok) throw new Error("Expected invalid startup deadline");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "configuration_invalid",
category: "invalid_input",
details: {
settings: [
{
setting: "REA_GHIDRA_STARTUP_TIMEOUT_MS",
constraint: expect.any(String),
},
],
},
});
},
);
it.each(["1", "90000", "2147483647"])(
"accepts Swift compile deadline %s without changing Hopper's deadline",
(value) => {
expect(
parseEnvironmentValues({ REA_SWIFTC_COMPILE_TIMEOUT_MS: value }),
).toMatchObject({
ok: true,
value: { REA_SWIFTC_COMPILE_TIMEOUT_MS: Number(value) },
});
expect(
parseConfig({ REA_SWIFTC_COMPILE_TIMEOUT_MS: value }),
).toMatchObject({
ok: true,
value: {
hopperStartupTimeoutMs: 120_000,
},
});
},
);
it.each(["", "0", "-1", "1.5", "1e3", "invalid", "2147483648"])(
"rejects malformed Swift compile deadline %j with its setting",
(value) => {
const result = parseConfig({ REA_SWIFTC_COMPILE_TIMEOUT_MS: value });
if (result.ok) throw new Error("Expected invalid Swift compile deadline");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "configuration_invalid",
category: "invalid_input",
details: {
settings: [
{
setting: "REA_SWIFTC_COMPILE_TIMEOUT_MS",
constraint: expect.any(String),
},
],
},
});
},
);
it.each([
["GHIDRA_INSTALL_DIR", "./fixture"],
["JAVA_HOME", "./jdk"],
["REA_GHIDRA_NATIVEAOT_JAR", "./missing.jar"],
])(
"names the rejected %s and its absolute-path constraint",
(setting, value) => {
const result = parseConfig({ [setting]: value });
expect(result.ok).toBe(false);
if (result.ok) return;
const projected = projectAnalysisError(result.error);
expect(projected).toMatchObject({
code: "configuration_invalid",
category: "invalid_input",
message: expect.stringContaining(setting),
remediation: { action: expect.stringContaining(setting) },
details: {
settings: [
{ setting, constraint: expect.stringMatching(/absolute/iu) },
],
},
});
expect(projected.message).toMatch(/absolute/iu);
expect(projected.remediation.action).toMatch(/correct/iu);
// The rejected value itself is not echoed.
expect(JSON.stringify(projected)).not.toContain(value);
},
);
it("names settings rejected by their own parsers", () => {
const result = parseConfig({ HOPPER_LOADER_ARGS_JSON: "not-json" });
expect(result.ok).toBe(false);
if (!result.ok)
expect(projectAnalysisError(result.error)).toMatchObject({
details: {
settings: [
{
setting: "HOPPER_LOADER_ARGS_JSON",
constraint: "HOPPER_LOADER_ARGS_JSON must be valid JSON",
},
],
},
});
});
});
describe("JEB MCP timeout configuration", () => {
it("stays optional and validates the caller-selected environment value", () => {
expect(parseJebMcpTimeoutMs(undefined)).toEqual({
ok: true,
value: undefined,
});
expect(parseJebMcpTimeoutMs("900000")).toEqual({
ok: true,
value: 900_000,
});
expect(
parseEnvironmentValues({ REA_JEB_MCP_TIMEOUT_MS: "900000" }),
).toMatchObject({ ok: true, value: { REA_JEB_MCP_TIMEOUT_MS: 900_000 } });
});
it.each(["", "0", "-1", "1.5", "1e3", "invalid", "2147483648"])(
"rejects malformed timeout %j with its setting",
(value) => {
const result = parseJebMcpTimeoutMs(value);
if (result.ok) throw new Error("Expected invalid JEB MCP timeout");
expect(projectAnalysisError(result.error)).toMatchObject({
details: {
settings: [
{
setting: "REA_JEB_MCP_TIMEOUT_MS",
constraint: expect.any(String),
},
],
},
});
},
);
});
describe("default Linux Hopper launcher", () => {
const home = "/fixture/home";
const withExecutables =
(paths: readonly string[]) =>
(path: string): boolean =>
paths.includes(path);
it.each([["/opt/hopper-v4/bin/hopper"], ["/usr/bin/hopper"]])(
"selects an existing system launcher %s",
(path) => {
expect(
defaultHopperLauncherPath("linux", home, withExecutables([path])),
).toBe(path);
},
);
it("prefers REA-managed and user-local launchers over system paths", () => {
expect(
defaultHopperLauncherPath(
"linux",
home,
withExecutables([
"/opt/hopper/bin/Hopper",
"/opt/hopper-v4/bin/hopper",
"/usr/bin/hopper",
]),
),
).toBe("/opt/hopper/bin/Hopper");
expect(
defaultHopperLauncherPath(
"linux",
home,
withExecutables([
`${home}/.local/share/rea/hopper/bin/Hopper`,
"/usr/bin/hopper",
]),
),
).toBe(`${home}/.local/share/rea/hopper/bin/Hopper`);
});
});
describe("default macOS Hopper launcher", () => {
it("selects the system path before the installer user path", () => {
const home = "/fixture/home";
const system =
"/Applications/Hopper Disassembler.app/Contents/MacOS/hopper";
const user =
"/fixture/home/Applications/Hopper Disassembler.app/Contents/MacOS/hopper";
const executable = (path: string): boolean =>
path === system || path === user;
expect(defaultHopperLauncherPath("darwin", home, executable)).toBe(system);
expect(
defaultHopperLauncherPath("darwin", home, (path) => path === user),
).toBe(user);
});
it("keeps an explicit launcher path unchanged", () => {
const configured = "/custom/../Hopper launcher";
const parsed = parseConfig({ HOPPER_LAUNCHER_PATH: configured });
expect(parsed).toMatchObject({
ok: true,
value: { hopperLauncherPath: configured },
});
});
});
describe("Ghidra decompile limit configuration", () => {
it.each([
[undefined, undefined],
["1", 1],
["30", 30],
["86400", 86_400],
["86401", 86_401],
["2147483", 2_147_483],
] as const)(
"reads the Ghidra decompile limit %j as %j seconds",
(value, expected) => {
const result = parseConfig(
value === undefined
? {}
: { REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS: value },
);
if (!result.ok) throw result.error;
expect(result.value.ghidraDecompileTimeoutSeconds).toBe(expected);
},
);
it.each(["", "0", "-1", "1.5", "30s", "2147484", "2147483647"])(
"rejects malformed Ghidra decompile limit %j with its setting",
(value) => {
const result = parseConfig({
REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS: value,
});
if (result.ok) throw new Error("Expected invalid decompile limit");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "configuration_invalid",
details: {
settings: [
{
setting: "REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS",
constraint: expect.any(String),
},
],
},
});
},
);
});