22 lines
1.5 KiB
JavaScript
22 lines
1.5 KiB
JavaScript
#!/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.`);
|