import { spawnSync } from "node:child_process"; import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { expect, it } from "vitest"; import { captureCiCandidateArchive, withVerifiedCapturedCandidate, } from "../../scripts/lib/ci-candidate-archive.ts"; import { verifyArchivedLocalEvidence } from "../../scripts/lib/local-release-evidence.ts"; import { RELEASE_CANDIDATE_EVIDENCE_PATHS, RELEASE_CANDIDATE_MANIFEST_PATH, releaseCandidateManifestSchema, } from "../../scripts/lib/release-candidate.ts"; import { linkFixtureNodeModules } from "../../scripts/lib/fixture-node-modules.ts"; it( "builds a real candidate assessment and passes the default archived verifier from the captured archive", async () => { const sourceRoot = process.cwd(); const fixtureRoot = await mkdtemp(path.join(tmpdir(), "security-followup-producer-")); try { await cp(sourceRoot, fixtureRoot, { recursive: true, filter: (source) => { const relative = path.relative(sourceRoot, source); if (!relative) return true; const first = relative.split(path.sep)[0]; return ![ ".release", "artifacts", "dist", "node_modules", ].includes(first ?? ""); }, }); await cp(path.join(sourceRoot, "artifacts"), path.join(fixtureRoot, "artifacts"), { recursive: true, }); await rm(path.join(fixtureRoot, "artifacts/release"), { recursive: true, force: true, }); await linkFixtureNodeModules(fixtureRoot, sourceRoot); const git = spawnSync("git", ["show", "-s", "--format=%H%n%ct", "HEAD"], { cwd: sourceRoot, encoding: "utf8", }); expect(git.status, git.stderr).toBe(0); const [revision, sourceDateEpoch] = git.stdout.trim().split(/\r?\n/u); const build = spawnSync( "corepack", ["pnpm", "build:release-candidate"], { cwd: fixtureRoot, encoding: "utf8", timeout: 120_000, maxBuffer: 32 * 1024 * 1024, env: { ...process.env, CI: "true", VITE_BUILD_ID: "security-followup-integration", VITE_COMMIT_SHA: revision, RELEASE_ID: "security-followup-integration", SOURCE_DATE_EPOCH: sourceDateEpoch, CI_RUNNER_IMAGE: `fixture@sha256:${"a".repeat(64)}`, }, }, ); expect(build.status, `${build.stdout}\n${build.stderr}`).toBe(0); const manifest = releaseCandidateManifestSchema.parse( JSON.parse( await readFile(path.join(fixtureRoot, RELEASE_CANDIDATE_MANIFEST_PATH), "utf8"), ) as unknown, ); const archivePath = path.join(fixtureRoot, "candidate.tar.gz"); const archived = spawnSync( "/usr/bin/tar", [ "--sort=name", "--mtime=@0", "--owner=0", "--group=0", "--numeric-owner", "-czf", archivePath, "dist", ...RELEASE_CANDIDATE_EVIDENCE_PATHS, RELEASE_CANDIDATE_MANIFEST_PATH, ], { cwd: fixtureRoot, encoding: "utf8" }, ); expect(archived.status, archived.stderr).toBe(0); const archiveBytes = await readFile(archivePath); const expectedSha256 = await import("node:crypto").then(({ createHash }) => createHash("sha256").update(archiveBytes).digest("hex"), ); const captured = await captureCiCandidateArchive({ archivePath, expectedSha256 }); const verified = await withVerifiedCapturedCandidate({ captured, verify: ({ extractionRoot, manifest: extractedManifest }) => verifyArchivedLocalEvidence({ extractionRoot, expectedManifest: extractedManifest, }), }); expect(manifest.files).toContainEqual( expect.objectContaining({ path: "artifacts/security/local-evidence-assessment.json", }), ); expect(verified).toEqual( expect.objectContaining({ status: "PASS", identity: expect.objectContaining({ sourceRevision: revision }), failures: [], }), ); const outsideRoot = await mkdtemp(path.join(tmpdir(), "security-followup-outside-")); try { await mkdir(path.join(outsideRoot, "config/security"), { recursive: true }); await writeFile( path.join(outsideRoot, "config/security/dependency-policy.json"), '{"contradictoryCheckoutCanary":"FAIL"}\n', ); const outsideVerification = spawnSync( process.execPath, [ path.join(sourceRoot, "scripts/verify-archived-local-evidence.ts"), "--archive", archivePath, "--sha256", expectedSha256, ], { cwd: outsideRoot, encoding: "utf8", timeout: 120_000, maxBuffer: 32 * 1024 * 1024, }, ); expect( outsideVerification.status, `${outsideVerification.stdout}\n${outsideVerification.stderr}`, ).toBe(0); expect(outsideVerification.stdout).toContain( "Archived local evidence verification: PASS", ); } finally { await rm(outsideRoot, { recursive: true, force: true }); } } finally { await rm(fixtureRoot, { recursive: true, force: true }); } }, 150_000, );