mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 11:56:12 +00:00
* feat(mcp): support runtime analysis limit recovery * refactor(config): remove unused limit re-exports * fix(android): keep fixture release metadata dependency-free * fix(settings): preserve provider budgets and recovery facts * fix(tests): resolve lint plugins from their repository owner * refactor(mcp): verify guidance delivery without frozen wording * fix(android): align availability with runtime timeout * test(recovery): assert limit facts instead of frozen wording * fix(mcp): share runtime settings with the default JADX provider * test(mcp): bound acknowledgements and release held work on failure * fix(catalog): declare runtime settings tools as MCP-only
160 lines
5.2 KiB
TypeScript
160 lines
5.2 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { AnalysisSettings } from "./AnalysisSettings.js";
|
|
import {
|
|
runtimeAnalysisConfig,
|
|
runtimeAsarHeaderAdmission,
|
|
} from "../config/runtimeAnalysisSettings.js";
|
|
import { parseConfig } from "../config/parseConfig.js";
|
|
|
|
it("updates atomically, restores launch values, and refuses stale or invalid writes", () => {
|
|
const store = new AnalysisSettings({ REA_ASAR_MAX_HEADER_BYTES: "20000000" });
|
|
const inspect = () => store.inspect({ settings: ["asar.max_header_bytes"] });
|
|
expect(inspect().settings[0]).toMatchObject({
|
|
value: 20000000,
|
|
source: "setting",
|
|
});
|
|
expect(
|
|
store.update({
|
|
changes: [
|
|
{ setting: "asar.max_header_bytes", action: "set", value: 30000000 },
|
|
],
|
|
expected_revision: 0,
|
|
}).ok,
|
|
).toBe(true);
|
|
expect(inspect()).toMatchObject({
|
|
revision: 1,
|
|
settings: [{ value: 30000000, source: "runtime" }],
|
|
});
|
|
expect(
|
|
store.update({
|
|
changes: [
|
|
{ setting: "asar.max_header_bytes", action: "set", value: 30000000 },
|
|
],
|
|
}),
|
|
).toMatchObject({ ok: true, value: { changed: false, revision: 1 } });
|
|
for (const changes of [
|
|
[
|
|
{ setting: "asar.max_header_bytes", action: "set", value: 100 },
|
|
{ setting: "wasm.command_timeout_ms", action: "set", value: 0 },
|
|
],
|
|
[
|
|
{ setting: "asar.max_header_bytes", action: "reset" },
|
|
{ setting: "asar.max_header_bytes", action: "set", value: 100 },
|
|
],
|
|
[{ setting: "NODE_OPTIONS", action: "set", value: 1 }],
|
|
])
|
|
expect(store.update({ changes }).ok).toBe(false);
|
|
expect(
|
|
store.update({
|
|
changes: [{ setting: "asar.max_header_bytes", action: "reset" }],
|
|
expected_revision: 0,
|
|
}).ok,
|
|
).toBe(false);
|
|
expect(inspect().revision).toBe(1);
|
|
expect(
|
|
store.update({
|
|
changes: [{ setting: "asar.max_header_bytes", action: "reset" }],
|
|
}),
|
|
).toMatchObject({
|
|
ok: true,
|
|
value: { revision: 2, settings: [{ value: 20000000, source: "setting" }] },
|
|
});
|
|
expect(new AnalysisSettings({}).inspect().revision).toBe(0);
|
|
});
|
|
|
|
it("keeps accepted operations and their typed provider views stable across concurrent updates", async () => {
|
|
const store = new AnalysisSettings({});
|
|
const config = parseConfig({});
|
|
if (!config.ok) throw config.error;
|
|
const live = runtimeAnalysisConfig(config.value, store.read);
|
|
const asar = runtimeAsarHeaderAdmission({}, store.read);
|
|
let release!: () => void;
|
|
const barrier = new Promise<void>((resolve) => {
|
|
release = resolve;
|
|
});
|
|
const pending = store.run(async () => {
|
|
await barrier;
|
|
expect(asar.maximumBytes).toBe(16777216);
|
|
expect(live.ghidraStartupTimeoutMs).toBe(330000);
|
|
expect(live.regexMatchingDeadline.deadlineMs).toBe(5000);
|
|
expect(store.read().revision).toBe(0);
|
|
});
|
|
try {
|
|
const changed = store.update({
|
|
changes: [
|
|
{ setting: "asar.max_header_bytes", action: "set", value: 33554432 },
|
|
{ setting: "ghidra.startup_timeout_ms", action: "set", value: 999999 },
|
|
{ setting: "regex.matching_deadline_ms", action: "set", value: 10000 },
|
|
],
|
|
});
|
|
expect(changed.ok).toBe(true);
|
|
} finally {
|
|
release();
|
|
await Promise.allSettled([pending]);
|
|
}
|
|
await pending;
|
|
store.run(() => {
|
|
expect(asar.maximumBytes).toBe(33554432);
|
|
expect(live.ghidraStartupTimeoutMs).toBe(999999);
|
|
expect(live.regexMatchingDeadline.deadlineMs).toBe(10000);
|
|
});
|
|
});
|
|
|
|
it("exposes shared provider controls and preserves the explicit unbounded decompile value", () => {
|
|
const environment = { REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS: "7" };
|
|
const store = new AnalysisSettings(environment);
|
|
const parsed = parseConfig(environment);
|
|
if (!parsed.ok) throw parsed.error;
|
|
const config = runtimeAnalysisConfig(parsed.value, store.read);
|
|
expect(
|
|
store
|
|
.inspect({ provider: "ghidra", operation: "search_strings" })
|
|
.settings.map(({ setting }) => setting),
|
|
).toEqual(["ghidra.startup_timeout_ms"]);
|
|
expect(
|
|
store
|
|
.inspect({ provider: "ida", operation: "search_strings" })
|
|
.settings.map(({ setting }) => setting),
|
|
).toEqual(["regex.matching_deadline_ms", "regex.worker_heap_request_mib"]);
|
|
expect(
|
|
store
|
|
.inspect({ operation: "inventory_artifact" })
|
|
.settings.map(({ setting }) => setting),
|
|
).toEqual(["asar.max_header_bytes"]);
|
|
expect(
|
|
store.update({
|
|
changes: [
|
|
{
|
|
setting: "ghidra.decompile_timeout_seconds",
|
|
action: "set",
|
|
value: null,
|
|
},
|
|
],
|
|
}).ok,
|
|
).toBe(true);
|
|
expect(config.ghidraDecompileTimeoutSeconds).toBeUndefined();
|
|
expect(
|
|
store.inspect({ settings: ["ghidra.decompile_timeout_seconds"] })
|
|
.settings[0],
|
|
).toMatchObject({ value: null, launch_value: 7, source: "runtime" });
|
|
for (const [setting, value] of [
|
|
["asar.max_header_bytes", null],
|
|
["asar.max_header_bytes", 268435457],
|
|
["javascript.max_heap_mb", 127],
|
|
["regex.matching_deadline_ms", 1.5],
|
|
]) {
|
|
expect(
|
|
store.update({ changes: [{ setting, action: "set", value }] }).ok,
|
|
).toBe(false);
|
|
}
|
|
expect(store.inspect().revision).toBe(1);
|
|
expect(
|
|
store.update({
|
|
changes: [
|
|
{ setting: "ghidra.decompile_timeout_seconds", action: "reset" },
|
|
],
|
|
}).ok,
|
|
).toBe(true);
|
|
expect(config.ghidraDecompileTimeoutSeconds).toBe(7);
|
|
});
|