mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 22:06:10 +00:00
* feat(artifacts): inventory and extract Bun standalone executable module graphs Adds a Bun standalone artifact reader for Mach-O, PE, ELF and appended trailers, supporting the 36-byte and 52-byte module record layouts. It preserves module names, entry-point details and raw metadata, and exposes contents, sourcemaps, bytecode and module-info as separate entries for safe extraction without executing the binary. Parsing rejects malformed or ambiguous graphs and enforces module-count, retained-byte and expansion limits. Adds synthetic parser tests, CLI and MCP coverage, agent skill guidance, and a verification script for Bun-produced executables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe * fix(artifacts): bound Bun section probes and reject truncated sections Split Bun recognition into a bounded probe that reads only section headers, length prefixes, and the Offsets/trailer suffix, and a single payload load used by the reader. Inventory classification and extraction admission no longer allocate the whole graph. Match ELF `.bun` section names by comparing only the name bytes and terminator, so many headers sharing one long name cost constant work each. Reject sections whose declared extent ends past EOF instead of reading a silently shortened window. Launch Bun, Docker, and REA in the standalone verifier through the shared owned verifier commands, and release the runtime root only after those command families have settled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe * style(artifacts): format Bun reader integration * fix(contracts): describe static Bun inventory facts * fix(artifacts): bound Bun metadata reads and combined path growth --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
200 lines
6.9 KiB
TypeScript
200 lines
6.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
ArtifactPathRegistry,
|
|
destinationCaseCollision,
|
|
DestinationCaseCollisionFailure,
|
|
annotateEnglishUnicodeCaseCollisions,
|
|
ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION,
|
|
normalizeArtifactPath,
|
|
} from "./ArtifactPaths.js";
|
|
|
|
const NOTE =
|
|
"a case-insensitive destination cannot store both. This comparison is not a filesystem observation.";
|
|
|
|
it.each(["drive", "escaped", "normalization", "prefix", "collision"])(
|
|
"bounds path failure diagnostics at the owning boundary: %s",
|
|
(kind) => {
|
|
const name = "x".repeat(1024 * 1024);
|
|
const registry = new ArtifactPathRegistry();
|
|
const fail = () => {
|
|
if (kind === "drive") normalizeArtifactPath("C:/" + name);
|
|
else if (kind === "escaped")
|
|
normalizeArtifactPath("\0".repeat(name.length));
|
|
else if (kind === "normalization")
|
|
normalizeArtifactPath(name + "/../child");
|
|
else {
|
|
registry.add(name, "file");
|
|
registry.add(kind === "prefix" ? name + "/child" : name, "directory");
|
|
}
|
|
};
|
|
const failure = (() => {
|
|
try {
|
|
fail();
|
|
} catch (cause: unknown) {
|
|
return cause;
|
|
}
|
|
return undefined;
|
|
})();
|
|
expect(failure).toMatchObject({ reason: "path" });
|
|
expect((failure as Error).message.length).toBeLessThan(1024);
|
|
},
|
|
);
|
|
|
|
it.each(["a:b", "C:cache/file.txt", "a:"])(
|
|
"preserves a literal POSIX colon name %s",
|
|
(path) => {
|
|
expect(normalizeArtifactPath(path)).toBe(path);
|
|
},
|
|
);
|
|
|
|
const annotate = (paths: readonly string[]) => {
|
|
const occurrences = paths.map((logical_path) => ({
|
|
entry_kind: "file" as const,
|
|
logical_path,
|
|
limitations: [] as string[],
|
|
}));
|
|
const limitation = annotateEnglishUnicodeCaseCollisions(occurrences);
|
|
return {
|
|
limitation,
|
|
byPath: new Map(
|
|
occurrences.map(({ logical_path, limitations }) => [
|
|
logical_path,
|
|
limitations,
|
|
]),
|
|
),
|
|
};
|
|
};
|
|
|
|
describe("English Unicode case collisions", () => {
|
|
it("retains repeated same-kind entries while rejecting file-directory aliasing", () => {
|
|
const registry = new ArtifactPathRegistry();
|
|
registry.add("duplicate", "file");
|
|
expect(() => registry.add("duplicate", "file")).not.toThrow();
|
|
expect(() => registry.add("duplicate", "directory")).toThrow(
|
|
"Artifact path collision",
|
|
);
|
|
});
|
|
|
|
it("names the other spelling of a colliding file", () => {
|
|
const { limitation, byPath } = annotate([".", "Main.js", "main.js", "x"]);
|
|
expect(limitation).toBe(ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION);
|
|
expect(byPath.get("Main.js")).toEqual([
|
|
`Logical path Main.js collides under English (en-US) Unicode case folding with main.js; ${NOTE}`,
|
|
]);
|
|
expect(byPath.get("main.js")).toEqual([
|
|
`Logical path main.js collides under English (en-US) Unicode case folding with Main.js; ${NOTE}`,
|
|
]);
|
|
expect(byPath.get("x")).toEqual([]);
|
|
});
|
|
|
|
it("records a directory collision once at the diverging segment", () => {
|
|
const { byPath } = annotate([
|
|
"res/Foo",
|
|
"res/Foo/a.txt",
|
|
"res/foo",
|
|
"res/foo/b.txt",
|
|
]);
|
|
expect(byPath.get("res/Foo")).toEqual([
|
|
`Logical path res/Foo collides under English (en-US) Unicode case folding with res/foo; ${NOTE}`,
|
|
]);
|
|
expect(byPath.get("res/foo/b.txt")).toEqual([
|
|
`Logical path res/foo/b.txt is under res/foo, which collides under English (en-US) Unicode case folding with res/Foo; ${NOTE}`,
|
|
]);
|
|
});
|
|
|
|
it("counts further spellings, each of which is its own occurrence", () => {
|
|
const { byPath } = annotate(["A.txt", "a.txt", "a.TXT"]);
|
|
expect(byPath.get("a.txt")).toEqual([
|
|
`Logical path a.txt collides under English (en-US) Unicode case folding with A.txt and 1 other spelling; ${NOTE}`,
|
|
]);
|
|
});
|
|
|
|
it("retains every spelling in a high-cardinality folded sibling group", () => {
|
|
const count = 8_192;
|
|
const variants = caseVariants("abcdefghijklm", count);
|
|
const { limitation, byPath } = annotate(variants);
|
|
const ordered = [...variants].sort();
|
|
const first = ordered[0];
|
|
const second = ordered[1];
|
|
const original = variants[0];
|
|
if (first === undefined || second === undefined || original === undefined) {
|
|
throw new Error("Expected at least two case variants");
|
|
}
|
|
expect(limitation).toBe(ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION);
|
|
expect(byPath.size).toBe(count);
|
|
expect(byPath.get(first)).toEqual([
|
|
`Logical path ${first} collides under English (en-US) Unicode case folding with ${second} and ${String(count - 2)} other spellings; ${NOTE}`,
|
|
]);
|
|
expect(byPath.get(original)?.[0]).toContain(
|
|
`with ${first} and ${String(count - 2)} other spellings;`,
|
|
);
|
|
expect(
|
|
[...byPath.values()].every((limitations) => limitations.length === 1),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("stays linear for large case-variant subtrees", () => {
|
|
const count = 5_000;
|
|
const paths = Array.from({ length: count }, (_, index) => [
|
|
`Foo/a${String(index)}.txt`,
|
|
`foo/b${String(index)}.txt`,
|
|
]).flat();
|
|
const { byPath } = annotate(paths);
|
|
for (const path of paths) {
|
|
const limitations = byPath.get(path) ?? [];
|
|
expect(limitations).toHaveLength(1);
|
|
expect(limitations[0]?.length).toBeLessThan(300);
|
|
}
|
|
});
|
|
|
|
it("walks names with more components than the call stack holds", () => {
|
|
const deep = Array.from({ length: 30_000 }, () => "a").join("/");
|
|
const { byPath } = annotate([deep, `${deep}/X`, `${deep}/x`]);
|
|
expect(byPath.get(`${deep}/X`)).toHaveLength(1);
|
|
expect(byPath.get(deep)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
const caseVariants = (lowercase: string, count: number): string[] =>
|
|
Array.from({ length: count }, (_, mask) =>
|
|
[...lowercase]
|
|
.map((character, index) =>
|
|
(mask & (1 << index)) === 0 ? character : character.toUpperCase(),
|
|
)
|
|
.join(""),
|
|
);
|
|
|
|
it("retains both source spellings and the native failure in a destination collision", () => {
|
|
const cause = Object.assign(new Error("exclusive create refused"), {
|
|
code: "EEXIST",
|
|
});
|
|
const collision = destinationCaseCollision("res/café.js", ["CAFÉ.js"], {
|
|
cause,
|
|
});
|
|
expect(collision).toBeInstanceOf(DestinationCaseCollisionFailure);
|
|
expect(collision).toMatchObject({
|
|
reason: "path",
|
|
requestedPath: "res/café.js",
|
|
existingPath: "res/CAFÉ.js",
|
|
});
|
|
expect(collision?.cause).toBe(cause);
|
|
});
|
|
|
|
it.each([["app.js"], ["app.js", "App.js"], ["other.js"]])(
|
|
"does not invent a destination alias from listing %j",
|
|
(...names) => {
|
|
expect(destinationCaseCollision("res/app.js", names)).toBeUndefined();
|
|
},
|
|
);
|
|
|
|
it("reserves prefix capacity atomically without declaring destination kinds", () => {
|
|
const registry = new ArtifactPathRegistry({ maxParts: 4, maxNodes: 3 });
|
|
registry.reservePath("a/b");
|
|
expect(() => registry.reservePath("a/c/d")).toThrow();
|
|
expect(() => registry.reservePath("z")).not.toThrow();
|
|
expect(() => registry.reservePath("a/b")).not.toThrow();
|
|
registry.add("a/b", "file");
|
|
expect(() => registry.add("a/b", "directory")).toThrow();
|
|
});
|