import { createHash, generateKeyPairSync, sign, type KeyObject, } from "node:crypto"; import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { localEvidenceAssessmentArtifactSchema } from "../../scripts/contracts/release-artifacts.ts"; import { verifyArchivedLocalEvidence } from "../../scripts/lib/local-release-evidence.ts"; import { evaluatePromotionEvidence, providerEvidenceSignaturePayload, providerPublicKeyFingerprint, } from "../../scripts/lib/provider-evidence.ts"; import { readProviderTrust } from "../../scripts/lib/promotion-verifier.ts"; import { superviseProviderEvidence } from "../../scripts/lib/provider-supervisor.ts"; import { LOCAL_EVIDENCE_ASSESSMENT_PATH, distSha256, type ReleaseCandidateManifest, } from "../../scripts/lib/release-candidate.ts"; import { supplyChainDigest } from "../../scripts/lib/supply-chain.ts"; const digest = (value: string): string => createHash("sha256").update(value).digest("hex"); const digestBytes = (value: Buffer): string => createHash("sha256").update(value).digest("hex"); function passingAssessment(): any { return { schemaVersion: 1 as const, artifactType: "local-evidence-assessment" as const, generatedAt: "2026-08-02T00:00:00.000Z", status: "PASS" as const, verifier: { id: "clean-architecture-frontend-template/local-evidence-verifier", version: "1", sourceSha256: digest("verifier source"), }, source: { revision: "a".repeat(40), sourceSetSha256: digest("source set"), }, candidate: { distSha256: digest("dist"), lockfileSha256: digest("lockfile"), sbomSha256: digest("sbom"), }, policyInputs: [ { path: "config/security/dependency-policy.json", bytes: 3, sha256: digest("{}\n"), }, ], evidenceInputs: [ { path: "pnpm-lock.yaml", bytes: 9, sha256: digest("lockfile\n") }, ], checks: { release: "PASS" as const, supplyChain: "PASS" as const, dependencyPolicy: "PASS" as const, licensePolicy: "PASS" as const, vulnerabilityPolicy: "PASS" as const, secretScan: "PASS" as const, }, failures: [] as string[], }; } describe("security follow-up contracts", () => { it("rejects a PASS local assessment with a failed check or failure diagnostic", () => { const failedCheck = passingAssessment(); failedCheck.checks.secretScan = "FAIL"; const failureDiagnostic = passingAssessment(); failureDiagnostic.failures.push("secret scan failed"); expect(localEvidenceAssessmentArtifactSchema.safeParse(failedCheck).success).toBe(false); expect(localEvidenceAssessmentArtifactSchema.safeParse(failureDiagnostic).success).toBe(false); expect(localEvidenceAssessmentArtifactSchema.parse(passingAssessment()).status).toBe("PASS"); }); it("passes archived verification from extracted members without checkout source or policy paths", async () => { const fixture = await createArchivedAssessmentFixture(); try { const result = await verifyArchivedLocalEvidence({ extractionRoot: fixture.root, expectedManifest: fixture.manifest, }); expect(result).toEqual({ status: "PASS", identity: { sourceRevision: "a".repeat(40), sourceSetSha256: digest("source set"), assessmentSha256: fixture.assessmentSha256, }, failures: [], }); } finally { await rm(fixture.root, { recursive: true, force: true }); } }); it("rejects archived verification when the assessment is absent", async () => { const fixture = await createArchivedAssessmentFixture(); try { await rm(path.join(fixture.root, LOCAL_EVIDENCE_ASSESSMENT_PATH)); const result = await verifyArchivedLocalEvidence({ extractionRoot: fixture.root, expectedManifest: fixture.manifest, }); expect(result.status).toBe("FAIL"); expect(result.failures).toContain("local evidence assessment is missing or invalid"); } finally { await rm(fixture.root, { recursive: true, force: true }); } }); it("rejects digest-bound raw identity evidence that is not valid under its strict producer schema", async () => { const fixture = await createArchivedAssessmentFixture(); try { const provenancePath = "artifacts/release/provenance.json"; const malformed = Buffer.from( `${JSON.stringify({ predicate: { materials: { sourceSetSha256: digest("source set") } } })}\n`, ); await writeFile(path.join(fixture.root, provenancePath), malformed); const assessmentPath = path.join(fixture.root, LOCAL_EVIDENCE_ASSESSMENT_PATH); const assessment = localEvidenceAssessmentArtifactSchema.parse( JSON.parse(await readFile(assessmentPath, "utf8")) as unknown, ); assessment.evidenceInputs = assessment.evidenceInputs.map((row) => row.path === provenancePath ? { path: provenancePath, bytes: malformed.byteLength, sha256: digestBytes(malformed) } : row, ); const assessmentBytes = Buffer.from(`${JSON.stringify(assessment)}\n`); await writeFile(assessmentPath, assessmentBytes); const files = fixture.manifest.files.map((row) => { if (row.path === provenancePath) { return { path: provenancePath, bytes: malformed.byteLength, sha256: digestBytes(malformed) }; } if (row.path === LOCAL_EVIDENCE_ASSESSMENT_PATH) { return { path: LOCAL_EVIDENCE_ASSESSMENT_PATH, bytes: assessmentBytes.byteLength, sha256: digestBytes(assessmentBytes), }; } return row; }); const manifest = { ...fixture.manifest, files, bundleSha256: supplyChainDigest(files) }; await writeFile( path.join(fixture.root, "artifacts/release/release-candidate.json"), `${JSON.stringify(manifest)}\n`, ); const result = await verifyArchivedLocalEvidence({ extractionRoot: fixture.root, expectedManifest: manifest, }); expect(result.status).toBe("FAIL"); expect(result.failures).toContain( "archived source/build/provenance identities are missing or invalid", ); } finally { await rm(fixture.root, { recursive: true, force: true }); } }); it("accepts signed provider v2 evidence only for the exact run, source, archive, and nonce", () => { const now = Date.parse("2026-08-02T01:00:00.000Z"); const vulnerabilityKeys = generateKeyPairSync("ed25519"); const provenanceKeys = generateKeyPairSync("ed25519"); const expected = providerExpectedContext(); const vulnerability = signedProviderV2( { ...expected, schemaVersion: 2, evidenceType: "vulnerability-report", provider: "fixture-vulnerability", issuedAt: "2026-08-02T01:00:00.000Z", expiresAt: "2026-08-02T02:00:00.000Z", run: { ...expected.run, invocationNonce: "1".repeat(64) }, findings: [], }, "vulnerability-key", vulnerabilityKeys.publicKey, vulnerabilityKeys.privateKey, ); const provenance = signedProviderV2( { ...expected, schemaVersion: 2, evidenceType: "provenance-attestation", provider: "fixture-provenance", signer: "fixture-workload", issuedAt: "2026-08-02T01:00:00.000Z", expiresAt: "2026-08-02T02:00:00.000Z", run: { ...expected.run, invocationNonce: "2".repeat(64) }, subject: { name: "dist", digest: { sha256: expected.candidate.distSha256 } }, }, "provenance-key", provenanceKeys.publicKey, provenanceKeys.privateKey, ); const result = evaluatePromotionEvidence({ expected: { ...expected, vulnerabilityInvocationNonce: "1".repeat(64), provenanceInvocationNonce: "2".repeat(64), }, localStatus: "PASS", vulnerabilityReport: vulnerability, provenanceAttestation: provenance, vulnerabilityTrust: trust("vulnerability-key", vulnerabilityKeys.publicKey), provenanceTrust: trust("provenance-key", provenanceKeys.publicKey), nowEpochMs: () => now, }); expect(result).toEqual({ status: "PASS", vulnerabilityStatus: "PASS", provenanceAttestationStatus: "PASS", failures: [], }); const replayed = evaluatePromotionEvidence({ expected: { ...expected, run: { id: expected.run.id, attempt: 2 }, vulnerabilityInvocationNonce: "1".repeat(64), provenanceInvocationNonce: "2".repeat(64), }, localStatus: "PASS", vulnerabilityReport: vulnerability, provenanceAttestation: provenance, vulnerabilityTrust: trust("vulnerability-key", vulnerabilityKeys.publicKey), provenanceTrust: trust("provenance-key", provenanceKeys.publicKey), nowEpochMs: () => now, }); expect(replayed.status).toBe("FAIL_UNVERIFIED"); expect(replayed.failures).toEqual( expect.arrayContaining([ "vulnerability report run identity mismatch", "provenance attestation run identity mismatch", ]), ); }); it.each(["vulnerability", "provenance"] as const)( "rejects correctly re-signed %s v2 context/time/replay drift", (kind) => { const now = Date.parse("2026-08-02T01:00:00.000Z"); const vulnerabilityKeys = generateKeyPairSync("ed25519"); const provenanceKeys = generateKeyPairSync("ed25519"); const expected = providerExpectedContext(); const baseVulnerability = providerUnsigned("vulnerability", expected); const baseProvenance = providerUnsigned("provenance", expected); const validVulnerability = signedProviderV2( baseVulnerability, "vulnerability-key", vulnerabilityKeys.publicKey, vulnerabilityKeys.privateKey, ); const validProvenance = signedProviderV2( baseProvenance, "provenance-key", provenanceKeys.publicKey, provenanceKeys.privateKey, ); const rawCases: Array) => Record, RegExp, ]> = [ ["schema v1", (value) => ({ ...value, schemaVersion: 1 }), /missing or invalid/u], [ "evidence type", (value) => ({ ...value, evidenceType: kind === "vulnerability" ? "provenance-attestation" : "vulnerability-report", }), /missing or invalid/u, ], ...(["archiveSha256", "bundleSha256", "distSha256", "lockfileSha256"] as const).map( (field) => [ `candidate ${field}`, (value: Record) => ({ ...value, candidate: { ...value.candidate, [field]: "f".repeat(64) }, ...(kind === "provenance" && field === "distSha256" ? { subject: { name: "dist", digest: { sha256: "f".repeat(64) }, }, } : {}), }), /candidate identity|subject dist/u, ] as const, ), [ "different archive with same dist and lockfile", (value) => ({ ...value, candidate: { ...value.candidate, archiveSha256: "e".repeat(64) }, }), /candidate identity/u, ], [ "source revision", (value) => ({ ...value, source: { ...value.source, revision: "c".repeat(40) } }), /source identity/u, ], [ "source set", (value) => ({ ...value, source: { ...value.source, sourceSetSha256: "c".repeat(64) } }), /source identity/u, ], [ "run id", (value) => ({ ...value, run: { ...value.run, id: "other-run" } }), /run identity/u, ], [ "run attempt replay", (value) => ({ ...value, run: { ...value.run, attempt: 2 } }), /run identity/u, ], [ "different nonce", (value) => ({ ...value, run: { ...value.run, invocationNonce: "3".repeat(64) } }), /invocation nonce/u, ], [ "missing nonce", (value) => { const run = { ...value.run }; delete run.invocationNonce; return { ...value, run }; }, /missing or invalid/u, ], [ "uppercase nonce", (value) => ({ ...value, run: { ...value.run, invocationNonce: "A".repeat(64) } }), /missing or invalid/u, ], [ "short nonce", (value) => ({ ...value, run: { ...value.run, invocationNonce: "1".repeat(62) } }), /missing or invalid/u, ], [ "issued future boundary", (value) => ({ ...value, issuedAt: "2026-08-02T01:05:00.001Z" }), /future skew/u, ], [ "expiry equality", (value) => ({ ...value, expiresAt: "2026-08-02T01:00:00.000Z" }), /expired/u, ], [ "expiry past", (value) => ({ ...value, expiresAt: "2026-08-02T00:59:59.999Z" }), /expired/u, ], [ "zero lifetime", (value) => ({ ...value, issuedAt: "2026-08-02T01:01:00.000Z", expiresAt: "2026-08-02T01:01:00.000Z", }), /not positive/u, ], [ "negative lifetime", (value) => ({ ...value, issuedAt: "2026-08-02T01:02:00.000Z", expiresAt: "2026-08-02T01:01:59.999Z", }), /not positive/u, ], [ "lifetime above two hours", (value) => ({ ...value, issuedAt: "2026-08-02T01:00:00.000Z", expiresAt: "2026-08-02T03:00:00.001Z", }), /exceeds two hours/u, ], [ "wrong fingerprint", (value) => ({ ...value, signature: { ...value.signature, publicKeyFingerprint: `sha256:${"d".repeat(64)}`, }, }), /trust identity/u, ], ]; const cases = rawCases.map(([name, mutate, failure]) => ({ name, mutate, failure, })); for (const testCase of cases) { const base = kind === "vulnerability" ? baseVulnerability : baseProvenance; const mutated = testCase.mutate(structuredClone(base)); const resigned = signedProviderV2( mutated, kind === "vulnerability" ? "vulnerability-key" : "provenance-key", kind === "vulnerability" ? vulnerabilityKeys.publicKey : provenanceKeys.publicKey, kind === "vulnerability" ? vulnerabilityKeys.privateKey : provenanceKeys.privateKey, "signature" in mutated && mutated.signature?.publicKeyFingerprint ? mutated.signature.publicKeyFingerprint : undefined, ); const result = evaluatePromotionEvidence({ expected: { ...expected, vulnerabilityInvocationNonce: "1".repeat(64), provenanceInvocationNonce: "2".repeat(64), }, localStatus: "PASS", vulnerabilityReport: kind === "vulnerability" ? resigned : validVulnerability, provenanceAttestation: kind === "provenance" ? resigned : validProvenance, vulnerabilityTrust: trust("vulnerability-key", vulnerabilityKeys.publicKey), provenanceTrust: trust("provenance-key", provenanceKeys.publicKey), nowEpochMs: () => now, }); expect(result.status, testCase.name).toBe("FAIL_UNVERIFIED"); expect(result.failures.join("\n"), testCase.name).toMatch(testCase.failure); } }, ); it("canonicalizes provider fingerprints from DER SPKI across PEM wrapping and rejects Ed448", async () => { const root = await mkdtemp(path.join(tmpdir(), "provider-fingerprint-")); try { const ed25519 = generateKeyPairSync("ed25519").publicKey; const pem = ed25519.export({ type: "spki", format: "pem" }).toString(); const body = pem.replace(/-----[^-]+-----|\s/gu, ""); const wrapped = (width: number) => `-----BEGIN PUBLIC KEY-----\n${body.match(new RegExp(`.{1,${width}}`, "gu"))!.join("\n")}\n-----END PUBLIC KEY-----\n`; await writeFile(path.join(root, "a.pem"), wrapped(64)); await writeFile(path.join(root, "b.pem"), wrapped(32)); const first = await readProviderTrust(root, "a.pem", "fixture-key"); const second = await readProviderTrust(root, "b.pem", "fixture-key"); expect(first?.publicKeyFingerprint).toBe(providerPublicKeyFingerprint(ed25519)); expect(second?.publicKeyFingerprint).toBe(first?.publicKeyFingerprint); const ed448 = generateKeyPairSync("ed448").publicKey; await writeFile(root + "/ed448.pem", ed448.export({ type: "spki", format: "pem" })); await expect(readProviderTrust(root, "ed448.pem", "fixture-key")).resolves.toBeNull(); expect(() => providerPublicKeyFingerprint(ed448)).toThrow(/must be Ed25519/u); } finally { await rm(root, { recursive: true, force: true }); } }); it("captures the downloaded archive pathname exactly once in the provider supervisor", async () => { const keys = generateKeyPairSync("ed25519"); const expected = providerExpectedContext(); let captureCount = 0; let receivedEnvironment: Readonly> | undefined; const manifest: ReleaseCandidateManifest = { schemaVersion: 1, distSha256: expected.candidate.distSha256, lockfileSha256: expected.candidate.lockfileSha256, bundleSha256: expected.candidate.bundleSha256, files: [{ path: "pnpm-lock.yaml", bytes: 1, sha256: expected.candidate.lockfileSha256 }], }; const result = await superviseProviderEvidence( { kind: "vulnerability", archivePath: "/downloads/candidate.tar.gz", expectedArchiveSha256: expected.candidate.archiveSha256, expectedRun: { id: expected.run.id, attempt: expected.run.attempt, sourceRevision: expected.source.revision, }, trust: trust("vulnerability-key", keys.publicKey), executeProvider: async ({ environment }) => { receivedEnvironment = environment; }, captureReport: async () => Buffer.from("{}\n"), }, { captureArchive: async (input) => { captureCount += 1; expect(input).toEqual({ archivePath: "/downloads/candidate.tar.gz", expectedSha256: expected.candidate.archiveSha256, }); return { bytes: Buffer.from("captured archive"), archiveSha256: expected.candidate.archiveSha256, }; }, withVerifiedCandidate: (async (input: any) => input.verify({ extractionRoot: "/captured/extraction", manifest })) as any, verifyLocalEvidence: async () => ({ status: "PASS", identity: { sourceRevision: expected.source.revision, sourceSetSha256: expected.source.sourceSetSha256, assessmentSha256: digest("assessment"), }, failures: [], }), validateUpload: (async (input: any) => { expect("archivePath" in input).toBe(false); return { sealed: true }; }) as any, randomBytes: () => Buffer.alloc(32, 0x11), nowEpochMs: () => Date.parse("2026-08-02T01:00:00.000Z"), }, ); expect(captureCount).toBe(1); expect(receivedEnvironment).toEqual( expect.objectContaining({ PROVIDER_EVIDENCE_SCHEMA_VERSION: "2", PROVIDER_INVOCATION_NONCE: "11".repeat(32), PROVIDER_ISSUED_AT: "2026-08-02T01:00:00.000Z", PROVIDER_EXPIRES_AT: "2026-08-02T02:00:00.000Z", CI_RUN_ID: expected.run.id, CI_RUN_ATTEMPT: "1", SOURCE_REVISION: expected.source.revision, CANDIDATE_ARCHIVE_SHA256: expected.candidate.archiveSha256, }), ); expect(result.evidence).toEqual({ sealed: true }); }); }); function providerExpectedContext() { return { run: { id: "run-42", attempt: 1 }, source: { revision: "b".repeat(40), sourceSetSha256: digest("provider source") }, candidate: { archiveSha256: digest("archive"), bundleSha256: digest("bundle"), distSha256: digest("provider dist"), lockfileSha256: digest("provider lockfile"), }, } as const; } function fingerprint(publicKey: KeyObject): string { return `sha256:${createHash("sha256") .update(publicKey.export({ type: "spki", format: "der" })) .digest("hex")}`; } function trust(keyId: string, publicKey: KeyObject) { return { keyId, publicKey, publicKeyFingerprint: fingerprint(publicKey) }; } function signedProviderV2( unsigned: Record, keyId: string, publicKey: KeyObject, privateKey: KeyObject, fingerprintOverride?: string, ) { const { signature: existingSignature, ...payload } = unsigned; const value = { ...payload, signature: { algorithm: "Ed25519" as const, keyId, publicKeyFingerprint: fingerprintOverride ?? (existingSignature && typeof existingSignature === "object" && "publicKeyFingerprint" in existingSignature ? String(existingSignature.publicKeyFingerprint) : fingerprint(publicKey)), value: "", }, }; value.signature.value = sign( null, providerEvidenceSignaturePayload(value), privateKey, ).toString("base64"); return value; } function providerUnsigned( kind: "vulnerability" | "provenance", expected: ReturnType, ): Record { const common = { ...expected, schemaVersion: 2, evidenceType: kind === "vulnerability" ? "vulnerability-report" : "provenance-attestation", provider: `fixture-${kind}`, issuedAt: "2026-08-02T01:00:00.000Z", expiresAt: "2026-08-02T02:00:00.000Z", run: { ...expected.run, invocationNonce: kind === "vulnerability" ? "1".repeat(64) : "2".repeat(64), }, }; return kind === "vulnerability" ? { ...common, findings: [] } : { ...common, signer: "fixture-workload", subject: { name: "dist", digest: { sha256: expected.candidate.distSha256 }, }, }; } async function createArchivedAssessmentFixture(): Promise<{ root: string; manifest: ReleaseCandidateManifest; assessmentSha256: string; }> { const root = await mkdtemp(path.join(tmpdir(), "archived-assessment-")); const sourceRevision = "a".repeat(40); const sourceSetSha256 = digest("source set"); const releaseManifestBytes = Buffer.from( `${JSON.stringify({ schemaVersion: 1, appVersion: "1.0.0", buildId: "build-1", commitSha: sourceRevision, configSchemaVersion: "1", apiContractVersion: "1", assetManifestHash: digest("vite manifest"), releaseId: "release-1", builtAt: "2026-08-02T00:00:00.000Z", routeChunks: { home: "assets/home.js" }, })}\n`, ); const distInputs = [ { path: "dist/app.js", bytes: Buffer.byteLength("app\n"), sha256: digest("app\n"), gzipBytes: 0 }, { path: "dist/release-manifest.json", bytes: releaseManifestBytes.byteLength, sha256: digestBytes(releaseManifestBytes), gzipBytes: 0, }, ]; const candidateDist = distSha256(distInputs); const sbomBytes = Buffer.from( `${JSON.stringify({ bomFormat: "CycloneDX", specVersion: "1.6", serialNumber: "urn:uuid:00000000-0000-4000-8000-000000000001", version: 1, metadata: { component: { type: "application", name: "fixture", version: "1.0.0" }, properties: [], }, components: [], dependencies: [], })}\n`, ); const sbomSha256 = digestBytes(sbomBytes); const lockfileBytes = Buffer.from("lockfile\n"); const lockfileDigest = digestBytes(lockfileBytes); const buildManifest = { schemaVersion: 1, buildId: "build-1", commitSha: sourceRevision, releaseId: "release-1", moduleInventoryHash: digest("module inventory"), generatedAt: "2026-08-02T00:00:00.000Z", buildContext: { nodeVersion: "v24.0.0", packageManagerVersion: "11.0.0", runnerImage: "linux-x64", sourceDateEpoch: "1785638400", }, outputs: { directory: "dist", viteManifest: "dist/.vite/manifest.json", moduleInventory: "artifacts/quality/vite-module-inventory.json", routeChunks: { home: "assets/home.js" }, runtimeConfigSchema: "dist/runtime-config.schema.json", }, }; const provenance = { _type: "https://in-toto.io/Statement/v1", subject: [{ name: "dist", digest: { sha256: candidateDist } }], predicateType: "https://slsa.dev/provenance/v1", predicate: { buildDefinition: { buildType: "https://vite.dev/build/v1", externalParameters: {}, internalParameters: {}, resolvedDependencies: [ { uri: "pnpm-lock.yaml", digest: { sha256: lockfileDigest } }, ], }, runDetails: { builder: { id: "fixture-builder" }, metadata: { invocationId: "LOCAL_UNSIGNED" }, }, materials: { lockfileSha256: lockfileDigest, sourceSetSha256, sbomSha256 }, }, }; const supplyVerification = { schemaVersion: 1, localStatus: "PASS", promotionStatus: "FAIL_UNVERIFIED", lockfileSha256: lockfileDigest, sourceSetSha256, distSha256: candidateDist, sbomSha256, dependencyDiff: { added: [], removed: [], changed: [], upgrades: [] }, highRiskReview: [], vulnerabilityStatus: "FAIL_UNVERIFIED", provenanceAttestationStatus: "FAIL_UNVERIFIED", failures: [], }; const members = new Map([ ["dist/app.js", Buffer.from("app\n")], ["dist/release-manifest.json", releaseManifestBytes], ["pnpm-lock.yaml", lockfileBytes], ["artifacts/release/build-manifest.json", Buffer.from(`${JSON.stringify(buildManifest)}\n`)], ["artifacts/release/provenance.json", Buffer.from(`${JSON.stringify(provenance)}\n`)], [ "artifacts/security/supply-chain-verification.json", Buffer.from(`${JSON.stringify(supplyVerification)}\n`), ], ["artifacts/release/sbom.cdx.json", sbomBytes], ]); const evidenceInputs = [...members.entries()] .map(([memberPath, bytes]) => ({ path: memberPath, bytes: bytes.byteLength, sha256: digestBytes(bytes), })) .sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0)); const policyPaths = [ "config/security/dependency-baseline.approval.json", "config/security/dependency-baseline.json", "config/security/dependency-change-evidence.json", "config/security/dependency-policy.json", "config/security/secret-scan-policy.json", "config/security/vulnerability-exceptions.json", "config/security/vulnerability-policy.json", "schemas/artifacts/build-manifest.schema.json", "schemas/artifacts/dependency-inventory.schema.json", "schemas/artifacts/supply-chain-verification.schema.json", "scripts/contracts/release-artifacts.ts", "scripts/create-release-candidate.ts", "scripts/generate-supply-chain.ts", "scripts/lib/build-manifest-outputs.ts", "scripts/lib/json-schema.ts", "scripts/lib/local-policy-evidence.ts", "scripts/lib/local-release-evidence.ts", "scripts/lib/release-candidate.ts", "scripts/lib/release-input-evidence.ts", "scripts/lib/release-runtime-coherence.ts", "scripts/lib/repository-file-inventory.ts", "scripts/lib/secret-scan-evaluator.ts", "scripts/lib/secret-scan-policy.ts", "scripts/lib/supply-chain.ts", "scripts/lib/validated-json-artifact.ts", "src/contracts/release-artifacts.ts", ]; const sbomRow = evidenceInputs.find( ({ path: memberPath }) => memberPath === "artifacts/release/sbom.cdx.json", )!; const policyInputs = policyPaths.map((policyPath) => ({ path: policyPath, bytes: 2, sha256: digest(`policy:${policyPath}`), })); const verifierPaths = new Set([ "scripts/contracts/release-artifacts.ts", "scripts/create-release-candidate.ts", "scripts/generate-supply-chain.ts", "scripts/lib/build-manifest-outputs.ts", "scripts/lib/json-schema.ts", "scripts/lib/local-policy-evidence.ts", "scripts/lib/local-release-evidence.ts", "scripts/lib/release-candidate.ts", "scripts/lib/release-input-evidence.ts", "scripts/lib/release-runtime-coherence.ts", "scripts/lib/repository-file-inventory.ts", "scripts/lib/secret-scan-evaluator.ts", "scripts/lib/secret-scan-policy.ts", "scripts/lib/supply-chain.ts", "scripts/lib/validated-json-artifact.ts", "src/contracts/release-artifacts.ts", ]); const assessment = localEvidenceAssessmentArtifactSchema.parse({ ...passingAssessment(), verifier: { id: "clean-architecture-frontend-template/local-evidence-verifier", version: "1", sourceSha256: supplyChainDigest( policyInputs.filter(({ path: policyPath }) => verifierPaths.has(policyPath)), ), }, source: { revision: sourceRevision, sourceSetSha256 }, candidate: { distSha256: candidateDist, lockfileSha256: evidenceInputs.find(({ path: memberPath }) => memberPath === "pnpm-lock.yaml")! .sha256, sbomSha256: sbomRow.sha256, }, policyInputs, evidenceInputs, }); const assessmentBytes = Buffer.from(`${JSON.stringify(assessment)}\n`); members.set(LOCAL_EVIDENCE_ASSESSMENT_PATH, assessmentBytes); for (const [memberPath, bytes] of members) { await mkdir(path.dirname(path.join(root, memberPath)), { recursive: true }); await writeFile(path.join(root, memberPath), bytes); } const files = [...members.entries()] .map(([memberPath, bytes]) => ({ path: memberPath, bytes: bytes.byteLength, sha256: digestBytes(bytes), })) .sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0)); const manifest: ReleaseCandidateManifest = { schemaVersion: 1, distSha256: assessment.candidate.distSha256, lockfileSha256: assessment.candidate.lockfileSha256, bundleSha256: supplyChainDigest(files), files, }; await mkdir(path.join(root, "artifacts/release"), { recursive: true }); await writeFile( path.join(root, "artifacts/release/release-candidate.json"), `${JSON.stringify(manifest)}\n`, ); return { root, manifest, assessmentSha256: digestBytes(assessmentBytes) }; }