feat: add multi-engine regex flavour support

This commit is contained in:
2026-07-27 11:43:51 +02:00
parent 7079cde15f
commit 7f3a91ad37
340 changed files with 643286 additions and 483 deletions

View 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,
);
});
});