426 lines
11 KiB
TypeScript
426 lines
11 KiB
TypeScript
import { z } from "zod";
|
|
|
|
export * from "../../src/contracts/release-artifacts.ts";
|
|
|
|
const nonEmptyString = z.string().min(1);
|
|
const timestamp = z.iso.datetime();
|
|
const sha256 = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
const jsonObject = z.record(z.string(), z.json());
|
|
|
|
export const moduleInventoryArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
chunks: z.array(
|
|
z
|
|
.object({
|
|
fileName: nonEmptyString,
|
|
modules: z.array(nonEmptyString),
|
|
})
|
|
.strict(),
|
|
),
|
|
})
|
|
.strict();
|
|
|
|
export const jsonSchemaDocumentArtifactSchema = z
|
|
.object({
|
|
$schema: z.literal("https://json-schema.org/draft/2020-12/schema"),
|
|
})
|
|
.catchall(z.json());
|
|
|
|
const dependencyInventoryRowSchema = z
|
|
.object({
|
|
name: nonEmptyString,
|
|
version: nonEmptyString,
|
|
direct: z.boolean(),
|
|
scope: z.enum(["production", "development"]),
|
|
optional: z.boolean(),
|
|
license: nonEmptyString,
|
|
integrity: z.string().regex(/^sha512-/u),
|
|
dependencies: z.array(nonEmptyString),
|
|
})
|
|
.strict();
|
|
|
|
export const dependencyInventoryArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(2),
|
|
packageManager: nonEmptyString,
|
|
lockfileSha256: sha256,
|
|
dependencyCount: z.int().nonnegative(),
|
|
directDependencyCount: z.int().nonnegative(),
|
|
dependencies: z.array(dependencyInventoryRowSchema),
|
|
})
|
|
.strict()
|
|
.superRefine((inventory, context) => {
|
|
if (inventory.dependencyCount !== inventory.dependencies.length) {
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["dependencyCount"],
|
|
message: "must equal dependencies.length",
|
|
});
|
|
}
|
|
const actualDirect = inventory.dependencies.filter(
|
|
(dependency) => dependency.direct,
|
|
).length;
|
|
if (inventory.directDependencyCount !== actualDirect) {
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["directDependencyCount"],
|
|
message: "must equal the number of direct dependencies",
|
|
});
|
|
}
|
|
});
|
|
|
|
const dependencyUpgradeSchema = z
|
|
.object({
|
|
name: nonEmptyString,
|
|
from: nonEmptyString,
|
|
to: nonEmptyString,
|
|
})
|
|
.strict();
|
|
|
|
export const dependencyDiffSchema = z
|
|
.object({
|
|
added: z.array(nonEmptyString),
|
|
removed: z.array(nonEmptyString),
|
|
changed: z.array(nonEmptyString),
|
|
upgrades: z.array(dependencyUpgradeSchema),
|
|
})
|
|
.strict();
|
|
|
|
export const supplyChainVerificationArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
localStatus: z.enum(["PASS", "FAIL"]),
|
|
promotionStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]),
|
|
lockfileSha256: sha256,
|
|
sourceSetSha256: sha256,
|
|
distSha256: sha256,
|
|
sbomSha256: sha256,
|
|
dependencyDiff: dependencyDiffSchema,
|
|
highRiskReview: z.array(nonEmptyString),
|
|
vulnerabilityStatus: z.enum(["PASS", "FAIL", "FAIL_UNVERIFIED"]),
|
|
provenanceAttestationStatus: z.enum(["PASS", "FAIL_UNVERIFIED"]),
|
|
failures: z.array(z.string()),
|
|
})
|
|
.strict();
|
|
|
|
const registryChangeSchema = z
|
|
.object({
|
|
changeId: nonEmptyString,
|
|
registryId: nonEmptyString,
|
|
rowName: nonEmptyString,
|
|
field: nonEmptyString,
|
|
kind: nonEmptyString,
|
|
impact: z.enum(["none", "additive", "behavior-change", "breaking"]),
|
|
before: z.json().optional(),
|
|
after: z.json().optional(),
|
|
})
|
|
.strict();
|
|
|
|
const registryArtifactRowSchema = z
|
|
.object({
|
|
registryId: nonEmptyString,
|
|
owner: nonEmptyString,
|
|
source: nonEmptyString,
|
|
rowCount: z.int().nonnegative(),
|
|
contract: jsonObject,
|
|
rows: jsonObject,
|
|
})
|
|
.strict();
|
|
|
|
export const registrySnapshotArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(2),
|
|
generatedAt: timestamp,
|
|
baselineDigest: sha256.nullable(),
|
|
currentDigest: sha256,
|
|
compatibility: z
|
|
.object({
|
|
impact: z.enum([
|
|
"not-evaluated",
|
|
"none",
|
|
"additive",
|
|
"behavior-change",
|
|
"breaking",
|
|
]),
|
|
changes: z.array(registryChangeSchema),
|
|
})
|
|
.strict(),
|
|
failures: z.array(z.string()),
|
|
registries: z.array(registryArtifactRowSchema),
|
|
})
|
|
.strict();
|
|
|
|
const outputDigestSchema = z
|
|
.object({
|
|
path: nonEmptyString,
|
|
bytes: z.int().nonnegative(),
|
|
gzipBytes: z.int().nonnegative(),
|
|
sha256,
|
|
})
|
|
.strict();
|
|
|
|
export const bundlePerformanceArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
generatedAt: timestamp,
|
|
context: z
|
|
.object({
|
|
nodeVersion: nonEmptyString,
|
|
packageManager: nonEmptyString,
|
|
runnerImage: nonEmptyString,
|
|
})
|
|
.strict(),
|
|
outputs: z.array(outputDigestSchema).min(1),
|
|
})
|
|
.strict();
|
|
|
|
const cyclonedxComponentSchema = z
|
|
.object({
|
|
type: z.literal("library"),
|
|
"bom-ref": nonEmptyString,
|
|
name: nonEmptyString,
|
|
version: nonEmptyString,
|
|
scope: z.enum(["optional", "required"]),
|
|
hashes: z.array(
|
|
z.object({ alg: z.literal("SHA-512"), content: nonEmptyString }).strict(),
|
|
),
|
|
licenses: z.array(
|
|
z.object({ expression: nonEmptyString }).strict(),
|
|
),
|
|
properties: z.array(
|
|
z.object({ name: nonEmptyString, value: nonEmptyString }).strict(),
|
|
),
|
|
})
|
|
.strict();
|
|
|
|
export const sbomArtifactSchema = z
|
|
.object({
|
|
bomFormat: z.literal("CycloneDX"),
|
|
specVersion: z.literal("1.6"),
|
|
serialNumber: nonEmptyString,
|
|
version: z.literal(1),
|
|
metadata: z
|
|
.object({
|
|
component: z
|
|
.object({
|
|
type: z.literal("application"),
|
|
name: nonEmptyString,
|
|
version: nonEmptyString,
|
|
})
|
|
.strict(),
|
|
properties: z.array(
|
|
z.object({ name: nonEmptyString, value: nonEmptyString }).strict(),
|
|
),
|
|
})
|
|
.strict(),
|
|
components: z.array(cyclonedxComponentSchema),
|
|
dependencies: z.array(
|
|
z
|
|
.object({ ref: nonEmptyString, dependsOn: z.array(nonEmptyString) })
|
|
.strict(),
|
|
),
|
|
})
|
|
.strict();
|
|
|
|
export const provenanceArtifactSchema = z
|
|
.object({
|
|
_type: z.literal("https://in-toto.io/Statement/v1"),
|
|
subject: z
|
|
.array(
|
|
z
|
|
.object({
|
|
name: z.literal("dist"),
|
|
digest: z.object({ sha256 }).strict(),
|
|
})
|
|
.strict(),
|
|
)
|
|
.length(1),
|
|
predicateType: z.literal("https://slsa.dev/provenance/v1"),
|
|
predicate: z
|
|
.object({
|
|
buildDefinition: z
|
|
.object({
|
|
buildType: nonEmptyString,
|
|
externalParameters: jsonObject,
|
|
internalParameters: jsonObject,
|
|
resolvedDependencies: z.array(
|
|
z
|
|
.object({ uri: nonEmptyString, digest: z.object({ sha256 }).strict() })
|
|
.strict(),
|
|
),
|
|
})
|
|
.strict(),
|
|
runDetails: z
|
|
.object({
|
|
builder: z.object({ id: nonEmptyString }).strict(),
|
|
metadata: z.object({ invocationId: nonEmptyString }).strict(),
|
|
})
|
|
.strict(),
|
|
materials: z
|
|
.object({ lockfileSha256: sha256, sourceSetSha256: sha256, sbomSha256: sha256 })
|
|
.strict(),
|
|
})
|
|
.strict(),
|
|
})
|
|
.strict();
|
|
|
|
export const dependencyDiffArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(2),
|
|
baselineDigest: sha256.nullable(),
|
|
currentDigest: sha256,
|
|
...dependencyDiffSchema.shape,
|
|
highRisk: z.array(nonEmptyString),
|
|
reviewFailures: z.array(z.string()),
|
|
})
|
|
.strict();
|
|
|
|
export const licenseReportArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
status: z.enum(["PASS", "FAIL"]),
|
|
dependencyCount: z.int().nonnegative(),
|
|
results: z.array(
|
|
z
|
|
.object({
|
|
package: nonEmptyString,
|
|
license: nonEmptyString,
|
|
passed: z.boolean(),
|
|
reason: z.string().nullable(),
|
|
})
|
|
.strict(),
|
|
),
|
|
failures: z.array(z.string()),
|
|
})
|
|
.strict();
|
|
|
|
export const vulnerabilityReportArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
provider: nonEmptyString,
|
|
scannedLockfileSha256: sha256,
|
|
status: z.enum(["PASS", "FAIL", "FAIL_UNVERIFIED"]),
|
|
findings: z.array(jsonObject),
|
|
exceptionsApplied: z.array(jsonObject),
|
|
failures: z.array(z.string()),
|
|
blocking: z.array(z.string()),
|
|
})
|
|
.strict();
|
|
|
|
export const fieldWebVitalsArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
generatedAt: timestamp,
|
|
window: z
|
|
.object({ days: z.literal(28), start: timestamp, end: timestamp })
|
|
.strict(),
|
|
context: z
|
|
.object({
|
|
source: nonEmptyString,
|
|
sourceSystem: z.string().nullable(),
|
|
exportId: z.string().nullable(),
|
|
network: z.literal("production-real-user"),
|
|
routeAggregation: z.literal("route-id-only"),
|
|
releaseId: z.string().nullable(),
|
|
privacyApprovalRef: z.string().nullable(),
|
|
thresholdDecisionRef: z.string().nullable(),
|
|
validationFailures: z.array(z.string()),
|
|
})
|
|
.strict(),
|
|
metrics: z
|
|
.object({
|
|
p75LcpMs: z.number().finite().nonnegative().nullable(),
|
|
p75Cls: z.number().finite().nonnegative().nullable(),
|
|
p75InpMs: z.number().finite().nonnegative().nullable(),
|
|
})
|
|
.strict(),
|
|
thresholds: z
|
|
.object({
|
|
p75LcpMs: z.number().finite().nonnegative(),
|
|
p75Cls: z.number().finite().nonnegative(),
|
|
p75InpMs: z.number().finite().nonnegative(),
|
|
minimumEligibleSamples: z.int().positive().nullable(),
|
|
})
|
|
.strict(),
|
|
eligibility: z
|
|
.object({
|
|
consentRequired: z.literal(true),
|
|
totalSamples: z.int().nonnegative(),
|
|
eligibleSamples: z.int().nonnegative(),
|
|
minimumEligibleSamples: z.int().positive().nullable(),
|
|
routeSamples: z.record(z.string(), z.int().nonnegative()),
|
|
})
|
|
.strict(),
|
|
status: z.enum(["PASS", "FAIL_THRESHOLD", "FAIL_UNVERIFIED"]),
|
|
passed: z.boolean(),
|
|
})
|
|
.strict();
|
|
|
|
export const labPerformanceArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
generatedAt: timestamp,
|
|
context: jsonObject,
|
|
metrics: jsonObject,
|
|
thresholds: jsonObject,
|
|
fixtures: z.array(
|
|
z.object({ name: nonEmptyString, passed: z.boolean() }).strict(),
|
|
),
|
|
passed: z.boolean(),
|
|
})
|
|
.strict();
|
|
|
|
export const releaseVerificationArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
generatedAt: timestamp,
|
|
artifact: z
|
|
.object({
|
|
checked: z.boolean(),
|
|
compatible: z.boolean(),
|
|
mismatches: z.array(z.string()),
|
|
releaseId: nonEmptyString,
|
|
})
|
|
.strict(),
|
|
fixtures: z.array(
|
|
z
|
|
.object({
|
|
name: nonEmptyString,
|
|
expectedCompatible: z.boolean(),
|
|
actualCompatible: z.boolean(),
|
|
mismatches: z.array(z.string()),
|
|
passed: z.boolean(),
|
|
})
|
|
.strict(),
|
|
),
|
|
passed: z.boolean(),
|
|
})
|
|
.strict();
|
|
|
|
export const runbookRecordArtifactSchema = z
|
|
.object({
|
|
schemaVersion: z.literal(1),
|
|
runbookId: z.string().regex(/^FE-RB-00[1-5]$/u),
|
|
releaseId: nonEmptyString,
|
|
drillTimestamp: timestamp,
|
|
triggerInjected: nonEmptyString,
|
|
triggerAsserted: z.boolean(),
|
|
containmentAsserted: z.boolean(),
|
|
escalationPathAsserted: z.boolean(),
|
|
recoveryAssertions: z.array(
|
|
z
|
|
.object({
|
|
assertion: nonEmptyString,
|
|
evidence: nonEmptyString,
|
|
passed: z.boolean(),
|
|
})
|
|
.strict(),
|
|
),
|
|
negativeFixtureFailedAsExpected: z.boolean(),
|
|
windowObservedBucket: nonEmptyString,
|
|
providerVerificationRequired: z.boolean(),
|
|
passed: z.boolean(),
|
|
})
|
|
.strict();
|