import { mkdtemp, readFile, unlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; import { byteDigest, canonicalValueBytes, } from "../../src/security/canonical-digest.ts"; import { buildReleasePackage, buildReleasePackageForTest, } from "../../scripts/build-release.mjs"; import { parseDeterministicTar } from "../../scripts/deploy/lib/release-common.mjs"; import { createReleaseFixture } from "./release-test-fixture.ts"; function buildInput( fixture: Awaited>, outputPath: string, ) { return { distDir: fixture.distDir, signedManifestPath: fixture.signedManifestPath, sbomPath: fixture.sbomPath, trustPolicyPath: fixture.trustPolicyPath, verifySignedReleaseEnvelope: fixture.verifySignedReleaseEnvelope, protectedState: fixture.protectedState, now: "2026-09-02T01:00:00Z", outputPath, }; } describe("deterministic exact-manifest release package", () => { test("builds byte-identical archives containing only the signed closure and release metadata", async () => { const root = await mkdtemp(join(tmpdir(), "portfolio-package-")); const fixture = await createReleaseFixture(root); const first = join(root, "release-one.tar"); const second = join(root, "release-two.tar"); await buildReleasePackageForTest(buildInput(fixture, first)); await buildReleasePackageForTest(buildInput(fixture, second)); expect(byteDigest(await readFile(first))).toBe( byteDigest(await readFile(second)), ); expect( parseDeterministicTar(await readFile(first)).map((file) => file.path), ).toEqual([ ".release/package-index.json", ".release/sbom.cdx.json", ".release/signed-release-manifest.json", "assets/app.js", "index.html", ]); }); test("fails closed on extra, substituted, or invalidly signed inputs", async () => { const root = await mkdtemp(join(tmpdir(), "portfolio-package-invalid-")); const fixture = await createReleaseFixture(root); await writeFile(join(fixture.distDir, "extra.txt"), "not in manifest"); await expect( buildReleasePackageForTest(buildInput(fixture, join(root, "extra.tar"))), ).rejects.toThrow("exactly match"); await unlink(join(fixture.distDir, "extra.txt")); await writeFile(join(fixture.distDir, "index.html"), "substituted"); await expect( buildReleasePackageForTest( buildInput(fixture, join(root, "substituted.tar")), ), ).rejects.toThrow("hash"); await writeFile( join(fixture.distDir, "index.html"), "Portfolio\n", ); const envelope = JSON.parse( await readFile(fixture.signedManifestPath, "utf8"), ); envelope.signature.value = `${envelope.signature.value[0] === "A" ? "B" : "A"}${envelope.signature.value.slice(1)}`; await writeFile(fixture.signedManifestPath, canonicalValueBytes(envelope)); await expect( buildReleasePackageForTest( buildInput(fixture, join(root, "invalid.tar")), ), ).rejects.toThrow("signature"); }); test("production package requests reject caller-selected verifier, policy, and clock fields", async () => { const root = await mkdtemp(join(tmpdir(), "portfolio-package-authority-")); const fixture = await createReleaseFixture(root); await expect( buildReleasePackage( buildInput(fixture, join(root, "release.tar")) as any, ), ).rejects.toThrow("rejects caller"); }); });