chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import {
|
||||
createHash,
|
||||
generateKeyPairSync,
|
||||
sign,
|
||||
type KeyObject,
|
||||
} from "node:crypto";
|
||||
import {
|
||||
cp,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
readFile,
|
||||
readdir,
|
||||
rm,
|
||||
symlink,
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
localEvidenceAssessmentArtifactSchema,
|
||||
supplyChainProviderFixturesArtifactSchema,
|
||||
} from "./contracts/release-artifacts.ts";
|
||||
import { PROMOTED_FILE_NAMES } from "./contracts/promotion-artifacts.ts";
|
||||
import {
|
||||
captureCiCandidateArchive,
|
||||
} from "./lib/ci-candidate-archive.ts";
|
||||
import {
|
||||
cleanupFinalizedPromotion,
|
||||
finalizeVerifiedPromotion,
|
||||
} from "./lib/promotion-stager.ts";
|
||||
import { verifyExactPromotionBundle } from "./lib/exact-promotion-bundle.ts";
|
||||
import {
|
||||
providerEvidenceSignaturePayload,
|
||||
providerPublicKeyFingerprint,
|
||||
} from "./lib/provider-evidence.ts";
|
||||
import {
|
||||
LOCAL_EVIDENCE_ASSESSMENT_PATH,
|
||||
RELEASE_CANDIDATE_EVIDENCE_PATHS,
|
||||
RELEASE_CANDIDATE_MANIFEST_PATH,
|
||||
releaseCandidateManifestSchema,
|
||||
} from "./lib/release-candidate.ts";
|
||||
import { writeValidatedJsonArtifact } from "./lib/validated-json-artifact.ts";
|
||||
|
||||
const repositoryRoot = process.cwd();
|
||||
const fixtureRoot = await mkdtemp(path.join(tmpdir(), "provider-exact-five-fixture-"));
|
||||
try {
|
||||
await cp(repositoryRoot, fixtureRoot, {
|
||||
recursive: true,
|
||||
filter: (source) => {
|
||||
const relative = path.relative(repositoryRoot, source);
|
||||
if (!relative) return true;
|
||||
const first = relative.split(path.sep)[0];
|
||||
return ![".release", "artifacts", "dist", "node_modules"].includes(first ?? "");
|
||||
},
|
||||
});
|
||||
await cp(path.join(repositoryRoot, "artifacts"), path.join(fixtureRoot, "artifacts"), {
|
||||
recursive: true,
|
||||
});
|
||||
await rm(path.join(fixtureRoot, "artifacts/release"), { recursive: true, force: true });
|
||||
await symlink(path.join(repositoryRoot, "node_modules"), path.join(fixtureRoot, "node_modules"), "dir");
|
||||
const git = spawnSync("git", ["show", "-s", "--format=%H%n%ct", "HEAD"], {
|
||||
cwd: repositoryRoot,
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (git.status !== 0) throw new Error(`provider fixture git identity failed: ${git.stderr}`);
|
||||
const [revision, sourceDateEpoch] = git.stdout.trim().split(/\r?\n/u);
|
||||
if (!revision || !sourceDateEpoch) throw new Error("provider fixture git identity is incomplete");
|
||||
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: "provider-exact-five-fixture",
|
||||
VITE_COMMIT_SHA: revision,
|
||||
RELEASE_ID: "provider-exact-five-fixture",
|
||||
SOURCE_DATE_EPOCH: sourceDateEpoch,
|
||||
CI_RUNNER_IMAGE: `fixture@sha256:${"a".repeat(64)}`,
|
||||
},
|
||||
});
|
||||
if (build.status !== 0) throw new Error(`${build.stdout}\n${build.stderr}`);
|
||||
const manifest = releaseCandidateManifestSchema.parse(
|
||||
JSON.parse(
|
||||
await readFile(path.join(fixtureRoot, RELEASE_CANDIDATE_MANIFEST_PATH), "utf8"),
|
||||
) as unknown,
|
||||
);
|
||||
const assessment = localEvidenceAssessmentArtifactSchema.parse(
|
||||
JSON.parse(
|
||||
await readFile(path.join(fixtureRoot, LOCAL_EVIDENCE_ASSESSMENT_PATH), "utf8"),
|
||||
) as unknown,
|
||||
);
|
||||
const archivePath = path.join(fixtureRoot, "candidate.tar.gz");
|
||||
const tar = 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" },
|
||||
);
|
||||
if (tar.status !== 0) throw new Error(`provider fixture real tar failed: ${tar.stderr}`);
|
||||
const archiveBytes = await readFile(archivePath);
|
||||
const archiveSha256 = sha256(archiveBytes);
|
||||
const now = Date.parse("2026-08-02T01:00:00.000Z");
|
||||
const vulnerabilityKeys = generateKeyPairSync("ed25519");
|
||||
const provenanceKeys = generateKeyPairSync("ed25519");
|
||||
const vulnerabilityKeyId = "fixture-vulnerability-key";
|
||||
const provenanceKeyId = "fixture-provenance-key";
|
||||
const vulnerabilityNonce = "1".repeat(64);
|
||||
const provenanceNonce = "2".repeat(64);
|
||||
const context = {
|
||||
run: { id: "fixture-run", attempt: 1 },
|
||||
source: {
|
||||
revision: assessment.source.revision,
|
||||
sourceSetSha256: assessment.source.sourceSetSha256,
|
||||
},
|
||||
candidate: {
|
||||
archiveSha256,
|
||||
bundleSha256: manifest.bundleSha256,
|
||||
distSha256: manifest.distSha256,
|
||||
lockfileSha256: manifest.lockfileSha256,
|
||||
},
|
||||
} as const;
|
||||
const vulnerability = signedEvidence(
|
||||
{
|
||||
schemaVersion: 2,
|
||||
evidenceType: "vulnerability-report",
|
||||
provider: "fixture-vulnerability-provider",
|
||||
issuedAt: new Date(now).toISOString(),
|
||||
expiresAt: new Date(now + 60 * 60 * 1_000).toISOString(),
|
||||
run: { ...context.run, invocationNonce: vulnerabilityNonce },
|
||||
source: context.source,
|
||||
candidate: context.candidate,
|
||||
secretScanAttestation: {
|
||||
status: "PASS",
|
||||
localEvidenceAssessmentSha256: sha256(
|
||||
await readFile(path.join(fixtureRoot, LOCAL_EVIDENCE_ASSESSMENT_PATH)),
|
||||
),
|
||||
sourceSetSha256: assessment.source.sourceSetSha256,
|
||||
policySha256: assessment.secretScan.policySha256,
|
||||
sarifSha256: assessment.secretScan.sarifSha256,
|
||||
scanInputSha256: assessment.secretScan.scanInputSha256,
|
||||
},
|
||||
findings: [],
|
||||
},
|
||||
vulnerabilityKeyId,
|
||||
vulnerabilityKeys.publicKey,
|
||||
vulnerabilityKeys.privateKey,
|
||||
);
|
||||
const provenance = signedEvidence(
|
||||
{
|
||||
schemaVersion: 2,
|
||||
evidenceType: "provenance-attestation",
|
||||
provider: "fixture-provenance-provider",
|
||||
signer: "fixture-workload-identity",
|
||||
issuedAt: new Date(now).toISOString(),
|
||||
expiresAt: new Date(now + 60 * 60 * 1_000).toISOString(),
|
||||
run: { ...context.run, invocationNonce: provenanceNonce },
|
||||
source: context.source,
|
||||
candidate: context.candidate,
|
||||
subject: { name: "dist", digest: { sha256: manifest.distSha256 } },
|
||||
},
|
||||
provenanceKeyId,
|
||||
provenanceKeys.publicKey,
|
||||
provenanceKeys.privateKey,
|
||||
);
|
||||
await Promise.all([
|
||||
write(fixtureRoot, "provider/vulnerability.json", `${JSON.stringify(vulnerability)}\n`),
|
||||
write(fixtureRoot, "provider/provenance.json", `${JSON.stringify(provenance)}\n`),
|
||||
write(
|
||||
fixtureRoot,
|
||||
"provider/vulnerability.pem",
|
||||
vulnerabilityKeys.publicKey.export({ type: "spki", format: "pem" }),
|
||||
),
|
||||
write(
|
||||
fixtureRoot,
|
||||
"provider/provenance.pem",
|
||||
provenanceKeys.publicKey.export({ type: "spki", format: "pem" }),
|
||||
),
|
||||
]);
|
||||
const runnerTempRoot = path.join(fixtureRoot, "runner-temp");
|
||||
await mkdir(runnerTempRoot, { mode: 0o700 });
|
||||
const finalized = await finalizeVerifiedPromotion(
|
||||
{
|
||||
repositoryRoot: fixtureRoot,
|
||||
archivePath,
|
||||
expectedArchiveSha256: archiveSha256,
|
||||
vulnerabilityReportPath: "provider/vulnerability.json",
|
||||
provenanceAttestationPath: "provider/provenance.json",
|
||||
vulnerabilityPublicKeyPath: "provider/vulnerability.pem",
|
||||
vulnerabilityKeyId,
|
||||
provenancePublicKeyPath: "provider/provenance.pem",
|
||||
provenanceKeyId,
|
||||
expectedRun: { id: context.run.id, attempt: 1, sourceRevision: revision },
|
||||
vulnerabilityInvocationNonce: vulnerabilityNonce,
|
||||
provenanceInvocationNonce: provenanceNonce,
|
||||
runnerTempRoot,
|
||||
},
|
||||
{
|
||||
nowEpochMs: () => now,
|
||||
randomBytes: (bytes) => Buffer.alloc(bytes, 0x4a),
|
||||
afterCapture: async () => {
|
||||
await writeFile(path.join(fixtureRoot, "dist/index.html"), "contradictory external tree\n");
|
||||
},
|
||||
},
|
||||
);
|
||||
const validImmutable =
|
||||
finalized.files.length === 5 &&
|
||||
(await readdir(finalized.stagingRoot)).length === 5;
|
||||
const exactFiles = Object.fromEntries(
|
||||
await Promise.all(
|
||||
PROMOTED_FILE_NAMES.map(async (name) => [
|
||||
name,
|
||||
await readFile(path.join(finalized.stagingRoot, name)),
|
||||
] as const),
|
||||
),
|
||||
);
|
||||
const bundleTrust = {
|
||||
vulnerabilityTrust: {
|
||||
keyId: vulnerabilityKeyId,
|
||||
publicKey: vulnerabilityKeys.publicKey,
|
||||
publicKeyFingerprint: providerPublicKeyFingerprint(vulnerabilityKeys.publicKey),
|
||||
},
|
||||
provenanceTrust: {
|
||||
keyId: provenanceKeyId,
|
||||
publicKey: provenanceKeys.publicKey,
|
||||
publicKeyFingerprint: providerPublicKeyFingerprint(provenanceKeys.publicKey),
|
||||
},
|
||||
expected: {
|
||||
run: context.run,
|
||||
sourceRevision: context.source.revision,
|
||||
sourceSetSha256: context.source.sourceSetSha256,
|
||||
archiveSha256: context.candidate.archiveSha256,
|
||||
bundleSha256: context.candidate.bundleSha256,
|
||||
distSha256: context.candidate.distSha256,
|
||||
lockfileSha256: context.candidate.lockfileSha256,
|
||||
},
|
||||
nowEpochMs: () => now,
|
||||
} as const;
|
||||
const absentFiles = { ...exactFiles } as Partial<typeof exactFiles>;
|
||||
delete absentFiles["provider-verification.json"];
|
||||
const absent = await captureRejection(async () => {
|
||||
await verifyExactPromotionBundle(absentFiles, bundleTrust);
|
||||
});
|
||||
|
||||
const invalidPath = path.join(fixtureRoot, "invalid.tar.gz");
|
||||
const invalidBytes = Buffer.from("arbitrary non-tar bytes\n");
|
||||
await writeFile(invalidPath, invalidBytes);
|
||||
const invalidTar = await captureRejection(async () => {
|
||||
await finalizeVerifiedPromotion(
|
||||
{
|
||||
repositoryRoot: fixtureRoot,
|
||||
archivePath: invalidPath,
|
||||
expectedArchiveSha256: sha256(invalidBytes),
|
||||
vulnerabilityReportPath: "provider/vulnerability.json",
|
||||
provenanceAttestationPath: "provider/provenance.json",
|
||||
vulnerabilityPublicKeyPath: "provider/vulnerability.pem",
|
||||
vulnerabilityKeyId,
|
||||
provenancePublicKeyPath: "provider/provenance.pem",
|
||||
provenanceKeyId,
|
||||
expectedRun: { id: context.run.id, attempt: 1, sourceRevision: revision },
|
||||
vulnerabilityInvocationNonce: vulnerabilityNonce,
|
||||
provenanceInvocationNonce: provenanceNonce,
|
||||
runnerTempRoot,
|
||||
},
|
||||
{ nowEpochMs: () => now },
|
||||
);
|
||||
});
|
||||
const wrongDigest = await captureRejection(async () => {
|
||||
await captureCiCandidateArchive({ archivePath, expectedSha256: "0".repeat(64) });
|
||||
});
|
||||
const passed =
|
||||
validImmutable && absent.rejected && invalidTar.rejected && wrongDigest.rejected;
|
||||
await cleanupFinalizedPromotion({
|
||||
runnerTempRoot,
|
||||
stagingRoot: finalized.stagingRoot,
|
||||
cleanupToken: finalized.cleanupToken,
|
||||
runnerTempIdentity: finalized.runnerTempIdentity,
|
||||
stagingIdentity: finalized.stagingIdentity,
|
||||
});
|
||||
await mkdir(path.join(repositoryRoot, "artifacts/security"), { recursive: true });
|
||||
await writeValidatedJsonArtifact({
|
||||
path: path.join(repositoryRoot, "artifacts/security/supply-chain-provider-fixtures.json"),
|
||||
schema: supplyChainProviderFixturesArtifactSchema,
|
||||
value: {
|
||||
schemaVersion: 1,
|
||||
actualDefaultVerifier: {
|
||||
status: validImmutable ? "PASS" : "FAIL_UNVERIFIED",
|
||||
failures: validImmutable ? [] : ["canonical exact-five finalizer failed"],
|
||||
},
|
||||
fixtures: {
|
||||
absent: {
|
||||
status: absent.rejected ? "FAIL_UNVERIFIED" : "PASS",
|
||||
failures: [absent.failure],
|
||||
},
|
||||
validImmutable: { status: validImmutable ? "PASS" : "FAIL_UNVERIFIED", failures: [] },
|
||||
wrongDigest: {
|
||||
status: wrongDigest.rejected ? "FAIL_UNVERIFIED" : "PASS",
|
||||
failures: [wrongDigest.failure],
|
||||
},
|
||||
invalidTar: {
|
||||
status: invalidTar.rejected ? "FAIL_UNVERIFIED" : "PASS",
|
||||
failures: [invalidTar.failure],
|
||||
},
|
||||
},
|
||||
externalTreeCanary: {
|
||||
status: validImmutable ? "PASS" : "FAIL_UNVERIFIED",
|
||||
failures: validImmutable ? [] : ["external tree canary influenced captured archive"],
|
||||
},
|
||||
passingFixtureCount: validImmutable ? 1 : 0,
|
||||
status: passed ? "PASS" : "FAIL",
|
||||
},
|
||||
});
|
||||
if (!passed) throw new Error("canonical exact-five provider fixtures failed closed incorrectly");
|
||||
process.stdout.write("Supply-chain provider real-tar signed exact-five fixtures: PASS\n");
|
||||
} finally {
|
||||
await rm(fixtureRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function signedEvidence(
|
||||
value: Record<string, unknown>,
|
||||
keyId: string,
|
||||
publicKey: KeyObject,
|
||||
privateKey: KeyObject,
|
||||
) {
|
||||
return {
|
||||
...value,
|
||||
signature: {
|
||||
algorithm: "Ed25519",
|
||||
keyId,
|
||||
publicKeyFingerprint: providerPublicKeyFingerprint(publicKey),
|
||||
value: sign(
|
||||
null,
|
||||
providerEvidenceSignaturePayload(value),
|
||||
privateKey,
|
||||
).toString("base64"),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function write(root: string, relative: string, value: string | Buffer): Promise<void> {
|
||||
const absolute = path.join(root, relative);
|
||||
await mkdir(path.dirname(absolute), { recursive: true });
|
||||
await writeFile(absolute, value);
|
||||
}
|
||||
|
||||
async function captureRejection(
|
||||
operation: () => Promise<void>,
|
||||
): Promise<Readonly<{ rejected: boolean; failure: string }>> {
|
||||
try {
|
||||
await operation();
|
||||
return Object.freeze({ rejected: false, failure: "fixture unexpectedly passed" });
|
||||
} catch (error) {
|
||||
return Object.freeze({
|
||||
rejected: true,
|
||||
failure: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function sha256(bytes: Buffer): string {
|
||||
return createHash("sha256").update(bytes).digest("hex");
|
||||
}
|
||||
Reference in New Issue
Block a user