import { validateProviderEvidence, type ExpectedPromotionContext, type ProviderTrust, } from "./provider-evidence.ts"; import { verifyReleaseCandidate, type ReleaseCandidateManifest, } from "./release-candidate.ts"; export async function validateProviderUpload(input: Readonly<{ kind: "vulnerability" | "provenance"; verifiedManifest: ReleaseCandidateManifest; archiveSha256: string; candidateRoot: string; capturedReport: Buffer; expectedContext: ExpectedPromotionContext; trust: ProviderTrust; nowEpochMs?: () => number; }>): Promise { if ( input.expectedContext.candidate.archiveSha256 !== input.archiveSha256 || input.expectedContext.candidate.bundleSha256 !== input.verifiedManifest.bundleSha256 || input.expectedContext.candidate.distSha256 !== input.verifiedManifest.distSha256 || input.expectedContext.candidate.lockfileSha256 !== input.verifiedManifest.lockfileSha256 ) { throw new Error("provider supervisor expected candidate context mismatch"); } const verifiedCandidate = await verifyReleaseCandidate( input.verifiedManifest, input.candidateRoot, ); if (verifiedCandidate.failures.length > 0) { throw new Error( `provider input candidate root changed: ${verifiedCandidate.failures.join("; ")}`, ); } let report: unknown; try { report = JSON.parse( new TextDecoder("utf-8", { fatal: true }).decode(input.capturedReport), ) as unknown; } catch { throw new TypeError("provider output is not canonical UTF-8 JSON"); } const evaluated = validateProviderEvidence({ kind: input.kind, value: report, expected: input.expectedContext, trust: input.trust, nowEpochMs: input.nowEpochMs, }); if (evaluated.status !== "PASS" || !evaluated.evidence) { throw new Error( `provider evidence context validation failed: ${evaluated.failures.join("; ")}`, ); } return evaluated.evidence; }