import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { z, type ZodType } from "zod"; import { EXPECTED_CONTRACT_SET_PACKAGES } from "../../src/features/installed-contract-contributions.ts"; import { ROUTE_REGISTRY, ROUTE_RUNTIME_CONTRACT, } from "../../src/features/installed-feature-contracts.ts"; import { buildManifestArtifactSchema, bundlePerformanceArtifactSchema, dependencyDiffArtifactSchema, dependencyInventoryArtifactSchema, licenseReportArtifactSchema, localEvidenceAssessmentArtifactSchema, provenanceArtifactSchema, releaseManifestArtifactSchema, releaseVerificationArtifactSchema, runtimeConfigArtifactSchema, sbomArtifactSchema, supplyChainVerificationArtifactSchema, vulnerabilityReportArtifactSchema, } from "../contracts/release-artifacts.ts"; import { CANONICAL_VITE_MANIFEST_PATH, verifyBuildManifestOutputs, } from "./build-manifest-outputs.ts"; import { assertMatchesJsonSchema } from "./json-schema.ts"; import { LOCAL_EVIDENCE_ASSESSMENT_PATH, LOCAL_EVIDENCE_POLICY_INPUT_PATHS, LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS, RELEASE_CANDIDATE_EVIDENCE_PATHS, RELEASE_CANDIDATE_MANIFEST_PATH, collectDistOutputs, distSha256, releaseCandidateManifestSchema, type ReleaseCandidateManifest, } from "./release-candidate.ts"; import { verifyReleaseRuntimeCoherence } from "./release-runtime-coherence.ts"; import { digestReleaseInputFiles } from "./release-input-evidence.ts"; import { compareStoredDependencyEvidence, compareStoredLicenseEvidence, compareStoredLocalVulnerabilityReport, recomputeDependencyEvidence, recomputeLicenseEvidence, verifyLocalSupplyChainDefaults, verifyStoredDistChecksums, } from "./local-policy-evidence.ts"; import { buildRepositoryFileInventory, parseRepositoryFileInventoryPolicy, } from "./repository-file-inventory.ts"; import { parseSecretScanPolicy, secretScanSarifSchema, evaluateRepositorySecretScan, verifyStoredSecretScan, } from "./secret-scan-evaluator.ts"; import { secretScanRules } from "./secret-scan.ts"; import { isValidSha512Integrity, parsePnpmLockfilePackages, supplyChainDigest, verifySupplyChainCoherence, } from "./supply-chain.ts"; const sha256 = z.string().regex(/^[a-f0-9]{64}$/u); export const supplyChainCoherenceReportSchema = z .object({ schemaVersion: z.literal(1), status: z.enum(["PASS", "FAIL"]), dependencyCount: z.int().nonnegative(), lockfileSha256: sha256, distSha256: sha256, sbomSha256: sha256, failures: z.array(z.string()), }) .strict(); export type SupplyChainCoherenceReport = z.infer< typeof supplyChainCoherenceReportSchema >; export async function verifyLocalSupplyChainEvidence( repositoryRoot = process.cwd(), ): Promise { const failures: string[] = []; const inventory = await parseArtifact( repositoryRoot, "artifacts/release/dependency-inventory.json", dependencyInventoryArtifactSchema, "dependency inventory", failures, ); const sbom = await parseArtifact( repositoryRoot, "artifacts/release/sbom.cdx.json", sbomArtifactSchema, "SBOM", failures, ); const provenance = await parseArtifact( repositoryRoot, "artifacts/release/provenance.json", provenanceArtifactSchema, "local provenance", failures, ); const verification = await parseArtifact( repositoryRoot, "artifacts/security/supply-chain-verification.json", supplyChainVerificationArtifactSchema, "supply-chain verification", failures, ); for (const [schemaPath, artifact, label] of [ [ "schemas/artifacts/dependency-inventory.schema.json", inventory, "dependency inventory", ], [ "schemas/artifacts/supply-chain-verification.schema.json", verification, "supply-chain verification", ], ] as const) { if (!artifact) continue; try { assertMatchesJsonSchema( await readJson(repositoryRoot, schemaPath), artifact, label, ); } catch { failures.push(`${label} JSON Schema mismatch`); } } let lockfileText = ""; let lockfileSha256 = "0".repeat(64); try { const rawLockfile = await readFile( path.join(repositoryRoot, "pnpm-lock.yaml"), ); lockfileText = rawLockfile.toString("utf8"); lockfileSha256 = createHash("sha256").update(rawLockfile).digest("hex"); } catch { failures.push("raw pnpm-lock.yaml is missing or unreadable"); } let distDigest = "0".repeat(64); try { distDigest = distSha256(await collectDistOutputs(repositoryRoot)); } catch { failures.push("candidate dist is missing or unreadable"); } const sbomSha256 = sbom ? supplyChainDigest(sbom) : "0".repeat(64); let coherenceFailures: readonly string[] = Object.freeze([]); if (inventory && sbom && provenance) { coherenceFailures = verifySupplyChainCoherence( sbom, inventory, provenance, distDigest, ).failures; failures.push(...coherenceFailures); } if ( !inventory || !verification || inventory.lockfileSha256 !== lockfileSha256 || verification.lockfileSha256 !== lockfileSha256 ) { failures.push("inventory/verification lockfile digest mismatch"); } if ( !verification || verification.localStatus !== "PASS" || verification.failures.length > 0 || verification.distSha256 !== distDigest || verification.sbomSha256 !== sbomSha256 ) { failures.push("verification digest/status set is incoherent"); } if (verification) { failures.push(...verifyLocalSupplyChainDefaults(verification)); } if (inventory && sbom && provenance && verification) { try { const policy = parseRepositoryFileInventoryPolicy( await readJson( repositoryRoot, "config/security/secret-scan-policy.json", ), ); const repositoryInventory = await buildRepositoryFileInventory({ repositoryRoot, trackedRoots: policy.trackedRoots, generatedRoots: policy.generatedRoots, optionalRoots: policy.optionalRoots, }); const sourceSetSha256 = await digestReleaseInputFiles( repositoryInventory.trackedFiles, (file) => readFile(path.join(repositoryRoot, file)), ); if ( verification.sourceSetSha256 !== sourceSetSha256 || provenance.predicate.materials.sourceSetSha256 !== sourceSetSha256 || provenance.predicate.materials.sbomSha256 !== sbomSha256 ) { failures.push("source/SBOM provenance materials are incoherent"); } } catch { failures.push("release source inventory is unavailable or unreadable"); } } if (inventory && verification) { try { const dependencyPolicy = recomputeDependencyEvidence({ inventory, baseline: await optionalReadJson( repositoryRoot, "config/security/dependency-baseline.json", ), baselineApproval: await optionalReadJson( repositoryRoot, "config/security/dependency-baseline.approval.json", ), dependencyChangeEvidence: await readJson( repositoryRoot, "config/security/dependency-change-evidence.json", ), }); const licensePolicy = recomputeLicenseEvidence({ inventory, policy: await readJson( repositoryRoot, "config/security/dependency-policy.json", ), }); const expectedFailures = [ ...licensePolicy.failures, ...dependencyPolicy.failures, ...coherenceFailures, ]; if ( supplyChainDigest(verification.dependencyDiff) !== supplyChainDigest(dependencyPolicy.dependencyDiff) || supplyChainDigest(verification.highRiskReview) !== supplyChainDigest(dependencyPolicy.highRisk) || supplyChainDigest(verification.failures) !== supplyChainDigest(expectedFailures) || verification.localStatus !== (expectedFailures.length === 0 ? "PASS" : "FAIL") ) { failures.push( "supply-chain verification policy fields do not match recomputed evidence", ); } } catch { failures.push("supply-chain verification policy inputs are invalid"); } } const lockRows = parsePnpmLockfilePackages(lockfileText); const inventoryRows = inventory?.dependencies ?? []; const inventoryByIdentity = new Map( inventoryRows.map( (entry) => [`${entry.name}@${entry.version}`, entry] as const, ), ); if (lockRows.length !== inventoryRows.length) { failures.push("transitive dependency count differs from lockfile"); } for (const lockRow of lockRows) { const identity = `${lockRow.name}@${lockRow.version}`; const dependency = inventoryByIdentity.get(identity); if ( !dependency || dependency.integrity !== lockRow.integrity || !isValidSha512Integrity(lockRow.integrity) ) { failures.push(`lockfile inventory integrity mismatch: ${identity}`); } } return supplyChainCoherenceReportSchema.parse({ schemaVersion: 1, status: failures.length === 0 ? "PASS" : "FAIL", dependencyCount: inventoryRows.length, lockfileSha256, distSha256: distDigest, sbomSha256, failures, }); } export const LOCAL_EVIDENCE_VERIFIER_ID = "tech-log-frontend/local-evidence-verifier"; export const LOCAL_EVIDENCE_VERIFIER_VERSION = "1"; export { LOCAL_EVIDENCE_POLICY_INPUT_PATHS, LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS, } from "./release-candidate.ts"; export async function createLocalEvidenceAssessment( repositoryRoot = process.cwd(), ): Promise> { const root = path.resolve(repositoryRoot); const outputs = await collectDistOutputs(root); const evidencePaths = RELEASE_CANDIDATE_EVIDENCE_PATHS.filter( (memberPath) => memberPath !== LOCAL_EVIDENCE_ASSESSMENT_PATH, ); const evidenceInputs = ( await Promise.all([ ...outputs.map(async ({ path: memberPath }) => digestInput(root, memberPath)), ...evidencePaths.map((memberPath) => digestInput(root, memberPath)), ]) ).sort((left, right) => asciiCompare(left.path, right.path)); const lockfile = evidenceInputs.find(({ path: memberPath }) => memberPath === "pnpm-lock.yaml"); const sbom = evidenceInputs.find( ({ path: memberPath }) => memberPath === "artifacts/release/sbom.cdx.json", ); if (!lockfile || !sbom) throw new Error("local assessment candidate inputs are incomplete"); const candidate: ReleaseCandidateManifest = { schemaVersion: 1, distSha256: distSha256(outputs), lockfileSha256: lockfile.sha256, bundleSha256: supplyChainDigest(evidenceInputs), files: evidenceInputs, }; const evaluated = await evaluateProducerLocalChecks(root, candidate); const [build, release, provenance, supply, sbomDocument, policyInputs, secretScan] = await Promise.all([ readJson(root, "artifacts/release/build-manifest.json").then((value) => buildManifestArtifactSchema.parse(value), ), readJson(root, "dist/release-manifest.json").then((value) => releaseManifestArtifactSchema.parse(value), ), readJson(root, "artifacts/release/provenance.json").then((value) => provenanceArtifactSchema.parse(value), ), readJson(root, "artifacts/security/supply-chain-verification.json").then((value) => supplyChainVerificationArtifactSchema.parse(value), ), readJson(root, "artifacts/release/sbom.cdx.json").then((value) => sbomArtifactSchema.parse(value), ), Promise.all( LOCAL_EVIDENCE_POLICY_INPUT_PATHS.map((policyPath) => digestInput(root, policyPath), ), ), evaluateRepositorySecretScan({ repositoryRoot: root }), ]); const identityFailures: string[] = []; if (build.commitSha !== release.commitSha) { identityFailures.push("producer build/release source revision mismatch"); } if ( provenance.predicate.materials.sourceSetSha256 !== supply.sourceSetSha256 ) { identityFailures.push("producer provenance/supply source-set mismatch"); } if (supply.distSha256 !== candidate.distSha256) { identityFailures.push("producer supply/candidate dist digest mismatch"); } if (supply.lockfileSha256 !== candidate.lockfileSha256) { identityFailures.push("producer supply/candidate lockfile digest mismatch"); } if (supply.sbomSha256 !== supplyChainDigest(sbomDocument)) { identityFailures.push("producer supply/candidate SBOM digest mismatch"); } const checks = { ...evaluated.checks, ...(identityFailures.some((failure) => failure.includes("build/release")) ? { release: "FAIL" as const } : {}), ...(identityFailures.some((failure) => !failure.includes("build/release")) ? { supplyChain: "FAIL" as const } : {}), }; const failures = [...evaluated.failures, ...identityFailures]; const status = failures.length === 0 && Object.values(checks).every( (check) => check === "PASS", ) ? ("PASS" as const) : ("FAIL" as const); const verifierSources = policyInputs.filter(({ path: policyPath }) => (LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS as readonly string[]).includes(policyPath), ); if (verifierSources.length !== LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS.length) { throw new Error("local assessment verifier source set is incomplete"); } const secretPolicy = policyInputs.find( ({ path: policyPath }) => policyPath === "config/security/secret-scan-policy.json", ); const secretSarif = evidenceInputs.find( ({ path: evidencePath }) => evidencePath === "artifacts/security/scan.sarif", ); if (!secretPolicy || !secretSarif) { throw new Error("local assessment secret scan inputs are incomplete"); } return localEvidenceAssessmentArtifactSchema.parse({ schemaVersion: 1, artifactType: "local-evidence-assessment", generatedAt: build.generatedAt, status, verifier: { id: LOCAL_EVIDENCE_VERIFIER_ID, version: LOCAL_EVIDENCE_VERIFIER_VERSION, sourceSha256: supplyChainDigest(verifierSources), }, source: { revision: build.commitSha, sourceSetSha256: supply.sourceSetSha256, }, candidate: { distSha256: candidate.distSha256, lockfileSha256: candidate.lockfileSha256, sbomSha256: sbom.sha256, }, secretScan: { policySha256: secretPolicy.sha256, sarifSha256: secretSarif.sha256, scanInputSha256: secretScan.scanInputSha256, }, policyInputs, evidenceInputs, checks, failures, }); } type LocalCheckName = | "release" | "supplyChain" | "dependencyPolicy" | "licensePolicy" | "vulnerabilityPolicy" | "secretScan"; async function evaluateProducerLocalChecks( root: string, candidate: ReleaseCandidateManifest, options: Readonly<{ archived?: boolean }> = {}, ): Promise>; failures: readonly string[]; }>> { const checks: Record = { release: "PASS", supplyChain: "PASS", dependencyPolicy: "PASS", licensePolicy: "PASS", vulnerabilityPolicy: "PASS", secretScan: "PASS", }; const failures: string[] = []; const evaluate = async ( check: LocalCheckName, operation: () => Promise, ): Promise => { try { const diagnostics = await operation(); if (diagnostics.length > 0) { checks[check] = "FAIL"; failures.push(...diagnostics.map((failure) => `${check}:${failure}`)); } } catch (error) { checks[check] = "FAIL"; failures.push( `${check}:${error instanceof Error ? error.message : String(error)}`, ); } }; await evaluate("release", async () => { const [build, release, runtime, stored] = await Promise.all([ readJson(root, "artifacts/release/build-manifest.json").then((value) => buildManifestArtifactSchema.parse(value), ), readJson(root, "dist/release-manifest.json").then((value) => releaseManifestArtifactSchema.parse(value), ), readJson(root, "dist/config.json").then((value) => runtimeConfigArtifactSchema.parse(value), ), readJson(root, "artifacts/release/verification.json").then((value) => releaseVerificationArtifactSchema.parse(value), ), ]); const diagnostics: string[] = []; if ( build.commitSha !== release.commitSha || build.buildId !== release.buildId || build.releaseId !== release.releaseId || build.generatedAt !== release.builtAt ) { diagnostics.push("build/release identity mismatch"); } if ( !stored.passed || !stored.artifact.checked || !stored.artifact.compatible || stored.artifact.mismatches.length > 0 || stored.artifact.releaseId !== release.releaseId || stored.generatedAt !== release.builtAt || stored.fixtures.length === 0 || stored.fixtures.some((fixture) => !fixture.passed) ) { diagnostics.push("stored release verification is not a coherent PASS"); } if (!runtime.BUILD_ID || !runtime.RELEASE_ID) { diagnostics.push("runtime release identity is missing"); } else { const apiContractVersion = release.schemaVersion === 1 && "API_CONTRACT_VERSION" in runtime ? runtime.API_CONTRACT_VERSION : undefined; const coherence = await verifyReleaseRuntimeCoherence({ release, runtime: { BUILD_ID: runtime.BUILD_ID, RELEASE_ID: runtime.RELEASE_ID, CONFIG_SCHEMA_VERSION: runtime.CONFIG_SCHEMA_VERSION, ...(apiContractVersion === undefined ? {} : { API_CONTRACT_VERSION: apiContractVersion }), }, contractPackages: EXPECTED_CONTRACT_SET_PACKAGES, }); diagnostics.push(...coherence.mismatches.map((item) => `runtime:${item}`)); } diagnostics.push(...(await verifyBuildManifestOutputs(build, { repositoryRoot: root }))); return diagnostics; }); await evaluate("supplyChain", async () => { const [inventory, sbom, provenance, supply, coherence, lockfileBytes] = await Promise.all([ readJson(root, "artifacts/release/dependency-inventory.json").then((value) => dependencyInventoryArtifactSchema.parse(value), ), readJson(root, "artifacts/release/sbom.cdx.json").then((value) => sbomArtifactSchema.parse(value), ), readJson(root, "artifacts/release/provenance.json").then((value) => provenanceArtifactSchema.parse(value), ), readJson(root, "artifacts/security/supply-chain-verification.json").then((value) => supplyChainVerificationArtifactSchema.parse(value), ), readJson(root, "artifacts/security/supply-chain-coherence.json").then((value) => supplyChainCoherenceReportSchema.parse(value), ), readFile(path.join(root, "pnpm-lock.yaml")), ]); const diagnostics: string[] = []; const lockfileSha256 = createHash("sha256").update(lockfileBytes).digest("hex"); const outputs = await collectDistOutputs(root); const currentDistSha256 = distSha256(outputs); const sbomSha256 = supplyChainDigest(sbom); const independentlyCoherent = verifySupplyChainCoherence( sbom, inventory, provenance, currentDistSha256, ); if ( supply.localStatus !== "PASS" || supply.failures.length > 0 || supply.distSha256 !== candidate.distSha256 || supply.lockfileSha256 !== candidate.lockfileSha256 || supply.sbomSha256 !== sbomSha256 || supply.sourceSetSha256 !== provenance.predicate.materials.sourceSetSha256 || inventory.lockfileSha256 !== lockfileSha256 || candidate.lockfileSha256 !== lockfileSha256 || candidate.distSha256 !== currentDistSha256 || coherence.status !== "PASS" || coherence.failures.length > 0 || coherence.distSha256 !== candidate.distSha256 || coherence.lockfileSha256 !== candidate.lockfileSha256 || coherence.sbomSha256 !== sbomSha256 || coherence.dependencyCount !== inventory.dependencies.length || independentlyCoherent.failures.length > 0 ) { diagnostics.push("stored supply-chain evidence is not a coherent PASS"); } diagnostics.push(...verifyLocalSupplyChainDefaults(supply)); diagnostics.push( ...verifyStoredDistChecksums( outputs, await readFile(path.join(root, "artifacts/release/checksums.txt"), "utf8"), ), ); const lockRows = parsePnpmLockfilePackages(lockfileBytes.toString("utf8")); const inventoryByIdentity = new Map( inventory.dependencies.map((entry) => [`${entry.name}@${entry.version}`, entry] as const), ); if (lockRows.length !== inventory.dependencies.length) { diagnostics.push("transitive dependency count differs from lockfile"); } for (const lockRow of lockRows) { const dependency = inventoryByIdentity.get(`${lockRow.name}@${lockRow.version}`); if ( !dependency || dependency.integrity !== lockRow.integrity || !isValidSha512Integrity(lockRow.integrity) ) { diagnostics.push(`lockfile inventory integrity mismatch: ${lockRow.name}@${lockRow.version}`); } } return diagnostics; }); await evaluate("dependencyPolicy", async () => { const [inventory, stored] = await Promise.all([ readJson(root, "artifacts/release/dependency-inventory.json").then((value) => dependencyInventoryArtifactSchema.parse(value), ), readJson(root, "artifacts/security/dependency-diff.json").then((value) => dependencyDiffArtifactSchema.parse(value), ), ]); const recomputed = recomputeDependencyEvidence({ inventory, baseline: await optionalReadJson(root, "config/security/dependency-baseline.json"), baselineApproval: await optionalReadJson( root, "config/security/dependency-baseline.approval.json", ), dependencyChangeEvidence: await readJson( root, "config/security/dependency-change-evidence.json", ), }); return compareStoredDependencyEvidence(recomputed, stored); }); await evaluate("licensePolicy", async () => { const [inventory, stored, policy] = await Promise.all([ readJson(root, "artifacts/release/dependency-inventory.json").then((value) => dependencyInventoryArtifactSchema.parse(value), ), readJson(root, "artifacts/security/license-report.json").then((value) => licenseReportArtifactSchema.parse(value), ), readJson(root, "config/security/dependency-policy.json"), ]); return compareStoredLicenseEvidence( recomputeLicenseEvidence({ inventory, policy }), stored, ); }); await evaluate("vulnerabilityPolicy", async () => { const vulnerability = vulnerabilityReportArtifactSchema.parse( await readJson(root, "artifacts/security/vulnerability-report.json"), ); return compareStoredLocalVulnerabilityReport( candidate.lockfileSha256, vulnerability, ); }); await evaluate("secretScan", async () => { if (options.archived) { const policy = parseSecretScanPolicy( await readJson(root, "config/security/secret-scan-policy.json"), ); const sarif = secretScanSarifSchema.parse( await readJson(root, "artifacts/security/scan.sarif"), ); const diagnostics: string[] = []; if ( policy.trackedRoots.length === 0 || policy.generatedRoots.length === 0 || sarif.runs[0]!.results.length > 0 || JSON.stringify(sarif.runs[0]!.tool.driver.rules.map(({ id }) => id)) !== JSON.stringify(secretScanRules().map(({ id }) => id)) ) { diagnostics.push("archived secret scan is not an independently valid PASS"); } return diagnostics; } const evaluation = await evaluateRepositorySecretScan({ repositoryRoot: root }); return verifyStoredSecretScan( evaluation, await readJson(root, "artifacts/security/scan.sarif"), ); }); return Object.freeze({ checks: Object.freeze(checks), failures: Object.freeze(failures) }); } async function digestInput( repositoryRoot: string, memberPath: string, ): Promise> { const absolute = path.resolve(repositoryRoot, memberPath); const relative = path.relative(repositoryRoot, absolute); if ( relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) ) { throw new TypeError(`local assessment input escapes repository: ${memberPath}`); } const bytes = await readFile(absolute); return Object.freeze({ path: memberPath, bytes: bytes.byteLength, sha256: createHash("sha256").update(bytes).digest("hex"), }); } function asciiCompare(left: string, right: string): number { return left < right ? -1 : left > right ? 1 : 0; } export async function verifyArchivedLocalEvidence(input: Readonly<{ extractionRoot: string; expectedManifest: ReleaseCandidateManifest; }>): Promise; }>; failures: readonly string[]; }>> { const extractionRoot = path.resolve(input.extractionRoot); const failures: string[] = []; let extractedManifest: ReleaseCandidateManifest | null = null; try { extractedManifest = releaseCandidateManifestSchema.parse( await readJson(extractionRoot, RELEASE_CANDIDATE_MANIFEST_PATH), ); } catch { failures.push("extracted release candidate manifest is missing or invalid"); } if ( extractedManifest && JSON.stringify(extractedManifest) !== JSON.stringify(input.expectedManifest) ) { failures.push("caller expectedManifest differs from extracted manifest"); } let assessment: z.infer | null = null; let assessmentSha256 = ""; let assessmentBytes: Buffer | null = null; try { assessmentBytes = await readFile( path.join(extractionRoot, LOCAL_EVIDENCE_ASSESSMENT_PATH), ); assessmentSha256 = createHash("sha256").update(assessmentBytes).digest("hex"); assessment = localEvidenceAssessmentArtifactSchema.parse( JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(assessmentBytes)) as unknown, ); } catch { failures.push("local evidence assessment is missing or invalid"); } if (assessment && extractedManifest) { const assessmentMember = extractedManifest.files.find( ({ path: memberPath }) => memberPath === LOCAL_EVIDENCE_ASSESSMENT_PATH, ); if ( !assessmentMember || assessmentMember.bytes !== assessmentBytes?.byteLength || assessmentMember.sha256 !== assessmentSha256 ) { failures.push("local assessment manifest binding mismatch"); } if ( assessment.verifier.id !== LOCAL_EVIDENCE_VERIFIER_ID || assessment.verifier.version !== LOCAL_EVIDENCE_VERIFIER_VERSION ) { failures.push("local assessment verifier identity mismatch"); } const policyPaths = assessment.policyInputs.map(({ path: policyPath }) => policyPath); if (JSON.stringify(policyPaths) !== JSON.stringify(LOCAL_EVIDENCE_POLICY_INPUT_PATHS)) { failures.push("local assessment policyInputs exact set mismatch"); } for (const policyInput of assessment.policyInputs) { try { const bytes = await readFile(path.join(extractionRoot, policyInput.path)); const member = extractedManifest.files.find( ({ path: memberPath }) => memberPath === policyInput.path, ); if ( !member || member.bytes !== policyInput.bytes || member.sha256 !== policyInput.sha256 || bytes.byteLength !== policyInput.bytes || createHash("sha256").update(bytes).digest("hex") !== policyInput.sha256 ) { failures.push(`archived policy input binding mismatch: ${policyInput.path}`); } } catch { failures.push(`archived policy input is missing or invalid: ${policyInput.path}`); } } const verifierSources = assessment.policyInputs.filter(({ path: policyPath }) => (LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS as readonly string[]).includes(policyPath), ); if ( verifierSources.length !== LOCAL_EVIDENCE_VERIFIER_SOURCE_PATHS.length || supplyChainDigest(verifierSources) !== assessment.verifier.sourceSha256 ) { failures.push("local assessment verifier-source digest mismatch"); } const expectedEvidenceInputs = extractedManifest.files.filter( ({ path: memberPath }) => memberPath !== LOCAL_EVIDENCE_ASSESSMENT_PATH, ); if (JSON.stringify(assessment.evidenceInputs) !== JSON.stringify(expectedEvidenceInputs)) { failures.push("local assessment evidenceInputs exact member binding mismatch"); } const sbom = extractedManifest.files.find( ({ path: memberPath }) => memberPath === "artifacts/release/sbom.cdx.json", ); const secretPolicy = extractedManifest.files.find( ({ path: memberPath }) => memberPath === "config/security/secret-scan-policy.json", ); const secretSarif = extractedManifest.files.find( ({ path: memberPath }) => memberPath === "artifacts/security/scan.sarif", ); if ( assessment.candidate.distSha256 !== extractedManifest.distSha256 || assessment.candidate.lockfileSha256 !== extractedManifest.lockfileSha256 || !sbom || assessment.candidate.sbomSha256 !== sbom.sha256 ) { failures.push("local assessment candidate digest binding mismatch"); } if ( !secretPolicy || !secretSarif || assessment.secretScan.policySha256 !== secretPolicy.sha256 || assessment.secretScan.sarifSha256 !== secretSarif.sha256 ) { failures.push("local assessment secret scan artifact binding mismatch"); } if (assessment.status !== "PASS" || Object.values(assessment.checks).includes("FAIL")) { failures.push("local evidence assessment is not PASS"); } try { const [supply, coherence] = await Promise.all([ readJson(extractionRoot, "artifacts/security/supply-chain-verification.json").then( (value) => supplyChainVerificationArtifactSchema.parse(value), ), readJson(extractionRoot, "artifacts/security/supply-chain-coherence.json").then( (value) => supplyChainCoherenceReportSchema.parse(value), ), ]); if ( supply.localStatus !== "PASS" || supply.failures.length > 0 || coherence.status !== "PASS" || coherence.failures.length > 0 ) { failures.push("archived supply-chain subordinate evidence is not PASS"); } } catch { failures.push("archived supply-chain subordinate evidence is missing or invalid"); } const independent = await evaluateProducerLocalChecks( extractionRoot, extractedManifest, { archived: true }, ); if ( independent.failures.length > 0 || JSON.stringify(independent.checks) !== JSON.stringify(assessment.checks) ) { failures.push( ...independent.failures.map((failure) => `archived local check:${failure}`), ); failures.push("archived local checks do not independently reproduce assessment PASS"); } const identities = await readArchivedIdentities(extractionRoot, failures); if ( identities.buildRevision !== assessment.source.revision || identities.releaseRevision !== assessment.source.revision ) { failures.push("local assessment source revision identity mismatch"); } if ( identities.provenanceSourceSetSha256 !== assessment.source.sourceSetSha256 || identities.supplySourceSetSha256 !== assessment.source.sourceSetSha256 ) { failures.push("local assessment source-set identity mismatch"); } } const uniqueFailures = Object.freeze([...new Set(failures)]); const passingAssessment = uniqueFailures.length === 0 ? assessment : null; return Object.freeze({ status: passingAssessment ? "PASS" : "FAIL", identity: passingAssessment ? Object.freeze({ sourceRevision: passingAssessment.source.revision, sourceSetSha256: passingAssessment.source.sourceSetSha256, assessmentSha256, secretScan: passingAssessment.secretScan, }) : null, failures: uniqueFailures, }); } async function readArchivedIdentities( extractionRoot: string, failures: string[], ): Promise> { try { const [buildDocument, releaseDocument, provenanceDocument, supplyDocument] = await Promise.all([ readJson(extractionRoot, "artifacts/release/build-manifest.json"), readJson(extractionRoot, "dist/release-manifest.json"), readJson(extractionRoot, "artifacts/release/provenance.json"), readJson(extractionRoot, "artifacts/security/supply-chain-verification.json"), ]); const build = buildManifestArtifactSchema.parse(buildDocument); const release = releaseManifestArtifactSchema.parse(releaseDocument); const provenance = provenanceArtifactSchema.parse(provenanceDocument); const supply = supplyChainVerificationArtifactSchema.parse(supplyDocument); return Object.freeze({ buildRevision: build.commitSha, releaseRevision: release.commitSha, provenanceSourceSetSha256: provenance.predicate.materials.sourceSetSha256, supplySourceSetSha256: supply.sourceSetSha256, }); } catch { failures.push("archived source/build/provenance identities are missing or invalid"); return Object.freeze({ buildRevision: null, releaseRevision: null, provenanceSourceSetSha256: null, supplySourceSetSha256: null, }); } } export async function assessLocalEvidenceForProducer(input: Readonly<{ repositoryRoot?: string; candidate: ReleaseCandidateManifest; }>): Promise> { const repositoryRoot = path.resolve(input.repositoryRoot ?? process.cwd()); const failures: string[] = []; const supplyReport = await verifyLocalSupplyChainEvidence(repositoryRoot); failures.push(...supplyReport.failures); if (supplyReport.lockfileSha256 !== input.candidate.lockfileSha256) { failures.push("candidate/raw lockfile digest mismatch"); } const buildManifest = await parseArtifact( repositoryRoot, "artifacts/release/build-manifest.json", buildManifestArtifactSchema, "build manifest", failures, ); const release = await parseArtifact( repositoryRoot, "dist/release-manifest.json", releaseManifestArtifactSchema, "release manifest", failures, ); const runtime = await parseArtifact( repositoryRoot, "dist/config.json", runtimeConfigArtifactSchema, "runtime config", failures, ); const storedRelease = await parseArtifact( repositoryRoot, "artifacts/release/verification.json", releaseVerificationArtifactSchema, "release verification", failures, ); const storedSupply = await parseArtifact( repositoryRoot, "artifacts/security/supply-chain-coherence.json", supplyChainCoherenceReportSchema, "supply-chain coherence", failures, ); await validateSupportingArtifacts( repositoryRoot, failures, supplyReport.lockfileSha256, ); if (buildManifest) { try { assertMatchesJsonSchema( await readJson( repositoryRoot, "schemas/artifacts/build-manifest.schema.json", ), buildManifest, "build manifest", ); } catch { failures.push("build manifest JSON Schema mismatch"); } failures.push( ...(await verifyBuildManifestOutputs(buildManifest, { repositoryRoot })), ); } if (release && runtime) { if (!runtime.BUILD_ID || !runtime.RELEASE_ID) { failures.push("runtime release identity is missing"); } else { const apiContractVersion = release.schemaVersion === 1 && "API_CONTRACT_VERSION" in runtime ? runtime.API_CONTRACT_VERSION : undefined; if (release.schemaVersion === 1 && apiContractVersion === undefined) { failures.push("runtime API contract identity is missing"); } const coherence = await verifyReleaseRuntimeCoherence({ release, runtime: { BUILD_ID: runtime.BUILD_ID, RELEASE_ID: runtime.RELEASE_ID, CONFIG_SCHEMA_VERSION: runtime.CONFIG_SCHEMA_VERSION, ...(apiContractVersion === undefined ? {} : { API_CONTRACT_VERSION: apiContractVersion }), }, contractPackages: EXPECTED_CONTRACT_SET_PACKAGES, }); failures.push(...coherence.mismatches.map((item) => `release:${item}`)); } await verifyReleaseOutputs( repositoryRoot, release, buildManifest, failures, ); } if ( !storedRelease || !storedRelease.passed || !storedRelease.artifact.checked || !storedRelease.artifact.compatible || storedRelease.artifact.mismatches.length > 0 || storedRelease.fixtures.length === 0 || storedRelease.fixtures.some((fixture) => !fixture.passed) || storedRelease.artifact.releaseId !== release?.releaseId || storedRelease.generatedAt !== release?.builtAt ) { failures.push("stored release verification is not a coherent PASS"); } if ( !storedSupply || storedSupply.status !== "PASS" || storedSupply.failures.length > 0 || storedSupply.dependencyCount !== supplyReport.dependencyCount || storedSupply.lockfileSha256 !== supplyReport.lockfileSha256 || storedSupply.distSha256 !== supplyReport.distSha256 || storedSupply.sbomSha256 !== supplyReport.sbomSha256 ) { failures.push("stored supply-chain coherence is not a recomputed PASS"); } await verifySecretScan(repositoryRoot, failures); return Object.freeze({ status: failures.length === 0 ? "PASS" : "FAIL", failures: Object.freeze([...new Set(failures)]), }); } async function validateSupportingArtifacts( repositoryRoot: string, failures: string[], lockfileSha256: string, ): Promise { let actualOutputs: Awaited> | null = null; try { actualOutputs = await collectDistOutputs(repositoryRoot); } catch { failures.push("supporting evidence dist inputs are unreadable"); } const bundle = await parseArtifact( repositoryRoot, "artifacts/performance/bundle.json", bundlePerformanceArtifactSchema, "bundle report", failures, ); if ( bundle && actualOutputs && JSON.stringify(bundle.outputs) !== JSON.stringify(actualOutputs) ) { failures.push("bundle report does not describe current dist bytes"); } if (actualOutputs) { try { failures.push( ...verifyStoredDistChecksums( actualOutputs, await readFile( path.join(repositoryRoot, "artifacts/release/checksums.txt"), "utf8", ), ), ); } catch { failures.push("stored dist checksums are missing or unreadable"); } } const inventory = await parseArtifact( repositoryRoot, "artifacts/release/dependency-inventory.json", dependencyInventoryArtifactSchema, "dependency inventory", failures, ); const dependencyDiff = await parseArtifact( repositoryRoot, "artifacts/security/dependency-diff.json", dependencyDiffArtifactSchema, "dependency diff", failures, ); if (inventory && dependencyDiff) { try { const recomputed = recomputeDependencyEvidence({ inventory, baseline: await optionalReadJson( repositoryRoot, "config/security/dependency-baseline.json", ), baselineApproval: await optionalReadJson( repositoryRoot, "config/security/dependency-baseline.approval.json", ), dependencyChangeEvidence: await readJson( repositoryRoot, "config/security/dependency-change-evidence.json", ), }); failures.push( ...compareStoredDependencyEvidence(recomputed, dependencyDiff), ); } catch { failures.push("dependency policy evidence is missing or invalid"); } } const license = await parseArtifact( repositoryRoot, "artifacts/security/license-report.json", licenseReportArtifactSchema, "license report", failures, ); if (inventory && license) { try { const recomputed = recomputeLicenseEvidence({ inventory, policy: await readJson( repositoryRoot, "config/security/dependency-policy.json", ), }); failures.push(...compareStoredLicenseEvidence(recomputed, license)); } catch { failures.push("license policy evidence is missing or invalid"); } } const vulnerability = await parseArtifact( repositoryRoot, "artifacts/security/vulnerability-report.json", vulnerabilityReportArtifactSchema, "local vulnerability report", failures, ); if (vulnerability) { failures.push( ...compareStoredLocalVulnerabilityReport(lockfileSha256, vulnerability), ); } const provenance = await parseArtifact( repositoryRoot, "artifacts/release/provenance.json", provenanceArtifactSchema, "local provenance", failures, ); if ( provenance?.predicate.runDetails.metadata.invocationId !== "LOCAL_UNSIGNED" ) { failures.push("local provenance must remain LOCAL_UNSIGNED"); } } async function verifyReleaseOutputs( repositoryRoot: string, release: z.infer, buildManifest: z.infer | null, failures: string[], ): Promise { let viteManifest: Record = {}; try { const raw = await readFile( path.join(repositoryRoot, CANONICAL_VITE_MANIFEST_PATH), "utf8", ); viteManifest = asRecord(JSON.parse(raw), "Vite manifest"); if (createHash("sha256").update(raw).digest("hex") !== release.assetManifestHash) { failures.push("release asset manifest hash mismatch"); } } catch { failures.push("Vite manifest is missing or invalid"); } if ( buildManifest && (buildManifest.buildId !== release.buildId || buildManifest.commitSha !== release.commitSha || buildManifest.releaseId !== release.releaseId || buildManifest.generatedAt !== release.builtAt) ) { failures.push("build/release identity mismatch"); } const runtimeContracts: Readonly> = ROUTE_RUNTIME_CONTRACT; for (const definition of Object.values(ROUTE_REGISTRY)) { const runtime = runtimeContracts[definition.routeId]; const viteEntry = Object.values(viteManifest).find( (entry) => isRecord(entry) && entry.name === runtime?.moduleId && entry.isDynamicEntry === true, ); const file = isRecord(viteEntry) ? viteEntry.file : null; if ( typeof file !== "string" || release.routeChunks[definition.chunkId] !== file || buildManifest?.outputs.routeChunks[definition.chunkId] !== file ) { failures.push(`release route chunk mismatch: ${definition.chunkId}`); } } } async function verifySecretScan( repositoryRoot: string, failures: string[], ): Promise { try { const evaluation = await evaluateRepositorySecretScan({ repositoryRoot }); failures.push( ...verifyStoredSecretScan( evaluation, await readJson(repositoryRoot, "artifacts/security/scan.sarif"), ), ); } catch { failures.push("secret scan SARIF is missing or invalid"); } } async function parseArtifact( repositoryRoot: string, file: string, schema: ZodType, label: string, failures: string[], ): Promise { try { return schema.parse(await readJson(repositoryRoot, file)); } catch { failures.push(`${label} executable schema mismatch`); return null; } } async function readJson(repositoryRoot: string, file: string): Promise { return JSON.parse(await readFile(path.join(repositoryRoot, file), "utf8")); } async function optionalReadJson( repositoryRoot: string, file: string, ): Promise { try { return await readJson(repositoryRoot, file); } catch { return null; } } function asRecord(value: unknown, label: string): Record { if (!isRecord(value)) throw new TypeError(`${label} must be a JSON object`); return value; } function isRecord(value: unknown): value is Record { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }