mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
fix: retain invalid runtime seeds for long-lived providers
This commit is contained in:
1 parent
64d1a57aef
commit
ef754ce717
3 files changed
+221
-70
No files matched your search
@@ -14,6 +14,7 @@ import { runWithAnalysisOperationSnapshot } from "../domain/analysisOperationSna
|
||||
import {
|
||||
runtimeAnalysisConfig,
|
||||
runtimeAsarHeaderAdmission,
|
||||
runtimeCaptureDecoderHeap,
|
||||
} from "../config/runtimeAnalysisSettings.js";
|
||||
import { parseConfig } from "../config/parseConfig.js";
|
||||
|
||||
@@ -391,3 +392,75 @@ it("keeps the Hopper startup deadline source through a runtime override", () =>
|
||||
expect(live.hopperStartupTimeoutSource).toBe("runtime");
|
||||
});
|
||||
});
|
||||
|
||||
it("retains invalid ASAR seeds across long-lived views and override resets", async () => {
|
||||
const environment = {
|
||||
REA_ASAR_MAX_HEADER_BYTES: "invalid",
|
||||
REA_ASAR_MAX_LISTING_BYTES: "invalid",
|
||||
};
|
||||
const store = new AnalysisSettings(environment);
|
||||
const admission = runtimeAsarHeaderAdmission(environment, store.read);
|
||||
expect(() => admission.maximumBytes).toThrow("REA_ASAR_MAX_HEADER_BYTES");
|
||||
expect(() => admission.listing?.source).toThrow("REA_ASAR_MAX_LISTING_BYTES");
|
||||
store.update({
|
||||
changes: [
|
||||
{ setting: "asar.max_header_bytes", action: "set", value: 8192 },
|
||||
{ setting: "asar.max_listing_bytes", action: "set", value: 4096 },
|
||||
],
|
||||
});
|
||||
let release!: () => void;
|
||||
const barrier = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
const accepted = store.run(async () => {
|
||||
await barrier;
|
||||
expect(admission).toMatchObject({
|
||||
maximumBytes: 8192,
|
||||
source: "runtime",
|
||||
listing: { maximumBytes: 4096, source: "runtime" },
|
||||
});
|
||||
});
|
||||
store.update({
|
||||
changes: [
|
||||
{ setting: "asar.max_header_bytes", action: "reset" },
|
||||
{ setting: "asar.max_listing_bytes", action: "reset" },
|
||||
],
|
||||
});
|
||||
release();
|
||||
await accepted;
|
||||
for (const read of [() => admission.maximumBytes, () => admission.source])
|
||||
expect(read).toThrow("REA_ASAR_MAX_HEADER_BYTES");
|
||||
for (const read of [
|
||||
() => admission.listing?.maximumBytes,
|
||||
() => admission.listing?.source,
|
||||
])
|
||||
expect(read).toThrow("REA_ASAR_MAX_LISTING_BYTES");
|
||||
});
|
||||
|
||||
it("retains an invalid capture heap seed through override and reset", () => {
|
||||
const environment = { REA_CAPTURE_DECODER_OLD_GENERATION_MIB: "invalid" };
|
||||
const store = new AnalysisSettings(environment);
|
||||
const heap = runtimeCaptureDecoderHeap(environment, store.read);
|
||||
expect(() => heap.oldGenerationMiB).toThrow(
|
||||
"REA_CAPTURE_DECODER_OLD_GENERATION_MIB",
|
||||
);
|
||||
store.update({
|
||||
changes: [
|
||||
{
|
||||
setting: "capture.decoder_old_generation_mib",
|
||||
action: "set",
|
||||
value: 64,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(heap).toMatchObject({ oldGenerationMiB: 64, source: "runtime" });
|
||||
store.update({
|
||||
changes: [
|
||||
{ setting: "capture.decoder_old_generation_mib", action: "reset" },
|
||||
],
|
||||
});
|
||||
expect(() => heap.oldGenerationMiB).toThrow(
|
||||
"REA_CAPTURE_DECODER_OLD_GENERATION_MIB",
|
||||
);
|
||||
expect(() => heap.source).toThrow("REA_CAPTURE_DECODER_OLD_GENERATION_MIB");
|
||||
});
|
||||
@@ -11,17 +11,13 @@ import {
|
||||
DEFAULT_GHIDRA_VALUE_FLOW_MAX_INPUTS_PER_OPERATION,
|
||||
DEFAULT_GHIDRA_VALUE_FLOW_MAX_EDGES,
|
||||
} from "../domain/analysisResourceLimits.js";
|
||||
import { parseAsarListingAdmission } from "./asarListingAdmission.js";
|
||||
import {
|
||||
parseAsarListingAdmission,
|
||||
DEFAULT_ASAR_LISTING_ADMISSION,
|
||||
} from "./asarListingAdmission.js";
|
||||
import {
|
||||
DEFAULT_ASAR_HEADER_ADMISSION,
|
||||
parseAsarHeaderAdmission,
|
||||
type AsarHeaderAdmission,
|
||||
} from "./asarHeaderAdmission.js";
|
||||
import {
|
||||
resolveCaptureDecoderHeap,
|
||||
parseCaptureDecoderHeap,
|
||||
type CaptureDecoderHeap,
|
||||
} from "./captureDecoderHeap.js";
|
||||
import {
|
||||
@@ -172,78 +168,70 @@ export const runtimeAsarHeaderAdmission = (
|
||||
const parsed = parseAsarHeaderAdmission(
|
||||
environment.REA_ASAR_MAX_HEADER_BYTES,
|
||||
);
|
||||
const headerOverride =
|
||||
read?.().overrides["asar.max_header_bytes"] ??
|
||||
currentAnalysisSettingOverride("asar.max_header_bytes");
|
||||
if (!parsed.ok && headerOverride === undefined) throw parsed.error;
|
||||
const listing = parseAsarListingAdmission(
|
||||
environment.REA_ASAR_MAX_LISTING_BYTES,
|
||||
);
|
||||
const listingOverride =
|
||||
read?.().overrides["asar.max_listing_bytes"] ??
|
||||
currentAnalysisSettingOverride("asar.max_listing_bytes");
|
||||
if (!listing.ok && listingOverride === undefined) throw listing.error;
|
||||
const baseline = parsed.ok ? parsed.value : DEFAULT_ASAR_HEADER_ADMISSION;
|
||||
const listingBaseline = listing.ok
|
||||
? listing.value
|
||||
: DEFAULT_ASAR_LISTING_ADMISSION;
|
||||
return read === undefined
|
||||
? {
|
||||
maximumBytes: headerOverride ?? baseline.maximumBytes,
|
||||
source: headerOverride === undefined ? baseline.source : "runtime",
|
||||
listing: {
|
||||
maximumBytes: listingOverride ?? listingBaseline.maximumBytes,
|
||||
source:
|
||||
listingOverride === undefined ? listingBaseline.source : "runtime",
|
||||
},
|
||||
}
|
||||
: {
|
||||
listing: {
|
||||
get maximumBytes() {
|
||||
return (
|
||||
read().overrides["asar.max_listing_bytes"] ??
|
||||
listingBaseline.maximumBytes
|
||||
);
|
||||
},
|
||||
get source() {
|
||||
return read().overrides["asar.max_listing_bytes"] === undefined
|
||||
? listingBaseline.source
|
||||
: "runtime";
|
||||
},
|
||||
},
|
||||
get maximumBytes() {
|
||||
return (
|
||||
read().overrides["asar.max_header_bytes"] ?? baseline.maximumBytes
|
||||
);
|
||||
},
|
||||
get source() {
|
||||
return read().overrides["asar.max_header_bytes"] === undefined
|
||||
? baseline.source
|
||||
: "runtime";
|
||||
},
|
||||
};
|
||||
const header = () => {
|
||||
const override =
|
||||
read?.().overrides["asar.max_header_bytes"] ??
|
||||
currentAnalysisSettingOverride("asar.max_header_bytes");
|
||||
if (override !== undefined)
|
||||
return { maximumBytes: override, source: "runtime" as const };
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
return parsed.value;
|
||||
};
|
||||
const expandedListing = () => {
|
||||
const override =
|
||||
read?.().overrides["asar.max_listing_bytes"] ??
|
||||
currentAnalysisSettingOverride("asar.max_listing_bytes");
|
||||
if (override !== undefined)
|
||||
return { maximumBytes: override, source: "runtime" as const };
|
||||
if (!listing.ok) throw listing.error;
|
||||
return listing.value;
|
||||
};
|
||||
if (read === undefined) return { ...header(), listing: expandedListing() };
|
||||
return {
|
||||
get maximumBytes() {
|
||||
return header().maximumBytes;
|
||||
},
|
||||
get source() {
|
||||
return header().source;
|
||||
},
|
||||
listing: {
|
||||
get maximumBytes() {
|
||||
return expandedListing().maximumBytes;
|
||||
},
|
||||
get source() {
|
||||
return expandedListing().source;
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
export const runtimeCaptureDecoderHeap = (
|
||||
environment: Readonly<Record<string, string | undefined>>,
|
||||
read?: AnalysisSettingsReader,
|
||||
): CaptureDecoderHeap => {
|
||||
const baseline = resolveCaptureDecoderHeap(environment);
|
||||
return read === undefined
|
||||
? baseline
|
||||
: {
|
||||
get oldGenerationMiB() {
|
||||
return (
|
||||
read().overrides["capture.decoder_old_generation_mib"] ??
|
||||
baseline.oldGenerationMiB
|
||||
);
|
||||
},
|
||||
get source() {
|
||||
return read().overrides["capture.decoder_old_generation_mib"] ===
|
||||
undefined
|
||||
? baseline.source
|
||||
: "runtime";
|
||||
},
|
||||
};
|
||||
const parsed = parseCaptureDecoderHeap(
|
||||
environment.REA_CAPTURE_DECODER_OLD_GENERATION_MIB,
|
||||
);
|
||||
const effective = () => {
|
||||
const override =
|
||||
read?.().overrides["capture.decoder_old_generation_mib"] ??
|
||||
currentAnalysisSettingOverride("capture.decoder_old_generation_mib");
|
||||
if (override !== undefined)
|
||||
return { oldGenerationMiB: override, source: "runtime" as const };
|
||||
if (!parsed.ok) throw parsed.error;
|
||||
return parsed.value;
|
||||
};
|
||||
if (read === undefined) return effective();
|
||||
return {
|
||||
get oldGenerationMiB() {
|
||||
return effective().oldGenerationMiB;
|
||||
},
|
||||
get source() {
|
||||
return effective().source;
|
||||
},
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Resolve the accepted operation's allowance on each call. An invalid launch
|
||||
|
||||
@@ -428,3 +428,93 @@ it("lowers, restores and seeds the native matching allowance over MCP", async ()
|
||||
await invalid.client.close();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
it("starts with invalid runtime seeds and recovers ASAR and capture work on the same connection", async () => {
|
||||
const root = await createTestTempDirectory("rea-invalid-runtime-seeds-");
|
||||
const archive = join(root, "small.asar");
|
||||
const capture = join(root, "capture.har");
|
||||
const payload = Buffer.from("answer");
|
||||
await writeFile(
|
||||
archive,
|
||||
Buffer.concat([
|
||||
asarHeaderBytes(
|
||||
JSON.stringify({
|
||||
files: { "answer.txt": { size: payload.length, offset: "0" } },
|
||||
}),
|
||||
),
|
||||
payload,
|
||||
]),
|
||||
);
|
||||
await writeFile(
|
||||
capture,
|
||||
JSON.stringify({
|
||||
log: {
|
||||
version: "1.2",
|
||||
creator: { name: "test", version: "1" },
|
||||
entries: [],
|
||||
},
|
||||
}),
|
||||
);
|
||||
const { client, call } = await connectLocalToolsMcp({
|
||||
REA_ASAR_MAX_HEADER_BYTES: "invalid",
|
||||
REA_ASAR_MAX_LISTING_BYTES: "invalid",
|
||||
REA_CAPTURE_DECODER_OLD_GENERATION_MIB: "invalid",
|
||||
});
|
||||
try {
|
||||
const changes = [
|
||||
{ setting: "asar.max_header_bytes", action: "set", value: 8192 },
|
||||
{ setting: "asar.max_listing_bytes", action: "set", value: 4096 },
|
||||
{
|
||||
setting: "capture.decoder_old_generation_mib",
|
||||
action: "set",
|
||||
value: 64,
|
||||
},
|
||||
];
|
||||
expect(
|
||||
(await call("update_analysis_settings", { changes })).isError,
|
||||
).not.toBe(true);
|
||||
expect((await call("open_binary", { path: archive })).isError).not.toBe(
|
||||
true,
|
||||
);
|
||||
expect((await call("inspect_artifact", {})).isError).not.toBe(true);
|
||||
const inspected = await call("inspect_web_network_capture", {
|
||||
capture_path: capture,
|
||||
format: "har",
|
||||
});
|
||||
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
|
||||
expect(
|
||||
(
|
||||
await call("update_analysis_settings", {
|
||||
changes: changes.map(({ setting }) => ({ setting, action: "reset" })),
|
||||
})
|
||||
).isError,
|
||||
).not.toBe(true);
|
||||
const refusedArchive = await call("inspect_artifact", {});
|
||||
expect(refusedArchive.isError).toBe(true);
|
||||
expect(JSON.stringify(refusedArchive)).toContain(
|
||||
"REA_ASAR_MAX_HEADER_BYTES",
|
||||
);
|
||||
const refused = await call("inspect_web_network_capture", {
|
||||
capture_path: capture,
|
||||
format: "har",
|
||||
});
|
||||
expect(refused.isError).toBe(true);
|
||||
expect(JSON.stringify(refused)).toContain(
|
||||
"REA_CAPTURE_DECODER_OLD_GENERATION_MIB",
|
||||
);
|
||||
expect(
|
||||
(await call("update_analysis_settings", { changes })).isError,
|
||||
).not.toBe(true);
|
||||
expect((await call("inspect_artifact", {})).isError).not.toBe(true);
|
||||
expect(
|
||||
(
|
||||
await call("inspect_web_network_capture", {
|
||||
capture_path: capture,
|
||||
format: "har",
|
||||
})
|
||||
).isError,
|
||||
).not.toBe(true);
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
}, 30000);
|
||||
Reference in new issue
Block a user