import { readFile } from "node:fs/promises"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { createDeterministicZip, parseReleaseArguments, safeArchivePath, } from "./package-release.mjs"; import { sha256 } from "./checksum-release.mjs"; describe("release packaging", () => { it("creates a deterministic archive independent of input order", () => { const entries = [ { name: "index.html", data: Buffer.from("

ok

") }, { name: "assets/app.js", data: Buffer.from("export {};") }, ]; expect(sha256(createDeterministicZip(entries))).toBe( sha256(createDeterministicZip([...entries].reverse())), ); }); it.each(["../secret", "/absolute", "C:/absolute", "a\\b", "a/./b", ""])( "rejects unsafe path %j", (candidate) => { expect(() => safeArchivePath(candidate)).toThrow(); }, ); it("rejects duplicate paths, source maps and credential-like entries", () => { expect(() => createDeterministicZip([ { name: "index.html", data: Buffer.from("one") }, { name: "index.html", data: Buffer.from("two") }, ]), ).toThrow("Duplicate release path"); expect(() => createDeterministicZip([ { name: "assets/app.js.map", data: Buffer.from("{}") }, ]), ).toThrow("Source map"); expect(() => createDeterministicZip([{ name: ".env", data: Buffer.from("TOKEN=x") }]), ).toThrow("Credential-like"); }); it("scans reStructuredText legal notices for local build paths", () => { expect(() => createDeterministicZip([ { name: "engines/python/NOTICE.cpython-bundled.rst", data: Buffer.from("Built from /home/example/private/source"), }, ]), ).toThrow("Local absolute path"); }); it.each([ ["assets/leak.js", Buffer.from('const source = "/mnt/private/app.ts";')], [ "engines/example/module.wasm", Buffer.from("/home/private/build/runtime.c"), ], [ "engines/example/module.pdb", Buffer.from("c:\\Users\\private\\source\\module.cs", "utf16le"), ], ])("scans JS, WebAssembly and PDB-like asset %s", (name, data) => { expect(() => createDeterministicZip([{ name, data }])).toThrow( "Local absolute path", ); }); it("allows only the byte-exact upstream Perl and Ruby path-bearing assets", async () => { for (const name of ["engines/perl/emperl.data", "engines/ruby/ruby.wasm"]) { const data = await readFile(path.resolve("public", name)); expect(() => createDeterministicZip([{ name, data }])).not.toThrow(); expect(() => createDeterministicZip([ { name, data: Buffer.concat([data, Buffer.from("changed")]) }, ]), ).toThrow("Local absolute path"); } }, 30_000); it("requires an explicit force flag for exact-version replacement", () => { expect(parseReleaseArguments([])).toEqual({ force: false }); expect(parseReleaseArguments(["--force"])).toEqual({ force: true }); expect(() => parseReleaseArguments(["--unknown"])).toThrow( "Unknown release argument", ); }); });