Merge remote-tracking branch 'origin/master' into fix/1784-managed-repository-materialization
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const { createHash } = require("node:crypto");
|
||||
|
||||
function record(value) {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
||||
}
|
||||
@@ -37,12 +39,146 @@ function pipelineRunMatchesConsumer(consumerInput, itemInput) {
|
||||
const item = record(itemInput);
|
||||
const metadata = record(item.metadata);
|
||||
const labels = record(metadata.labels);
|
||||
const spec = record(item.spec);
|
||||
const pipelineRef = record(spec.pipelineRef);
|
||||
const name = stringOrNull(metadata.name) || "";
|
||||
const prefix = stringOrNull(consumer.pipelineRunPrefix) || "";
|
||||
const pipeline = stringOrNull(consumer.pipeline) || "";
|
||||
return (prefix.length > 0 && name.startsWith(prefix))
|
||||
|| (pipeline.length > 0 && labels["tekton.dev/pipeline"] === pipeline)
|
||||
|| (pipeline.length > 0 && labels["tekton.dev/pipelineName"] === pipeline);
|
||||
|| (pipeline.length > 0 && labels["tekton.dev/pipelineName"] === pipeline)
|
||||
|| (pipeline.length > 0 && pipelineRef.name === pipeline);
|
||||
}
|
||||
|
||||
function stableCanonicalJson(value) {
|
||||
if (Array.isArray(value)) return `[${value.map(stableCanonicalJson).join(",")}]`;
|
||||
if (typeof value === "object" && value !== null) {
|
||||
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableCanonicalJson(item)}`).join(",")}}`;
|
||||
}
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
function liveAdmissionSpecSha256(targetId, policyName, bindingName, policySpecInput, bindingSpecInput) {
|
||||
const policySpec = normalizeAdmissionPolicySpec(policySpecInput);
|
||||
const bindingSpec = normalizeAdmissionBindingSpec(bindingSpecInput);
|
||||
const normalized = {
|
||||
targetId,
|
||||
policyName,
|
||||
bindingName,
|
||||
policySpec,
|
||||
bindingSpec,
|
||||
};
|
||||
return `sha256:${createHash("sha256").update(stableCanonicalJson(normalized)).digest("hex")}`;
|
||||
}
|
||||
|
||||
function normalizeAdmissionPolicySpec(value) {
|
||||
const spec = { ...record(value) };
|
||||
if (Object.prototype.hasOwnProperty.call(spec, "matchConstraints")) {
|
||||
const matchConstraints = { ...record(spec.matchConstraints) };
|
||||
if (!Object.prototype.hasOwnProperty.call(matchConstraints, "matchPolicy")) matchConstraints.matchPolicy = "Equivalent";
|
||||
spec.matchConstraints = matchConstraints;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
function normalizeAdmissionBindingSpec(value) {
|
||||
const spec = { ...record(value) };
|
||||
if (Object.prototype.hasOwnProperty.call(spec, "matchResources")) {
|
||||
const matchResources = { ...record(spec.matchResources) };
|
||||
if (!Object.prototype.hasOwnProperty.call(matchResources, "matchPolicy")) matchResources.matchPolicy = "Equivalent";
|
||||
spec.matchResources = matchResources;
|
||||
}
|
||||
return spec;
|
||||
}
|
||||
|
||||
function evaluatePacAdmissionState(inputValue) {
|
||||
const input = record(inputValue);
|
||||
const policy = record(input.policy);
|
||||
const binding = record(input.binding);
|
||||
const role = record(input.role);
|
||||
const roleBinding = record(input.roleBinding);
|
||||
const expected = record(input.expected);
|
||||
const namespace = stringOrNull(input.namespace);
|
||||
const policyMetadata = record(policy.metadata);
|
||||
const bindingMetadata = record(binding.metadata);
|
||||
const roleMetadata = record(role.metadata);
|
||||
const roleBindingMetadata = record(roleBinding.metadata);
|
||||
const policyAnnotations = record(policyMetadata.annotations);
|
||||
const bindingAnnotations = record(bindingMetadata.annotations);
|
||||
const roleAnnotations = record(roleMetadata.annotations);
|
||||
const roleBindingAnnotations = record(roleBindingMetadata.annotations);
|
||||
const policySpec = record(policy.spec);
|
||||
const bindingSpec = record(binding.spec);
|
||||
const policyStatus = record(policy.status);
|
||||
const typeChecking = record(policyStatus.typeChecking);
|
||||
const warnings = Array.isArray(typeChecking.expressionWarnings) ? typeChecking.expressionWarnings : [];
|
||||
const reasons = [];
|
||||
const targetId = stringOrNull(expected.targetId);
|
||||
const liveSpecSha256 = targetId === null
|
||||
? null
|
||||
: liveAdmissionSpecSha256(targetId, expected.policyName, expected.bindingName, policySpec, bindingSpec);
|
||||
if (targetId === null) reasons.push("admission-target-id-missing");
|
||||
if (liveSpecSha256 !== expected.configSha256) reasons.push("admission-live-spec-sha256-mismatch");
|
||||
if (policyMetadata.name !== expected.policyName) reasons.push("policy-missing");
|
||||
if (bindingMetadata.name !== expected.bindingName) reasons.push("binding-missing");
|
||||
for (const [kind, annotations] of [["policy", policyAnnotations], ["binding", bindingAnnotations]]) {
|
||||
if (annotations["unidesk.ai/pac-provenance-version"] !== expected.version) reasons.push(`${kind}-version-mismatch`);
|
||||
if (annotations["unidesk.ai/pac-controller-identity"] !== expected.controllerIdentity) reasons.push(`${kind}-controller-identity-mismatch`);
|
||||
if (annotations["unidesk.ai/effective-config-sha256"] !== expected.configSha256) reasons.push(`${kind}-config-sha256-mismatch`);
|
||||
}
|
||||
if (policySpec.failurePolicy !== "Fail") reasons.push("policy-failure-policy-not-fail");
|
||||
if (bindingSpec.policyName !== expected.policyName) reasons.push("binding-policy-name-mismatch");
|
||||
if (!Array.isArray(bindingSpec.validationActions) || bindingSpec.validationActions.length !== 1 || bindingSpec.validationActions[0] !== "Deny") reasons.push("binding-validation-actions-not-deny");
|
||||
if (!Number.isInteger(policyMetadata.generation) || !Number.isInteger(policyStatus.observedGeneration) || policyStatus.observedGeneration < policyMetadata.generation) reasons.push("policy-generation-not-observed");
|
||||
if (warnings.length > 0) reasons.push("policy-expression-warning");
|
||||
if (roleMetadata.name !== "unidesk-pac-consumer-runner") reasons.push("default-role-missing");
|
||||
if (roleBindingMetadata.name !== "unidesk-pac-consumer-runner-default") reasons.push("default-rolebinding-missing");
|
||||
if (roleAnnotations["unidesk.ai/effective-config-sha256"] !== expected.rbacConfigSha256) reasons.push("default-role-config-sha256-mismatch");
|
||||
if (roleBindingAnnotations["unidesk.ai/effective-config-sha256"] !== expected.rbacConfigSha256) reasons.push("default-rolebinding-config-sha256-mismatch");
|
||||
const forbiddenVerbs = new Set(["create", "patch", "update", "delete", "deletecollection"]);
|
||||
const roleRules = Array.isArray(role.rules) ? role.rules : [];
|
||||
const roleHasMutation = roleRules.some((ruleValue) => {
|
||||
const rule = record(ruleValue);
|
||||
return (Array.isArray(rule.verbs) ? rule.verbs : []).some((verb) => forbiddenVerbs.has(verb));
|
||||
});
|
||||
if (roleHasMutation) reasons.push("default-role-declares-mutation");
|
||||
const expectedRoleRules = [
|
||||
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns", "taskruns"], verbs: ["get", "list", "watch"] },
|
||||
{ apiGroups: [""], resources: ["pods", "pods/log"], verbs: ["get", "list", "watch"] },
|
||||
];
|
||||
if (stableCanonicalJson(roleRules) !== stableCanonicalJson(expectedRoleRules)) reasons.push("default-role-rules-mismatch");
|
||||
const roleRef = record(roleBinding.roleRef);
|
||||
const subjects = Array.isArray(roleBinding.subjects) ? roleBinding.subjects.map(record) : [];
|
||||
if (roleRef.apiGroup !== "rbac.authorization.k8s.io" || roleRef.kind !== "Role" || roleRef.name !== "unidesk-pac-consumer-runner") reasons.push("default-rolebinding-role-ref-mismatch");
|
||||
if (namespace === null || subjects.length !== 1 || subjects[0].kind !== "ServiceAccount" || subjects[0].name !== "default" || subjects[0].namespace !== namespace) reasons.push("default-rolebinding-subject-mismatch");
|
||||
if (typeof input.defaultCanMutate !== "boolean") reasons.push("default-serviceaccount-mutation-probe-unavailable");
|
||||
if (input.defaultCanMutate === true) reasons.push("default-serviceaccount-can-mutate-tekton-runs");
|
||||
const timestamps = [policyMetadata.creationTimestamp, bindingMetadata.creationTimestamp]
|
||||
.filter((value) => typeof value === "string" && Number.isFinite(Date.parse(value)))
|
||||
.sort((left, right) => Date.parse(left) - Date.parse(right));
|
||||
const activeAfter = timestamps.length === 2 ? timestamps[1] : null;
|
||||
if (activeAfter === null) reasons.push("admission-creation-time-missing");
|
||||
return {
|
||||
configured: true,
|
||||
required: true,
|
||||
ready: reasons.length === 0,
|
||||
source: "live-admissionregistration-v1",
|
||||
policyName: stringOrNull(policyMetadata.name),
|
||||
bindingName: stringOrNull(bindingMetadata.name),
|
||||
version: stringOrNull(policyAnnotations["unidesk.ai/pac-provenance-version"]),
|
||||
controllerIdentity: stringOrNull(policyAnnotations["unidesk.ai/pac-controller-identity"]),
|
||||
configSha256: stringOrNull(policyAnnotations["unidesk.ai/effective-config-sha256"]),
|
||||
liveSpecSha256,
|
||||
policyUid: stringOrNull(policyMetadata.uid),
|
||||
bindingUid: stringOrNull(bindingMetadata.uid),
|
||||
policyGeneration: Number.isInteger(policyMetadata.generation) ? policyMetadata.generation : null,
|
||||
observedGeneration: Number.isInteger(policyStatus.observedGeneration) ? policyStatus.observedGeneration : null,
|
||||
rbacConfigSha256: stringOrNull(roleAnnotations["unidesk.ai/effective-config-sha256"]),
|
||||
defaultCanMutate: typeof input.defaultCanMutate === "boolean" ? input.defaultCanMutate : null,
|
||||
activeAfter,
|
||||
reasons,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function classifyPacPipelineRun(consumerInput, itemInput) {
|
||||
@@ -63,6 +199,8 @@ function classifyPacPipelineRun(consumerInput, itemInput) {
|
||||
const managedBy = stringOrNull(labels["app.kubernetes.io/managed-by"]);
|
||||
const provider = stringOrNull(annotations["pipelinesascode.tekton.dev/git-provider"]);
|
||||
const expectedRepository = stringOrNull(consumer.repository);
|
||||
const provenance = record(consumer.deliveryProvenance);
|
||||
const admissionState = record(consumer.admissionState);
|
||||
const outer = candidate
|
||||
&& eventType === "push"
|
||||
&& expectedRepository !== null
|
||||
@@ -75,6 +213,33 @@ function classifyPacPipelineRun(consumerInput, itemInput) {
|
||||
&& labels["unidesk.ai/ci-system"] === "tekton"
|
||||
&& annotations["unidesk.ai/source-authority"] === "gitea-snapshot"
|
||||
&& typeof annotations["unidesk.ai/publish-gitops"] === "string";
|
||||
const provenanceRequired = provenance.required === true;
|
||||
const markerAnnotation = stringOrNull(provenance.markerAnnotation);
|
||||
const markerValue = stringOrNull(provenance.markerValue);
|
||||
const executionServiceAccountName = stringOrNull(provenance.executionServiceAccountName);
|
||||
const taskRunTemplate = record(record(item.spec).taskRunTemplate);
|
||||
const createdAt = stringOrNull(metadata.creationTimestamp);
|
||||
const activeAfter = stringOrNull(admissionState.activeAfter);
|
||||
const createdAfterAdmission = createdAt !== null
|
||||
&& activeAfter !== null
|
||||
&& Number.isFinite(Date.parse(createdAt))
|
||||
&& Number.isFinite(Date.parse(activeAfter))
|
||||
&& Date.parse(createdAt) >= Date.parse(activeAfter);
|
||||
const admissionProvenanceVerified = outer
|
||||
&& provenanceRequired
|
||||
&& admissionState.ready === true
|
||||
&& admissionState.source === "live-admissionregistration-v1"
|
||||
&& admissionState.policyName === provenance.policyName
|
||||
&& admissionState.bindingName === provenance.bindingName
|
||||
&& admissionState.version === provenance.version
|
||||
&& admissionState.controllerIdentity === provenance.controllerIdentity
|
||||
&& admissionState.configSha256 === provenance.configSha256
|
||||
&& markerAnnotation !== null
|
||||
&& markerValue !== null
|
||||
&& annotations[markerAnnotation] === markerValue
|
||||
&& executionServiceAccountName !== null
|
||||
&& taskRunTemplate.serviceAccountName === executionServiceAccountName
|
||||
&& createdAfterAdmission;
|
||||
|
||||
const deliveryClass = outer
|
||||
? "outer-pac-event"
|
||||
@@ -82,7 +247,11 @@ function classifyPacPipelineRun(consumerInput, itemInput) {
|
||||
? "inner-deterministic-publish"
|
||||
: "unknown";
|
||||
const reason = outer
|
||||
? "observed-pac-push-metadata-matches-consumer"
|
||||
? admissionProvenanceVerified
|
||||
? "admission-owned-pac-push-provenance-verified"
|
||||
: provenanceRequired
|
||||
? "pac-push-metadata-observed-but-admission-provenance-unverified"
|
||||
: "observed-pac-push-metadata-matches-consumer"
|
||||
: inner
|
||||
? "deterministic-sentinel-publish-without-proven-parent"
|
||||
: candidate
|
||||
@@ -92,13 +261,19 @@ function classifyPacPipelineRun(consumerInput, itemInput) {
|
||||
deliveryClass,
|
||||
reason,
|
||||
candidate,
|
||||
deliveryAuthorityEligible: outer,
|
||||
deliveryOwner: outer ? "pac-controller-observed" : null,
|
||||
deliveryAuthorityEligible: outer && (!provenanceRequired || admissionProvenanceVerified),
|
||||
deliveryOwner: outer ? admissionProvenanceVerified ? "pac-controller-admission" : "pac-controller-observed" : null,
|
||||
executionOwner: inner ? "tekton-child-unattached" : null,
|
||||
parentPipelineRun: null,
|
||||
parentRelation: inner ? "unproven" : null,
|
||||
metadataObservationOnly: outer,
|
||||
admissionProvenanceVerified: false,
|
||||
metadataObservationOnly: outer && !admissionProvenanceVerified,
|
||||
admissionProvenanceVerified,
|
||||
admissionPolicyReady: admissionState.ready === true,
|
||||
admissionPolicyName: stringOrNull(admissionState.policyName),
|
||||
admissionBindingName: stringOrNull(admissionState.bindingName),
|
||||
admissionConfigSha256: stringOrNull(admissionState.configSha256),
|
||||
admissionActiveAfter: activeAfter,
|
||||
createdAfterAdmission,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -173,7 +348,7 @@ function terminalSource(task, marker, markerStatus = "skipped", markerSource = "
|
||||
|
||||
function extractPacSourceObservation(recordsInput) {
|
||||
const records = Array.isArray(recordsInput) ? recordsInput.map(record) : [];
|
||||
const plan = [...records].reverse().find((item) => item.event === "g14-ci-plan") || null;
|
||||
const plan = [...records].reverse().find((item) => item.event === "pac-delivery-plan" || item.event === "g14-ci-plan") || null;
|
||||
const collectMarker = exactSkipMarker(records, "collect-artifacts");
|
||||
const promoteMarker = exactSkipMarker(records, "gitops-promote");
|
||||
const planTask = exactTaskTerminal(records, "plan-artifacts");
|
||||
@@ -225,9 +400,14 @@ function extractPacSourceObservation(recordsInput) {
|
||||
reason = "delivery-terminal-evidence-incomplete";
|
||||
}
|
||||
|
||||
const planMarkerSource = plan?.event === "pac-delivery-plan"
|
||||
? "pac-delivery-plan-structured-log"
|
||||
: plan?.event === "g14-ci-plan"
|
||||
? "g14-ci-plan-structured-log"
|
||||
: "delivery-plan-structured-log";
|
||||
return {
|
||||
schemaVersion: "v1",
|
||||
contract: "hwlab-g14-ci-plan",
|
||||
contract: plan?.event === "pac-delivery-plan" ? "pac-delivery-plan-v1" : "hwlab-g14-ci-plan",
|
||||
mode,
|
||||
valid,
|
||||
reason,
|
||||
@@ -244,7 +424,7 @@ function extractPacSourceObservation(recordsInput) {
|
||||
gitopsPromote: promoteMarker !== null ? "no-build-no-rollout-plan" : promoteTask?.status || "missing",
|
||||
},
|
||||
terminalSources: {
|
||||
planArtifacts: terminalSource(planTask, plan, "observed", "g14-ci-plan-structured-log"),
|
||||
planArtifacts: terminalSource(planTask, plan, "observed", planMarkerSource),
|
||||
collectArtifacts: terminalSource(collectTask, collectMarker),
|
||||
gitopsPromote: terminalSource(promoteTask, promoteMarker),
|
||||
},
|
||||
@@ -721,6 +901,18 @@ function runPacStatusFixtureChecks() {
|
||||
expectedCode: "pac-ready-gitops-exact",
|
||||
input: fixtureInput({ artifact: { imageStatus: "built", digest: digestA, digests: [digestA], gitopsCommit: revision, sourceObservation: delivery, catalog: publishedCatalog() }, argo: exactArgo, runtime: { image: `registry/service@${digestA}`, digest: digestA, replicas: 1, readyReplicas: 1 } }),
|
||||
},
|
||||
{
|
||||
id: "delivery-ready-descendant",
|
||||
expectedOk: true,
|
||||
expectedCode: "pac-ready-gitops-descendant",
|
||||
input: fixtureInput({ artifact: { imageStatus: "built", digest: digestA, digests: [digestA], gitopsCommit: revision, sourceObservation: delivery, catalog: publishedCatalog() }, argo: { ...exactArgo, revision: "d".repeat(40), revisionRelation: { relation: "descendant", expectedRevision: revision, observedRevision: "d".repeat(40) } }, runtime: { image: `registry/service@${digestA}`, digest: digestA, replicas: 1, readyReplicas: 1 } }),
|
||||
},
|
||||
{
|
||||
id: "delivery-stale-gitops-fails-closed",
|
||||
expectedOk: false,
|
||||
expectedCode: "pac-argo-revision-stale",
|
||||
input: fixtureInput({ artifact: { imageStatus: "built", digest: digestA, digests: [digestA], gitopsCommit: revision, sourceObservation: delivery, catalog: publishedCatalog() }, argo: { ...exactArgo, revision: "d".repeat(40), revisionRelation: { relation: "stale", expectedRevision: revision, observedRevision: "d".repeat(40) } }, runtime: { image: `registry/service@${digestA}`, digest: digestA, replicas: 1, readyReplicas: 1 } }),
|
||||
},
|
||||
{
|
||||
id: "delivery-gitops-missing",
|
||||
expectedOk: false,
|
||||
@@ -802,11 +994,77 @@ function runPacStatusFixtureChecks() {
|
||||
actualCode: result.code,
|
||||
};
|
||||
});
|
||||
const provenance = {
|
||||
required: true,
|
||||
version: "admission-pac-v1",
|
||||
controllerIdentity: "system:serviceaccount:pipelines-as-code:pipelines-as-code-controller",
|
||||
policyName: "unidesk-pac-delivery-provenance-v1",
|
||||
bindingName: "unidesk-pac-delivery-provenance-v1",
|
||||
configSha256: `sha256:${"1".repeat(64)}`,
|
||||
markerAnnotation: "unidesk.ai/pac-admission-provenance",
|
||||
markerValue: "admission-pac-v1:fixture",
|
||||
executionServiceAccountName: "fixture-pac-runner",
|
||||
};
|
||||
const admissionState = {
|
||||
ready: true,
|
||||
source: "live-admissionregistration-v1",
|
||||
policyName: provenance.policyName,
|
||||
bindingName: provenance.bindingName,
|
||||
version: provenance.version,
|
||||
controllerIdentity: provenance.controllerIdentity,
|
||||
configSha256: provenance.configSha256,
|
||||
activeAfter: "2026-01-01T00:00:00Z",
|
||||
};
|
||||
const consumer = {
|
||||
id: "fixture",
|
||||
repository: "fixture-repository",
|
||||
pipeline: "fixture-pipeline",
|
||||
pipelineRunPrefix: "fixture-run-",
|
||||
deliveryProvenance: provenance,
|
||||
admissionState,
|
||||
};
|
||||
const verifiedRun = {
|
||||
metadata: {
|
||||
name: "fixture-run-verified",
|
||||
creationTimestamp: "2026-01-01T00:00:01Z",
|
||||
labels: {
|
||||
"app.kubernetes.io/managed-by": "pipelinesascode.tekton.dev",
|
||||
"pipelinesascode.tekton.dev/event-type": "push",
|
||||
"pipelinesascode.tekton.dev/repository": "fixture-repository",
|
||||
},
|
||||
annotations: {
|
||||
"pipelinesascode.tekton.dev/git-provider": "gitea",
|
||||
[provenance.markerAnnotation]: provenance.markerValue,
|
||||
},
|
||||
},
|
||||
spec: { taskRunTemplate: { serviceAccountName: provenance.executionServiceAccountName } },
|
||||
};
|
||||
const classificationCases = [
|
||||
{ id: "admission-provenance-verified", expected: true, item: verifiedRun, state: admissionState },
|
||||
{ id: "pre-bootstrap-run-fails-closed", expected: false, item: { ...verifiedRun, metadata: { ...verifiedRun.metadata, creationTimestamp: "2025-12-31T23:59:59Z" } }, state: admissionState },
|
||||
{ id: "forged-name-candidate-fails-closed", expected: false, item: { ...verifiedRun, metadata: { ...verifiedRun.metadata, annotations: { ...verifiedRun.metadata.annotations, [provenance.markerAnnotation]: "forged" } } }, state: admissionState },
|
||||
{ id: "forged-label-candidate-fails-closed", expected: false, item: { ...verifiedRun, metadata: { ...verifiedRun.metadata, name: "unrelated", labels: { ...verifiedRun.metadata.labels, "tekton.dev/pipeline": consumer.pipeline }, annotations: { ...verifiedRun.metadata.annotations, [provenance.markerAnnotation]: "forged" } } }, state: admissionState },
|
||||
{ id: "forged-pipeline-ref-candidate-fails-closed", expected: false, item: { ...verifiedRun, metadata: { ...verifiedRun.metadata, name: "unrelated", annotations: { ...verifiedRun.metadata.annotations, [provenance.markerAnnotation]: "forged" } }, spec: { ...verifiedRun.spec, pipelineRef: { name: consumer.pipeline } } }, state: admissionState },
|
||||
{ id: "wrong-service-account-fails-closed", expected: false, item: { ...verifiedRun, spec: { taskRunTemplate: { serviceAccountName: "default" } } }, state: admissionState },
|
||||
{ id: "policy-identity-mismatch-fails-closed", expected: false, item: verifiedRun, state: { ...admissionState, configSha256: `sha256:${"2".repeat(64)}` } },
|
||||
];
|
||||
for (const item of classificationCases) {
|
||||
const result = classifyPacPipelineRun({ ...consumer, admissionState: item.state }, item.item);
|
||||
checks.push({
|
||||
id: item.id,
|
||||
ok: result.admissionProvenanceVerified === item.expected && result.deliveryAuthorityEligible === item.expected,
|
||||
expectedOk: item.expected,
|
||||
actualOk: result.deliveryAuthorityEligible,
|
||||
expectedCode: item.expected ? "admission-owned-pac-push-provenance-verified" : "pac-push-metadata-observed-but-admission-provenance-unverified",
|
||||
actualCode: result.reason,
|
||||
});
|
||||
}
|
||||
return { ok: checks.every((item) => item.ok), checks, valuesPrinted: false };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
classifyPacPipelineRun,
|
||||
evaluatePacAdmissionState,
|
||||
evaluatePacStatus,
|
||||
extractPacArtifactEvidence,
|
||||
extractPacSourceObservation,
|
||||
|
||||
Reference in New Issue
Block a user