84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { instrumentJavaScriptExport } from "../src/domain/javascriptExportInstrumentation.js";
|
|
|
|
const encode = (value: string): Uint8Array => new TextEncoder().encode(value);
|
|
const sha256 = (value: Uint8Array): string =>
|
|
createHash("sha256").update(value).digest("hex");
|
|
|
|
describe("JavaScript export instrumentation", () => {
|
|
it.each([
|
|
[
|
|
"plain",
|
|
"const hidden = (value) => value.trim();",
|
|
"(value) => value.trim()",
|
|
],
|
|
["minified", "const x=a=>a+1,y=2;", "a=>a+1"],
|
|
[
|
|
"bundle",
|
|
"(()=>{const registry={7:(m)=>m.exports=(x)=>x*2};globalThis.r=registry})()",
|
|
"(x)=>x*2",
|
|
],
|
|
])(
|
|
"deterministically exposes one exact %s expression",
|
|
(_name, text, selectedText) => {
|
|
const source = encode(text);
|
|
const original = source.slice();
|
|
const start = text.indexOf(selectedText);
|
|
const selected = encode(selectedText);
|
|
const input = {
|
|
artifact_path: "/approved/app.js",
|
|
artifact_sha256: sha256(source),
|
|
selection: {
|
|
byte_start: start,
|
|
byte_end: start + selected.byteLength,
|
|
selected_sha256: sha256(selected),
|
|
export_name: "selected",
|
|
},
|
|
};
|
|
|
|
const first = instrumentJavaScriptExport(source, input);
|
|
const second = instrumentJavaScriptExport(source, input);
|
|
|
|
expect(first).toEqual(second);
|
|
expect(source).toEqual(original);
|
|
expect(first.manifest).toMatchObject({
|
|
original_sha256: sha256(source),
|
|
instrumented_sha256: sha256(first.bytes),
|
|
output_format: "commonjs-factory",
|
|
inserted_ranges: [
|
|
{ purpose: "factory-prefix" },
|
|
{ purpose: "export-binding" },
|
|
{ purpose: "factory-suffix" },
|
|
],
|
|
});
|
|
},
|
|
);
|
|
|
|
it("rejects stale artifacts and ambiguous caller-selected bytes", () => {
|
|
const source = encode("const hidden = () => 1;");
|
|
const input = {
|
|
artifact_path: "/approved/app.js",
|
|
artifact_sha256: "0".repeat(64),
|
|
selection: {
|
|
byte_start: 15,
|
|
byte_end: 22,
|
|
selected_sha256: "1".repeat(64),
|
|
export_name: "hidden",
|
|
},
|
|
};
|
|
|
|
expect(() => instrumentJavaScriptExport(source, input)).toThrow(
|
|
/artifact digest mismatch/u,
|
|
);
|
|
expect(() =>
|
|
instrumentJavaScriptExport(source, {
|
|
...input,
|
|
artifact_sha256: sha256(source),
|
|
}),
|
|
).toThrow(/selection digest mismatch/u);
|
|
});
|
|
});
|