#!/usr/bin/env node import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import { resolve } from "node:path"; import process from "node:process"; const root = resolve(import.meta.dirname, ".."); const outputFlag = process.argv.indexOf("--output"); const output = resolve(root, outputFlag >= 0 ? process.argv[outputFlag + 1] : "release-artifacts"); const sbomText = await readFile(resolve(output, "projectbrain.cdx.json"), "utf8"); const provenance = JSON.parse(await readFile(resolve(output, "projectbrain.provenance.json"), "utf8")); const sbom = JSON.parse(sbomText); const canonicalSbom = `${JSON.stringify(sbom, null, 2)}\n`; const digest = createHash("sha256").update(JSON.stringify(sbom)).digest("hex"); const failures = []; if (sbom.bomFormat !== "CycloneDX" || sbom.specVersion !== "1.6" || !Array.isArray(sbom.components) || sbom.components.length === 0) failures.push("SBOM is not a populated CycloneDX 1.6 document"); if (sbomText !== canonicalSbom) failures.push("SBOM is not canonical pretty-printed JSON"); if (provenance.predicateType !== "https://slsa.dev/provenance/v1" || !provenance.subject?.length) failures.push("Provenance has no SLSA v1 subjects"); if (provenance.sbom?.sha256 !== digest) failures.push("Provenance SBOM digest does not match"); if (failures.length) { console.error(failures.join("\n")); process.exit(1); } console.log(`Release metadata verified: ${sbom.components.length} components and ${provenance.subject.length} subjects.`);