Files
reverse-engineering-anything/src/artifacts/nodeSea/NodeSeaBlob.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

337 lines
10 KiB
TypeScript

import { setImmediate } from "node:timers/promises";
import { expect, it } from "vitest";
import { DEFAULT_NODE_SEA_READER_LIMITS } from "../../domain/artifactReaderLimits.js";
import {
NODE_SEA_MAGIC,
decodeNodeSeaBlob,
observeNodeSeaFuseMatches,
} from "./NodeSeaBlob.js";
import { buildNodeSeaBlob } from "./NodeSea.fixture.js";
const header = (flags: number, extension = 1): Buffer => {
const bytes = Buffer.alloc(9);
bytes.writeUInt32LE(NODE_SEA_MAGIC, 0);
bytes.writeUInt32LE(flags, 4);
bytes[8] = extension;
return bytes;
};
it("rejects a blob that ends before the magic", async () => {
await expect(
decodeNodeSeaBlob(Buffer.from([0x20, 0xda])),
).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/Node SEA.*truncated/iu),
});
});
it("rejects a blob whose magic is not 0x143da20", async () => {
await expect(decodeNodeSeaBlob(Buffer.alloc(9))).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/magic.*not.*0x143da20/iu),
});
});
it("rejects a header that ends before the extension byte", async () => {
await expect(
decodeNodeSeaBlob(header(0).subarray(0, 8)),
).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/Node SEA.*truncated/iu),
});
});
it("rejects unknown flag bits", async () => {
await expect(decodeNodeSeaBlob(header(1 << 5))).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/flags.*unknown bits/iu),
});
});
it("rejects an unknown exec argv extension", async () => {
await expect(decodeNodeSeaBlob(header(0, 9))).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/exec argv extension.*unknown/iu),
});
});
it("rejects an empty code path", async () => {
await expect(
decodeNodeSeaBlob(buildNodeSeaBlob({ codePath: "" })),
).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/code path.*empty/iu),
});
});
it("rejects a truncated length-prefixed field", async () => {
const bytes = Buffer.concat([header(0), Buffer.alloc(4)]);
await expect(decodeNodeSeaBlob(bytes)).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/Node SEA.*truncated/iu),
});
});
it("rejects colliding asset names", async () => {
await expect(
decodeNodeSeaBlob(
buildNodeSeaBlob({
assets: [
{ name: "shared", body: "one" },
{ name: "shared", body: "two" },
],
}),
),
).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/Node SEA asset name collides.*shared/iu),
});
});
it("retains an empty asset key as a raw Node SEA key", async () => {
const decoded = await decodeNodeSeaBlob(
buildNodeSeaBlob({ assets: [{ name: "", body: "value" }] }),
);
expect(decoded.assets).toMatchObject([{ name: "", range: { length: 5 } }]);
});
it.each([
{
field: "code path",
build: (value: string) => buildNodeSeaBlob({ codePath: value }),
read: (blob: Awaited<ReturnType<typeof decodeNodeSeaBlob>>) =>
blob.codePath,
},
{
field: "asset name",
build: (value: string) =>
buildNodeSeaBlob({ assets: [{ name: value, body: "" }] }),
read: (blob: Awaited<ReturnType<typeof decodeNodeSeaBlob>>) =>
blob.assets[0]?.name,
},
{
field: "exec argument",
build: (value: string) => buildNodeSeaBlob({ execArgv: [value] }),
read: (blob: Awaited<ReturnType<typeof decodeNodeSeaBlob>>) =>
blob.execArgv[0],
},
])(
"rejects invalid UTF-8 in $field while preserving a literal replacement character",
async ({ build, read }) => {
const valid = await decodeNodeSeaBlob(build("\ufffd"));
expect(read(valid)).toBe("\ufffd");
const malformed = build("fixture-marker");
const offset = malformed.indexOf("fixture-marker");
expect(offset).toBeGreaterThanOrEqual(0);
malformed[offset] = 0xff;
await expect(decodeNodeSeaBlob(malformed)).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/not valid UTF-8/iu),
});
},
);
it("rejects impossible asset rows before applying resource admission", async () => {
const prefix = buildNodeSeaBlob({ assets: [{ name: "a", body: "" }] });
const count = Buffer.alloc(8);
count.writeBigUInt64LE(2n);
const bytes = Buffer.concat([prefix.subarray(0, -25), count]);
await expect(
decodeNodeSeaBlob(bytes, undefined, {
...DEFAULT_NODE_SEA_READER_LIMITS,
asset_count: 1,
}),
).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/Node SEA.*truncated/iu),
readerLimit: undefined,
});
});
it.each([
{ field: "asset_count" as const, limit: 1, raised: 2 },
{ field: "asset_name_bytes" as const, limit: 2, raised: 3 },
{ field: "argument_count" as const, limit: 1, raised: 2 },
{ field: "argument_bytes" as const, limit: 2, raised: 3 },
])(
"recovers the same blob after raising $field",
async ({ field, limit, raised }) => {
const bytes = buildNodeSeaBlob({
assets: [
{ name: "a", body: "one" },
{ name: "bb", body: "two" },
],
execArgv: ["x", "yz"],
});
await expect(
decodeNodeSeaBlob(bytes, undefined, {
...DEFAULT_NODE_SEA_READER_LIMITS,
[field]: limit,
}),
).rejects.toMatchObject({
reason: "limit",
readerLimit: {
parameter: `artifact_limits.node_sea.${field}`,
effective_value: limit,
},
});
const recovered = await decodeNodeSeaBlob(bytes, undefined, {
...DEFAULT_NODE_SEA_READER_LIMITS,
[field]: raised,
});
expect(recovered.assets.map((asset) => asset.name)).toEqual(["a", "bb"]);
expect(recovered.execArgv).toEqual(["x", "yz"]);
},
);
it("applies blob byte admission even when called without a carrier reader", async () => {
const bytes = buildNodeSeaBlob({ main: "hello" });
await expect(
decodeNodeSeaBlob(bytes, undefined, {
...DEFAULT_NODE_SEA_READER_LIMITS,
blob_bytes: bytes.length - 1,
}),
).rejects.toMatchObject({
reason: "limit",
readerLimit: {
parameter: "artifact_limits.node_sea.blob_bytes",
required_value: bytes.length,
},
});
expect(
(
await decodeNodeSeaBlob(bytes, undefined, {
...DEFAULT_NODE_SEA_READER_LIMITS,
blob_bytes: bytes.length,
})
).mainScript?.length,
).toBe(5);
});
it("rejects non-zero trailing bytes and keeps zero padding", async () => {
const padded = buildNodeSeaBlob({ trailing: Buffer.alloc(8) });
expect((await decodeNodeSeaBlob(padded)).codePath).toBe("sea.js");
const dirty = Buffer.concat([padded, Buffer.from([1])]);
await expect(decodeNodeSeaBlob(dirty)).rejects.toMatchObject({
reason: "format",
message: expect.stringMatching(/trailing bytes/iu),
});
});
it("decodes flags, the main script, code cache, assets, and exec argv", async () => {
const decoded = await decodeNodeSeaBlob(
buildNodeSeaBlob({
codePath: "app/main.js",
main: "console.log(1)\n",
codeCache: Buffer.from([1, 2, 3]),
assets: [{ name: "pkg/data.txt", body: "asset" }],
execArgv: ["--no-warnings", ""],
disableExperimentalSeaWarning: true,
execArgvExtension: 2,
}),
);
expect(decoded).toMatchObject({
flags: (1 << 0) | (1 << 2) | (1 << 3) | (1 << 4),
disableExperimentalSeaWarning: true,
useSnapshot: false,
useCodeCache: true,
includeAssets: true,
includeExecArgv: true,
execArgvExtension: "cli",
codePath: "app/main.js",
execArgv: ["--no-warnings", ""],
assets: [{ name: "pkg/data.txt" }],
});
expect(decoded.mainScript?.length).toBe(
Buffer.byteLength("console.log(1)\n"),
);
expect(decoded.snapshot).toBeNull();
expect(decoded.codeCache?.length).toBe(3);
});
it("treats the body as a snapshot when that flag is set", async () => {
const decoded = await decodeNodeSeaBlob(
buildNodeSeaBlob({
codePath: "sea.js",
snapshot: Buffer.from("snap"),
}),
);
expect(decoded.useSnapshot).toBe(true);
expect(decoded.mainScript).toBeNull();
expect(decoded.snapshot?.length).toBe(4);
});
it("reports an absent, unset, set, or conflicting fuse sentinel", () => {
expect(observeNodeSeaFuseMatches([])).toMatchObject({
fuseState: "absent",
fuseSentinel: null,
fuseContradiction: expect.stringMatching(
/no NODE_SEA_FUSE sentinel.*observed/iu,
),
});
const unset = `NODE_SEA_FUSE_${"a".repeat(32)}:0`;
expect(observeNodeSeaFuseMatches([unset, unset])).toMatchObject({
fuseState: "unset",
fuseSentinel: unset,
fuseContradiction: expect.stringMatching(/NODE_SEA_FUSE sentinel.*unset/iu),
});
const set = `NODE_SEA_FUSE_${"b".repeat(32)}:1`;
expect(observeNodeSeaFuseMatches([set])).toMatchObject({
fuseState: "set",
fuseSentinel: set,
fuseContradiction: null,
});
expect(observeNodeSeaFuseMatches([unset, set])).toMatchObject({
fuseState: "conflicting",
fuseSentinel: null,
fuseContradiction: expect.stringMatching(/fuse sentinels disagree/iu),
});
});
it.each([
[
"assets",
() =>
buildNodeSeaBlob({
assets: Array.from({ length: 1024 }, (_, index) => ({
name: `asset-${String(index)}`,
body: "",
})),
}),
],
[
"exec arguments",
() =>
buildNodeSeaBlob({
execArgv: Array.from({ length: 1024 }, () => "--no-warnings"),
}),
],
[
"zero padding",
() => buildNodeSeaBlob({ trailing: Buffer.alloc(1024 * 1024) }),
],
] as const)("observes cancellation while decoding %s", async (_name, build) => {
const bytes = build();
const controller = new AbortController();
const reason = { stage: "decoding" };
const pending = decodeNodeSeaBlob(bytes, controller.signal);
// Let a batch run; cancellation must be observed before the whole table or
// padding scan finishes, without relying on elapsed-time assertions.
await setImmediate();
controller.abort(reason);
await expect(pending).rejects.toMatchObject({
reason: "cancelled",
cause: reason,
});
});
it("preserves cancellation before decoding even a malformed blob", async () => {
const reason = { stage: "before decoding" };
await expect(
decodeNodeSeaBlob(Buffer.alloc(0), AbortSignal.abort(reason)),
).rejects.toMatchObject({ reason: "cancelled", cause: reason });
});