fix: enforce exact local evidence defaults
This commit is contained in:
@@ -2,6 +2,8 @@ import {
|
||||
dependencyDiffArtifactSchema,
|
||||
dependencyInventoryArtifactSchema,
|
||||
licenseReportArtifactSchema,
|
||||
supplyChainVerificationArtifactSchema,
|
||||
vulnerabilityReportArtifactSchema,
|
||||
} from "../contracts/release-artifacts.ts";
|
||||
import type { DistOutput } from "./release-candidate.ts";
|
||||
import {
|
||||
@@ -13,6 +15,60 @@ import {
|
||||
|
||||
type Document = Record<string, unknown>;
|
||||
|
||||
export const LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS = Object.freeze({
|
||||
promotionStatus: "FAIL_UNVERIFIED" as const,
|
||||
vulnerabilityStatus: "FAIL_UNVERIFIED" as const,
|
||||
provenanceAttestationStatus: "FAIL_UNVERIFIED" as const,
|
||||
});
|
||||
|
||||
export function verifyLocalSupplyChainDefaults(stored: unknown): string[] {
|
||||
const parsed = supplyChainVerificationArtifactSchema.safeParse(stored);
|
||||
if (
|
||||
!parsed.success ||
|
||||
parsed.data.promotionStatus !==
|
||||
LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS.promotionStatus ||
|
||||
parsed.data.vulnerabilityStatus !==
|
||||
LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS.vulnerabilityStatus ||
|
||||
parsed.data.provenanceAttestationStatus !==
|
||||
LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS.provenanceAttestationStatus
|
||||
) {
|
||||
return [
|
||||
"supply-chain verification provider defaults are not local FAIL_UNVERIFIED",
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function createLocalVulnerabilityReport(lockfileSha256: string) {
|
||||
return vulnerabilityReportArtifactSchema.parse({
|
||||
schemaVersion: 1,
|
||||
provider: "UNCONFIGURED",
|
||||
scannedLockfileSha256: lockfileSha256,
|
||||
status: "FAIL_UNVERIFIED",
|
||||
findings: [],
|
||||
exceptionsApplied: [],
|
||||
failures: ["external vulnerability provider report is missing"],
|
||||
blocking: [],
|
||||
});
|
||||
}
|
||||
|
||||
export function compareStoredLocalVulnerabilityReport(
|
||||
lockfileSha256: string,
|
||||
stored: unknown,
|
||||
): string[] {
|
||||
const parsed = vulnerabilityReportArtifactSchema.safeParse(stored);
|
||||
if (
|
||||
!parsed.success ||
|
||||
supplyChainDigest(parsed.data) !==
|
||||
supplyChainDigest(createLocalVulnerabilityReport(lockfileSha256))
|
||||
) {
|
||||
return [
|
||||
"local vulnerability report does not match exact unconfigured defaults",
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function recomputeDependencyEvidence(input: Readonly<{
|
||||
inventory: unknown;
|
||||
baseline: unknown;
|
||||
|
||||
@@ -35,8 +35,10 @@ import { digestReleaseInputFiles } from "./release-input-evidence.ts";
|
||||
import {
|
||||
compareStoredDependencyEvidence,
|
||||
compareStoredLicenseEvidence,
|
||||
compareStoredLocalVulnerabilityReport,
|
||||
recomputeDependencyEvidence,
|
||||
recomputeLicenseEvidence,
|
||||
verifyLocalSupplyChainDefaults,
|
||||
verifyStoredDistChecksums,
|
||||
} from "./local-policy-evidence.ts";
|
||||
import {
|
||||
@@ -175,6 +177,9 @@ export async function verifyLocalSupplyChainEvidence(
|
||||
) {
|
||||
failures.push("verification digest/status set is incoherent");
|
||||
}
|
||||
if (verification) {
|
||||
failures.push(...verifyLocalSupplyChainDefaults(verification));
|
||||
}
|
||||
if (inventory && sbom && provenance && verification) {
|
||||
try {
|
||||
const policy = parseRepositoryFileInventoryPolicy(
|
||||
@@ -336,7 +341,11 @@ export async function verifyArchivedLocalEvidence(input: Readonly<{
|
||||
failures,
|
||||
);
|
||||
|
||||
await validateSupportingArtifacts(repositoryRoot, failures);
|
||||
await validateSupportingArtifacts(
|
||||
repositoryRoot,
|
||||
failures,
|
||||
supplyReport.lockfileSha256,
|
||||
);
|
||||
if (buildManifest) {
|
||||
try {
|
||||
assertMatchesJsonSchema(
|
||||
@@ -423,6 +432,7 @@ export async function verifyArchivedLocalEvidence(input: Readonly<{
|
||||
async function validateSupportingArtifacts(
|
||||
repositoryRoot: string,
|
||||
failures: string[],
|
||||
lockfileSha256: string,
|
||||
): Promise<void> {
|
||||
let actualOutputs: Awaited<ReturnType<typeof collectDistOutputs>> | null =
|
||||
null;
|
||||
@@ -526,12 +536,10 @@ async function validateSupportingArtifacts(
|
||||
"local vulnerability report",
|
||||
failures,
|
||||
);
|
||||
if (
|
||||
vulnerability &&
|
||||
(vulnerability.status !== "FAIL_UNVERIFIED" ||
|
||||
vulnerability.provider !== "UNCONFIGURED")
|
||||
) {
|
||||
failures.push("local vulnerability report may not satisfy promotion");
|
||||
if (vulnerability) {
|
||||
failures.push(
|
||||
...compareStoredLocalVulnerabilityReport(lockfileSha256, vulnerability),
|
||||
);
|
||||
}
|
||||
const provenance = await parseArtifact(
|
||||
repositoryRoot,
|
||||
|
||||
Reference in New Issue
Block a user