Files
regex-tools/scripts/perl-engine-pack.test.ts

171 lines
5.5 KiB
TypeScript

// @vitest-environment node
import { createHash } from "node:crypto";
import {
copyFile,
cp,
mkdir,
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 {
buildPerlPack,
installPerlPack,
PERL_ENGINE_LOCK,
verifyPerlPack,
} from "./perl-engine-pack.mjs";
const temporaryDirectories: string[] = [];
const installedPack = path.resolve("public", "engines", "perl");
const packFiles = [
"LICENSE-Devel-StackTrace-Artistic-2.0.txt",
"LICENSE-Emscripten.txt",
"LICENSE-WebPerl-Artistic.txt",
"LICENSE-WebPerl-GPL.txt",
"NOTICE.txt",
"SHA256SUMS",
"emperl.data",
"emperl.js",
"emperl.wasm",
"engine-metadata.json",
"perl-runtime.worker.js",
"regex_tools_bridge.pl",
] as const;
afterEach(async () => {
await Promise.all(
temporaryDirectories
.splice(0)
.map((directory) => rm(directory, { recursive: true, force: true })),
);
});
async function digest(file: string): Promise<string> {
return createHash("sha256")
.update(await readFile(file))
.digest("hex");
}
async function makeExtractedPrebuilt(parent: string): Promise<string> {
const prebuilt = path.join(parent, "webperl_prebuilt_v0.09-beta");
await mkdir(prebuilt, { recursive: true });
for (const name of ["emperl.data", "emperl.js", "emperl.wasm"]) {
await copyFile(path.join(installedPack, name), path.join(prebuilt, name));
}
await copyFile(
path.join(installedPack, "LICENSE-WebPerl-Artistic.txt"),
path.join(prebuilt, "LICENSE_artistic.txt"),
);
await copyFile(
path.join(installedPack, "LICENSE-WebPerl-GPL.txt"),
path.join(prebuilt, "LICENSE_gpl.txt"),
);
return prebuilt;
}
describe("Perl engine-pack gate", () => {
it("verifies the installed closed WebPerl pack in a strict-CSP Chromium worker", async () => {
await expect(verifyPerlPack(installedPack)).resolves.toEqual(
expect.objectContaining({
engineVersion: "5.28.1",
status: "legacy-beta-compatibility-runtime",
source: expect.objectContaining({
tag: "v0.09-beta",
commitSha1: "6f2173d29a2c2e3536e1de75ff5d291ae96ab348",
}),
bridge: expect.objectContaining({
replacementOutputTransport: expect.stringContaining(
"fixed MEMFS binary file",
),
limits: expect.objectContaining({
maximumResponseJsonBytes: 8_388_608,
outputChunkCharacters: 4_096,
}),
}),
securityAudit: expect.objectContaining({
generatedLoaderJavaScriptEvalSites: 1,
generatedLoaderNewFunctionSites: 0,
}),
}),
);
}, 60_000);
it("pins the exact archive, runtime assets, and legacy identity", () => {
expect(PERL_ENGINE_LOCK.engineVersion).toBe("5.28.1");
expect(PERL_ENGINE_LOCK.webPerlVersion).toBe("0.09-beta");
expect(PERL_ENGINE_LOCK.prebuilt.archiveSha256).toBe(
"5f441249217e90ab378c666f473d4206ab4f44907f6bb0aa8d70834bc38c40dc",
);
expect(PERL_ENGINE_LOCK.prebuilt.files["emperl.wasm"]).toEqual(
expect.objectContaining({
bytes: 3_734_063,
sha256:
"f1d49c4514c7332a57992c4a2444fd6a56ae3b5e6651b4fd484852a641e5e4ec",
}),
);
});
it("builds byte-identical offline packs and installs one atomically", async () => {
const directory = await mkdtemp(path.join(tmpdir(), "regex-perl-pack-"));
temporaryDirectories.push(directory);
const prebuilt = await makeExtractedPrebuilt(directory);
const first = path.join(directory, "first");
const second = path.join(directory, "second");
await buildPerlPack({ prebuiltDirectory: prebuilt, output: first });
await buildPerlPack({ prebuiltDirectory: prebuilt, output: second });
for (const file of packFiles) {
expect(await digest(path.join(first, file)), file).toBe(
await digest(path.join(second, file)),
);
}
const fakeRoot = path.join(directory, "repository");
await mkdir(path.join(fakeRoot, "engines"), { recursive: true });
await cp(
path.resolve("engines", "perl"),
path.join(fakeRoot, "engines", "perl"),
{ recursive: true },
);
const oldTarget = path.join(fakeRoot, "public", "engines", "perl");
await mkdir(oldTarget, { recursive: true });
await writeFile(path.join(oldTarget, "old-marker"), "old\n", "utf8");
const installed = await installPerlPack(first, fakeRoot);
expect(installed.output).toBe(oldTarget);
await expect(
readFile(path.join(oldTarget, "old-marker")),
).rejects.toThrow();
await expect(
verifyPerlPack(oldTarget, fakeRoot, { smoke: false }),
).resolves.toMatchObject({ engineVersion: "5.28.1" });
}, 120_000);
it("rejects extra pack entries and drifted extracted inputs", async () => {
const directory = await mkdtemp(path.join(tmpdir(), "regex-perl-reject-"));
temporaryDirectories.push(directory);
const pack = path.join(directory, "pack");
await cp(installedPack, pack, { recursive: true });
await writeFile(path.join(pack, "unexpected"), "not allowed\n", "utf8");
await expect(
verifyPerlPack(pack, path.resolve("."), { smoke: false }),
).rejects.toThrow("must contain only");
const prebuilt = await makeExtractedPrebuilt(directory);
await writeFile(path.join(prebuilt, "emperl.wasm"), "not wasm\n");
await expect(
buildPerlPack({
prebuiltDirectory: prebuilt,
output: path.join(directory, "rejected"),
}),
).rejects.toThrow("pinned WebPerl release");
});
});