fix: enforce exact local evidence defaults
This commit is contained in:
@@ -25,7 +25,9 @@ import {
|
||||
verifySupplyChainCoherence,
|
||||
} from "./lib/supply-chain.ts";
|
||||
import {
|
||||
createLocalVulnerabilityReport,
|
||||
distChecksumsText,
|
||||
LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS,
|
||||
recomputeDependencyEvidence,
|
||||
recomputeLicenseEvidence,
|
||||
} from "./lib/local-policy-evidence.ts";
|
||||
@@ -186,16 +188,9 @@ const licenseEvidence = recomputeLicenseEvidence({
|
||||
policy: licensePolicy,
|
||||
});
|
||||
|
||||
const vulnerabilityReport = {
|
||||
schemaVersion: 1,
|
||||
provider: "UNCONFIGURED",
|
||||
scannedLockfileSha256: inventory.lockfileSha256,
|
||||
status: "FAIL_UNVERIFIED",
|
||||
findings: [],
|
||||
exceptionsApplied: [],
|
||||
failures: ["external vulnerability provider report is missing"],
|
||||
blocking: [],
|
||||
};
|
||||
const vulnerabilityReport = createLocalVulnerabilityReport(
|
||||
inventory.lockfileSha256,
|
||||
);
|
||||
|
||||
const sourceFiles = [...repositoryInventory.trackedFiles];
|
||||
const sourceSetSha256 = await digestReleaseInputFiles(sourceFiles);
|
||||
@@ -301,15 +296,13 @@ const localPassed = localFailures.length === 0;
|
||||
const verification = {
|
||||
schemaVersion: 1,
|
||||
localStatus: localPassed ? "PASS" : "FAIL",
|
||||
promotionStatus: "FAIL_UNVERIFIED",
|
||||
...LOCAL_SUPPLY_CHAIN_UNVERIFIED_DEFAULTS,
|
||||
lockfileSha256: inventory.lockfileSha256,
|
||||
sourceSetSha256,
|
||||
distSha256: distDigest,
|
||||
sbomSha256: supplyChainDigest(sbom),
|
||||
dependencyDiff: dependencyPolicy.dependencyDiff,
|
||||
highRiskReview: dependencyPolicy.highRisk,
|
||||
vulnerabilityStatus: vulnerabilityReport.status,
|
||||
provenanceAttestationStatus: "FAIL_UNVERIFIED",
|
||||
failures: localFailures,
|
||||
};
|
||||
const bundleReport = {
|
||||
|
||||
@@ -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