feat: add multi-engine regex flavour support
This commit is contained in:
131
scripts/dotnet-engine-pack.test.ts
Normal file
131
scripts/dotnet-engine-pack.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { cp, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { writeEngineChecksums } from "./checksummed-engine-pack.mjs";
|
||||
import { DOTNET_LOCK, verifyDotNetPack } from "./dotnet-engine-pack.mjs";
|
||||
|
||||
const temporaryDirectories: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
temporaryDirectories
|
||||
.splice(0)
|
||||
.map((directory) => rm(directory, { recursive: true, force: true })),
|
||||
);
|
||||
});
|
||||
|
||||
describe(".NET engine-pack gate", () => {
|
||||
it("verifies the installed exact runtime, bridge sources and closed checksums", async () => {
|
||||
await expect(
|
||||
verifyDotNetPack(path.resolve("public", "engines", "dotnet")),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
engineVersion: ".NET 10.0.10 System.Text.RegularExpressions",
|
||||
source: expect.objectContaining({
|
||||
runtimeVersion: "10.0.10",
|
||||
runtimeCommit: "f7d90799ce",
|
||||
}),
|
||||
toolchain: expect.objectContaining({
|
||||
sdkVersion: "10.0.302",
|
||||
sdkCommit: "35b593bebf",
|
||||
workload: "wasm-tools",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("pins the compiler/runtime identity used by the checked-in pack", () => {
|
||||
expect(DOTNET_LOCK).toEqual(
|
||||
expect.objectContaining({
|
||||
sdkVersion: "10.0.302",
|
||||
runtimeVersion: "10.0.10",
|
||||
bridgeAbi: 1,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects modified framework assets", async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "regex-dotnet-pack-"));
|
||||
temporaryDirectories.push(directory);
|
||||
const pack = path.join(directory, "dotnet");
|
||||
await cp(path.resolve("public", "engines", "dotnet"), pack, {
|
||||
recursive: true,
|
||||
});
|
||||
const metadata = JSON.parse(
|
||||
await readFile(path.join(pack, "engine-metadata.json"), "utf8"),
|
||||
) as {
|
||||
files: readonly { path: string }[];
|
||||
};
|
||||
const runtime = metadata.files.find(({ path: name }) =>
|
||||
/^_framework\/System\.Text\.RegularExpressions\..+\.wasm$/u.test(name),
|
||||
);
|
||||
expect(runtime).toBeDefined();
|
||||
await writeFile(path.join(pack, runtime?.path ?? ""), "modified");
|
||||
|
||||
await expect(verifyDotNetPack(pack)).rejects.toThrow(/SHA-256 mismatch/u);
|
||||
});
|
||||
|
||||
it("rejects substituted notices even when self-reported checksums are regenerated", async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "regex-dotnet-pack-"));
|
||||
temporaryDirectories.push(directory);
|
||||
const pack = path.join(directory, "dotnet");
|
||||
await cp(path.resolve("public", "engines", "dotnet"), pack, {
|
||||
recursive: true,
|
||||
});
|
||||
const replacement = Buffer.from("substituted notices\n");
|
||||
await writeFile(path.join(pack, "THIRD-PARTY-NOTICES.txt"), replacement);
|
||||
const metadataFile = path.join(pack, "engine-metadata.json");
|
||||
const metadata = JSON.parse(await readFile(metadataFile, "utf8")) as {
|
||||
files: { path: string; sha256: string; bytes: number }[];
|
||||
};
|
||||
const record = metadata.files.find(
|
||||
({ path: name }) => name === "THIRD-PARTY-NOTICES.txt",
|
||||
);
|
||||
expect(record).toBeDefined();
|
||||
if (!record) return;
|
||||
record.sha256 = createHash("sha256").update(replacement).digest("hex");
|
||||
record.bytes = replacement.byteLength;
|
||||
await writeFile(metadataFile, `${JSON.stringify(metadata, null, 2)}\n`);
|
||||
await writeEngineChecksums(pack);
|
||||
|
||||
await expect(verifyDotNetPack(pack)).rejects.toThrow(
|
||||
/pinned runtime contract/u,
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects a build-host path hidden in WebAssembly after checksums are regenerated", async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "regex-dotnet-pack-"));
|
||||
temporaryDirectories.push(directory);
|
||||
const pack = path.join(directory, "dotnet");
|
||||
await cp(path.resolve("public", "engines", "dotnet"), pack, {
|
||||
recursive: true,
|
||||
});
|
||||
const metadataFile = path.join(pack, "engine-metadata.json");
|
||||
const metadata = JSON.parse(await readFile(metadataFile, "utf8")) as {
|
||||
files: { path: string; sha256: string; bytes: number }[];
|
||||
};
|
||||
const record = metadata.files.find(({ path: name }) =>
|
||||
/^_framework\/RegexTools\.DotNet\..+\.wasm$/u.test(name),
|
||||
);
|
||||
expect(record).toBeDefined();
|
||||
if (!record) return;
|
||||
const assetFile = path.join(pack, record.path);
|
||||
const replacement = Buffer.concat([
|
||||
await readFile(assetFile),
|
||||
Buffer.from("/mnt/private/regex-tools/obj/linked/RegexTools.DotNet.pdb"),
|
||||
]);
|
||||
await writeFile(assetFile, replacement);
|
||||
record.sha256 = createHash("sha256").update(replacement).digest("hex");
|
||||
record.bytes = replacement.byteLength;
|
||||
await writeFile(metadataFile, `${JSON.stringify(metadata, null, 2)}\n`);
|
||||
await writeEngineChecksums(pack);
|
||||
|
||||
await expect(verifyDotNetPack(pack)).rejects.toThrow(
|
||||
/Local absolute path/u,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user