129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
import { generateKeyPairSync, sign } from "node:crypto";
|
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
|
|
import {
|
|
evaluatePromotionEvidence,
|
|
providerEvidenceSignaturePayload,
|
|
} from "./lib/provider-evidence.ts";
|
|
|
|
const candidateDistSha256 = "1".repeat(64);
|
|
const lockfileSha256 = "2".repeat(64);
|
|
const vulnerabilityKeys = generateKeyPairSync("ed25519");
|
|
const provenanceKeys = generateKeyPairSync("ed25519");
|
|
const trust = {
|
|
vulnerabilityTrust: {
|
|
keyId: "fixture-vulnerability-key",
|
|
publicKey: vulnerabilityKeys.publicKey,
|
|
},
|
|
provenanceTrust: {
|
|
keyId: "fixture-provenance-key",
|
|
publicKey: provenanceKeys.publicKey,
|
|
},
|
|
};
|
|
|
|
function signedEvidence(
|
|
value: Record<string, unknown>,
|
|
keyId: string,
|
|
privateKey: typeof vulnerabilityKeys.privateKey,
|
|
) {
|
|
return {
|
|
...value,
|
|
signature: {
|
|
algorithm: "Ed25519",
|
|
keyId,
|
|
value: sign(
|
|
null,
|
|
providerEvidenceSignaturePayload(value),
|
|
privateKey,
|
|
).toString("base64"),
|
|
},
|
|
};
|
|
}
|
|
|
|
function evidenceFor(distDigest: string) {
|
|
return {
|
|
vulnerabilityReport: signedEvidence(
|
|
{
|
|
schemaVersion: 1,
|
|
provider: "fixture-vulnerability-provider",
|
|
generatedAt: "2026-08-01T00:00:00.000Z",
|
|
scannedLockfileSha256: lockfileSha256,
|
|
scannedDistSha256: distDigest,
|
|
findings: [],
|
|
},
|
|
"fixture-vulnerability-key",
|
|
vulnerabilityKeys.privateKey,
|
|
),
|
|
provenanceAttestation: signedEvidence(
|
|
{
|
|
schemaVersion: 1,
|
|
provider: "fixture-provenance-provider",
|
|
signer: "fixture-workload-identity",
|
|
generatedAt: "2026-08-01T00:00:00.000Z",
|
|
subject: { name: "dist", digest: { sha256: distDigest } },
|
|
},
|
|
"fixture-provenance-key",
|
|
provenanceKeys.privateKey,
|
|
),
|
|
};
|
|
}
|
|
|
|
const base = {
|
|
candidate: { distSha256: candidateDistSha256, lockfileSha256 },
|
|
currentDistSha256: candidateDistSha256,
|
|
localStatus: "PASS",
|
|
...trust,
|
|
};
|
|
const validEvidence = evidenceFor(candidateDistSha256);
|
|
const fixtures = {
|
|
absent: evaluatePromotionEvidence({
|
|
...base,
|
|
vulnerabilityReport: null,
|
|
provenanceAttestation: null,
|
|
}),
|
|
validImmutable: evaluatePromotionEvidence({ ...base, ...validEvidence }),
|
|
wrongDigest: evaluatePromotionEvidence({
|
|
...base,
|
|
...evidenceFor("3".repeat(64)),
|
|
}),
|
|
postAttestationMutation: evaluatePromotionEvidence({
|
|
...base,
|
|
...validEvidence,
|
|
currentDistSha256: "4".repeat(64),
|
|
}),
|
|
};
|
|
const passed =
|
|
fixtures.validImmutable.status === "PASS" &&
|
|
fixtures.absent.status === "FAIL_UNVERIFIED" &&
|
|
fixtures.wrongDigest.status === "FAIL_UNVERIFIED" &&
|
|
fixtures.postAttestationMutation.status === "FAIL_UNVERIFIED";
|
|
|
|
await mkdir("artifacts/security", { recursive: true });
|
|
await writeFile(
|
|
"artifacts/security/supply-chain-provider-fixtures.json",
|
|
`${JSON.stringify(
|
|
{
|
|
schemaVersion: 1,
|
|
fixtures: Object.fromEntries(
|
|
Object.entries(fixtures).map(([name, result]) => [
|
|
name,
|
|
{ status: result.status, failures: result.failures },
|
|
]),
|
|
),
|
|
passingFixtureCount: Object.values(fixtures).filter(
|
|
(result) => result.status === "PASS",
|
|
).length,
|
|
status: passed ? "PASS" : "FAIL",
|
|
},
|
|
null,
|
|
2,
|
|
)}\n`,
|
|
);
|
|
if (!passed) {
|
|
process.stderr.write("Supply-chain provider fixtures failed closed incorrectly\n");
|
|
process.exit(1);
|
|
}
|
|
process.stdout.write(
|
|
"Supply-chain provider fixtures: only the valid immutable fixture PASS\n",
|
|
);
|