import { verify, type KeyObject } from "node:crypto"; import { z } from "zod"; import { canonicalizeSupplyChainValue } from "./supply-chain.ts"; const sha256 = z.string().regex(/^[a-f0-9]{64}$/u); const nonEmptyString = z.string().trim().min(1); const signatureSchema = z .object({ algorithm: z.literal("Ed25519"), keyId: nonEmptyString, value: z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/u), }) .strict(); export const vulnerabilityProviderReportSchema = z .object({ schemaVersion: z.literal(1), provider: nonEmptyString, generatedAt: z.iso.datetime(), scannedLockfileSha256: sha256, scannedDistSha256: sha256, findings: z.array(z.record(z.string(), z.json())), signature: signatureSchema, }) .strict(); export const provenanceProviderAttestationSchema = z .object({ schemaVersion: z.literal(1), provider: nonEmptyString, signer: nonEmptyString, generatedAt: z.iso.datetime(), subject: z .object({ name: z.literal("dist"), digest: z.object({ sha256 }).strict(), }) .strict(), signature: signatureSchema, }) .strict(); export const providerVerificationArtifactSchema = z .object({ schemaVersion: z.literal(1), status: z.enum(["PASS", "FAIL_UNVERIFIED"]), vulnerabilityStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]), provenanceAttestationStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]), lockfileSha256: sha256, distSha256: sha256, failures: z.array(z.string()), }) .strict(); export type ProviderTrust = Readonly<{ keyId: string; publicKey: KeyObject; }>; export type PromotionEvidenceResult = Readonly<{ status: "PASS" | "FAIL_UNVERIFIED"; vulnerabilityStatus: "PASS" | "FAIL_UNVERIFIED"; provenanceAttestationStatus: "PASS" | "FAIL_UNVERIFIED"; failures: readonly string[]; }>; export function providerEvidenceSignaturePayload(value: unknown): Buffer { if (!isRecord(value)) return Buffer.from("null", "utf8"); const { signature: _signature, ...payload } = value; return Buffer.from( JSON.stringify(canonicalizeSupplyChainValue(payload)), "utf8", ); } export function evaluatePromotionEvidence(input: Readonly<{ candidate: Readonly<{ distSha256: string; lockfileSha256: string }>; currentDistSha256: string; localStatus: unknown; vulnerabilityReport: unknown; provenanceAttestation: unknown; vulnerabilityTrust: ProviderTrust | null; provenanceTrust: ProviderTrust | null; }>): PromotionEvidenceResult { const failures: string[] = []; let vulnerabilityStatus: "PASS" | "FAIL_UNVERIFIED" = "FAIL_UNVERIFIED"; let provenanceAttestationStatus: "PASS" | "FAIL_UNVERIFIED" = "FAIL_UNVERIFIED"; if (input.localStatus !== "PASS") { failures.push("local supply-chain evidence is not PASS"); } if (input.currentDistSha256 !== input.candidate.distSha256) { failures.push("candidate dist bytes changed after immutable build"); } const vulnerability = vulnerabilityProviderReportSchema.safeParse( input.vulnerabilityReport, ); if (!vulnerability.success) { failures.push("external vulnerability provider report is missing or invalid"); } else { if ( vulnerability.data.scannedLockfileSha256 !== input.candidate.lockfileSha256 ) { failures.push("vulnerability report lockfile digest mismatch"); } if ( vulnerability.data.scannedDistSha256 !== input.candidate.distSha256 ) { failures.push("vulnerability report dist digest mismatch"); } if (vulnerability.data.findings.length > 0) { failures.push("vulnerability report contains findings"); } const signaturePassed = signatureMatches( vulnerability.data, input.vulnerabilityTrust, ); if (!signaturePassed) { failures.push("vulnerability report signature verification failed"); } if ( vulnerability.data.scannedLockfileSha256 === input.candidate.lockfileSha256 && vulnerability.data.scannedDistSha256 === input.candidate.distSha256 && vulnerability.data.findings.length === 0 && input.currentDistSha256 === input.candidate.distSha256 && input.localStatus === "PASS" && signaturePassed ) { vulnerabilityStatus = "PASS"; } } const provenance = provenanceProviderAttestationSchema.safeParse( input.provenanceAttestation, ); if (!provenance.success) { failures.push("external signed provenance attestation is missing or invalid"); } else { if (provenance.data.subject.digest.sha256 !== input.candidate.distSha256) { failures.push("provenance attestation dist digest mismatch"); } const signaturePassed = signatureMatches( provenance.data, input.provenanceTrust, ); if (!signaturePassed) { failures.push("provenance attestation signature verification failed"); } if ( provenance.data.subject.digest.sha256 === input.candidate.distSha256 && input.currentDistSha256 === input.candidate.distSha256 && input.localStatus === "PASS" && signaturePassed ) { provenanceAttestationStatus = "PASS"; } } return Object.freeze({ status: failures.length === 0 && vulnerabilityStatus === "PASS" && provenanceAttestationStatus === "PASS" ? "PASS" : "FAIL_UNVERIFIED", vulnerabilityStatus, provenanceAttestationStatus, failures: Object.freeze(failures), }); } function signatureMatches( evidence: z.infer< | typeof vulnerabilityProviderReportSchema | typeof provenanceProviderAttestationSchema >, trust: ProviderTrust | null, ): boolean { if ( !trust || evidence.signature.keyId !== trust.keyId || trust.publicKey.asymmetricKeyType !== "ed25519" ) { return false; } try { return verify( null, providerEvidenceSignaturePayload(evidence), trust.publicKey, Buffer.from(evidence.signature.value, "base64"), ); } catch { return false; } } function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }