fix: harden provider and promotion evidence
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { cp, mkdtemp, readFile, rm, symlink } 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";
|
||||
|
||||
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 symlink(path.join(sourceRoot, "node_modules"), path.join(fixtureRoot, "node_modules"), "dir");
|
||||
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: [],
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
await rm(fixtureRoot, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
150_000,
|
||||
);
|
||||
Reference in New Issue
Block a user