@xemahq/xema 0.40.3 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/edition.d.ts.map +1 -1
- package/dist/commands/edition.js +1 -0
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +289 -19
- package/dist/edition/verify-image-evidence.bundle.provenance.json +71 -47
- package/dist/runtime/edition/verb-map.d.ts +3 -1
- package/dist/runtime/edition/verb-map.d.ts.map +1 -1
- package/dist/runtime/edition/verb-map.js +4 -0
- package/dist/runtime/edition/verb-map.js.map +1 -1
- package/edition/README.md +5 -0
- package/edition/catalogue-evidence.mjs +225 -0
- package/edition/catalogue-fetch.mjs +40 -7
- package/edition/customer-edition-verbs.mjs +477 -0
- package/edition/customer-flags.mjs +2 -0
- package/edition/develop-coverage-readers.mjs +212 -0
- package/edition/develop-coverage-run.mjs +48 -0
- package/edition/develop-coverage-verdict.mjs +153 -0
- package/edition/develop-coverage.mjs +120 -0
- package/edition/develop-edition-resolve.mjs +18 -7
- package/edition/develop-edition.mjs +47 -220
- package/edition/develop-freeze.mjs +50 -13
- package/edition/develop-image-probe.mjs +7 -7
- package/edition/dispatched-runtime-components.mjs +90 -1
- package/edition/edition-artifact.mjs +31 -14
- package/edition/edition-constants.mjs +9 -0
- package/edition/edition-contract.json +2 -1
- package/edition/{develop-seal.mjs → edition-image-seal.mjs} +36 -17
- package/edition/edition-workspace.mjs +4 -3
- package/edition/evidence-trust-pin.mjs +28 -8
- package/edition/evidence-trust-policy.mjs +51 -7
- package/edition/image-evidence.mjs +2 -2
- package/edition/lib.mjs +20 -11
- package/edition/oci-image-source-evidence.mjs +3 -0
- package/edition/oras-process.mjs +3 -3
- package/edition/oras.mjs +47 -5
- package/edition/produce-edition-catalogue-evidence.mjs +252 -0
- package/edition/produce-image-source-evidence.mjs +11 -2
- package/edition/release-edition.mjs +54 -0
- package/edition/repin-evidence-trust.mjs +5 -3
- package/edition/resolve-images.mjs +40 -5
- package/edition/sigstore-bundle-media-type.mjs +4 -0
- package/edition/trust-policy-arming.mjs +14 -1
- package/edition/verify-catalogue-evidence.mjs +245 -0
- package/edition/verify-image-evidence.mjs +15 -1
- package/package.json +2 -2
- package/edition/develop-customer-edition.mjs +0 -149
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edition.d.ts","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"edition.d.ts","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2CzC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiBtD"}
|
package/dist/commands/edition.js
CHANGED
|
@@ -8,6 +8,7 @@ const VERB_SUMMARY = {
|
|
|
8
8
|
[verb_map_1.EditionVerb.Resolve]: 'Resolve the whole release lock set of an edition: xema edition resolve <id> [--check]; or a customer edition against the catalogue pinned in catalogue.pin.json beside the definition: xema edition resolve --definition <file> --channel <release|develop> --out <dir> [--source-image <source>=<registry>/<prefix>]...',
|
|
9
9
|
[verb_map_1.EditionVerb.Seal]: 'Seal the image digests of an edition: xema edition seal <id> [--definition <file>] (a customer edition seals against the release catalogue pinned in catalogue.pin.json beside the definition)',
|
|
10
10
|
[verb_map_1.EditionVerb.Develop]: 'Resolve a develop edition snapshot: xema edition develop <resolve|target|freeze-graph|freeze-sources|check-existing|check-overrides> ...; or a customer develop edition (catalogue pinned in catalogue.pin.json beside the definition): xema edition develop <resolve|target|check-existing|freeze-sources> --definition <file> ... (resolve: --pins <file> --out <dir> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...; target: --pins <file> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image ...]; check-existing: --pins <file> --artifact <dir> --engine-image <repo>@sha256:<digest> [--source-image ...]). The develop tag is sha256 of the CLI version, the engine image, the pins and the source-image map.',
|
|
11
|
+
[verb_map_1.EditionVerb.Release]: 'Resolve a customer release edition (catalogue pinned for release in catalogue.pin.json beside the definition): xema edition release <freeze-sources|target|check-existing|resolve> --definition <file> ... (freeze-sources: --out <pins.json>, each source at its declared ref; resolve: --pins <file> --out <dir> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image <source>=<registry>/<prefix>]...; target: --pins <file> --registry <root> --engine-image <repo>@sha256:<digest> [--source-image ...]; resolve and check-existing are refused until the release catalogue evidence is verified)',
|
|
11
12
|
[verb_map_1.EditionVerb.Artifact]: 'Write the OCI artifact files of a release edition from the committed sealed locks: xema edition artifact <id> --channel release --registry <root> --coordinate <tag> --engine-image <repo>@sha256:<digest> --out <dir>',
|
|
12
13
|
[verb_map_1.EditionVerb.Catalogue]: 'Write the catalogue.json of a release offering from its committed sealed locks: xema edition catalogue <id> --out <dir>',
|
|
13
14
|
[verb_map_1.EditionVerb.Index]: `Rebuild a catalogue index: xema edition index <${Object.values(verb_map_1.EditionIndexKind).join('|')}> [--check]; xema edition index runtime-image-source --repin-contract-authority --kernel-commit <40-hex> replaces only the contract authority of the committed index with the kernel checkout's at the stated commit (its HEAD must equal it)`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"edition.js","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"edition.js","sourceRoot":"","sources":["../../src/commands/edition.ts"],"names":[],"mappings":";;AA+CA,0CAiBC;AAhED,kDAA8D;AAC9D,4DAA8D;AAC9D,0DAA2H;AAW3H,MAAM,YAAY,GAA0C;IAC1D,CAAC,sBAAW,CAAC,OAAO,CAAC,EACnB,0TAA0T;IAC5T,CAAC,sBAAW,CAAC,IAAI,CAAC,EAAE,gMAAgM;IACpN,CAAC,sBAAW,CAAC,OAAO,CAAC,EAAE,swBAAswB;IAC7xB,CAAC,sBAAW,CAAC,OAAO,CAAC,EACnB,2lBAA2lB;IAC7lB,CAAC,sBAAW,CAAC,QAAQ,CAAC,EACpB,wNAAwN;IAC1N,CAAC,sBAAW,CAAC,SAAS,CAAC,EAAE,yHAAyH;IAClJ,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,kDAAkD,MAAM,CAAC,MAAM,CAAC,2BAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,+OAA+O;IAC/V,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,oIAAoI;IACzJ,CAAC,sBAAW,CAAC,GAAG,CAAC,EAAE,qEAAqE;IACxF,CAAC,sBAAW,CAAC,eAAe,CAAC,EAAE,qGAAqG;IACpI,CAAC,sBAAW,CAAC,eAAe,CAAC,EAAE,wEAAwE;IACvG,CAAC,sBAAW,CAAC,kBAAkB,CAAC,EAAE,mIAAmI;IACrK,CAAC,sBAAW,CAAC,cAAc,CAAC,EAC1B,yQAAyQ;IAC3Q,CAAC,sBAAW,CAAC,QAAQ,CAAC,EAAE,+EAA+E,MAAM,CAAC,MAAM,CAAC,8BAAmB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK;IACxJ,CAAC,sBAAW,CAAC,IAAI,CAAC,EAAE,6FAA6F;IACjH,CAAC,sBAAW,CAAC,KAAK,CAAC,EAAE,6JAA6J;IAClL,CAAC,sBAAW,CAAC,QAAQ,CAAC,EAAE,gGAAgG,MAAM,CAAC,MAAM,CAAC,mCAAwB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,qKAAqK;IAC9U,CAAC,sBAAW,CAAC,cAAc,CAAC,EAC1B,6LAA6L;IAC/L,CAAC,sBAAW,CAAC,MAAM,CAAC,EAClB,yMAAyM;IAC3M,CAAC,sBAAW,CAAC,GAAG,CAAC,EACf,mWAAmW;IACrW,CAAC,sBAAW,CAAC,KAAK,CAAC,EACjB,qMAAqM;IACvM,CAAC,sBAAW,CAAC,aAAa,CAAC,EACzB,uMAAuM;CAC1M,CAAC;AAEF,SAAgB,eAAe,CAAC,OAAgB;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACvF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,sBAAW,CAAC,EAAE,CAAC;QAC9C,OAAO;aACJ,OAAO,CAAC,IAAI,CAAC;aACb,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;aAC/B,QAAQ,CAAC,WAAW,EAAE,0DAA0D,CAAC;aACjF,kBAAkB,EAAE;aACpB,MAAM,CAAC,CAAC,IAAc,EAAE,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAA,0BAAc,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAChD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAA,qBAAQ,EAAC,4BAAe,CAAC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjG,CAAC;QACH,CAAC,CAAC,CAAC;IACP,CAAC;AACH,CAAC"}
|
|
@@ -62184,7 +62184,8 @@ var edition_contract_default = {
|
|
|
62184
62184
|
catalogue: "catalogue.json"
|
|
62185
62185
|
},
|
|
62186
62186
|
provenancePins: {
|
|
62187
|
-
catalogue: "the sha256 digest (sha256:<64 hex>) of the offering artifact a customer edition was resolved against
|
|
62187
|
+
catalogue: "the sha256 digest (sha256:<64 hex>) of the offering artifact a customer edition was resolved against",
|
|
62188
|
+
definition: "the sha256 digest (sha256:<64 hex>) of the canonical JSON of the customer definition a customer edition was resolved from (vendor selection, image registry mirror, Store publisher anchors, sources); every other pin is a 40-hex commit"
|
|
62188
62189
|
},
|
|
62189
62190
|
imageNamespaceSegment: "dev",
|
|
62190
62191
|
evidenceTrust: {
|
|
@@ -62215,6 +62216,11 @@ var CATALOGUE_PIN_KEY = "catalogue";
|
|
|
62215
62216
|
if (!(CATALOGUE_PIN_KEY in EDITION_CONTRACT.provenancePins)) {
|
|
62216
62217
|
throw new Error("edition-contract.json provenancePins does not declare the catalogue pin");
|
|
62217
62218
|
}
|
|
62219
|
+
var DEFINITION_PIN_KEY = "definition";
|
|
62220
|
+
if (!(DEFINITION_PIN_KEY in EDITION_CONTRACT.provenancePins)) {
|
|
62221
|
+
throw new Error("edition-contract.json provenancePins does not declare the definition pin");
|
|
62222
|
+
}
|
|
62223
|
+
var DIGEST_PIN_KEYS = Object.freeze([CATALOGUE_PIN_KEY, DEFINITION_PIN_KEY]);
|
|
62218
62224
|
var EditionLockFile = Object.freeze({
|
|
62219
62225
|
Distribution: "distribution.lock.json",
|
|
62220
62226
|
Images: "images.lock.json",
|
|
@@ -62918,6 +62924,14 @@ function resetEditionWorkspaceConfig() {
|
|
|
62918
62924
|
cached = void 0;
|
|
62919
62925
|
}
|
|
62920
62926
|
|
|
62927
|
+
// edition/string-order.mjs
|
|
62928
|
+
function compareStrings(a, b) {
|
|
62929
|
+
if (a === b) {
|
|
62930
|
+
return 0;
|
|
62931
|
+
}
|
|
62932
|
+
return a < b ? -1 : 1;
|
|
62933
|
+
}
|
|
62934
|
+
|
|
62921
62935
|
// edition/evidence-grammars.mjs
|
|
62922
62936
|
var COMMIT_PATTERN = /^[a-f0-9]{40}$/;
|
|
62923
62937
|
var SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
|
|
@@ -62933,16 +62947,6 @@ var requireFromHere2 = createRequire2(import.meta.url);
|
|
|
62933
62947
|
|
|
62934
62948
|
// edition/operation-requirements.mjs
|
|
62935
62949
|
import { createRequire as createRequire3 } from "node:module";
|
|
62936
|
-
|
|
62937
|
-
// edition/string-order.mjs
|
|
62938
|
-
function compareStrings(a, b) {
|
|
62939
|
-
if (a === b) {
|
|
62940
|
-
return 0;
|
|
62941
|
-
}
|
|
62942
|
-
return a < b ? -1 : 1;
|
|
62943
|
-
}
|
|
62944
|
-
|
|
62945
|
-
// edition/operation-requirements.mjs
|
|
62946
62950
|
var requireFromHere3 = createRequire3(import.meta.url);
|
|
62947
62951
|
var PRINCIPAL_PRIVILEGE_FIELDS = Object.freeze(["requiresOperations", "optionalOperations", "requiresOrganizationSweeps"]);
|
|
62948
62952
|
|
|
@@ -63107,7 +63111,7 @@ function assertEngineImage(image) {
|
|
|
63107
63111
|
return image;
|
|
63108
63112
|
}
|
|
63109
63113
|
var COMMIT_PATTERN2 = /^[0-9a-f]{40}$/;
|
|
63110
|
-
var
|
|
63114
|
+
var DIGEST_PIN_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
63111
63115
|
var exactKeys = (value, keys, where) => {
|
|
63112
63116
|
if (value === null || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== [...keys].sort().join()) {
|
|
63113
63117
|
throw new Error(`${where} must be exactly {${keys.join(", ")}}`);
|
|
@@ -63128,13 +63132,14 @@ function parseEditionDescriptor(value) {
|
|
|
63128
63132
|
}
|
|
63129
63133
|
const { pins, workflow } = value.provenance;
|
|
63130
63134
|
if (pins === null || typeof pins !== "object" || Array.isArray(pins)) {
|
|
63131
|
-
throw new Error(`${EDITION_DESCRIPTOR_FILE}: provenance.pins must map pin keys to 40-hex commits (the "${
|
|
63135
|
+
throw new Error(`${EDITION_DESCRIPTOR_FILE}: provenance.pins must map pin keys to 40-hex commits (the ${DIGEST_PIN_KEYS.map((key) => `"${key}"`).join(" and ")} pins to sha256 digests)`);
|
|
63132
63136
|
}
|
|
63133
63137
|
for (const [key, pin] of Object.entries(pins)) {
|
|
63134
|
-
const
|
|
63138
|
+
const digestPin = DIGEST_PIN_KEYS.includes(key);
|
|
63139
|
+
const pattern = digestPin ? DIGEST_PIN_PATTERN : COMMIT_PATTERN2;
|
|
63135
63140
|
if (typeof pin !== "string" || !pattern.test(pin)) {
|
|
63136
63141
|
throw new Error(
|
|
63137
|
-
|
|
63142
|
+
digestPin ? `${EDITION_DESCRIPTOR_FILE}: provenance.pins["${key}"] must be a sha256:<64 hex> digest` : `${EDITION_DESCRIPTOR_FILE}: provenance.pins must map pin keys to 40-hex commits (${JSON.stringify(key)} is not)`
|
|
63138
63143
|
);
|
|
63139
63144
|
}
|
|
63140
63145
|
}
|
|
@@ -64704,7 +64709,7 @@ var PIN_KEYS = Object.freeze([
|
|
|
64704
64709
|
]);
|
|
64705
64710
|
var VERIFIER_KEYS = Object.freeze(["bundleDigest", "provenanceDigest"]);
|
|
64706
64711
|
var AUTHORITY_PIN_KEYS = Object.freeze(["digest", "sourceCommit"]);
|
|
64707
|
-
var PIN_SHAPE = "{schemaVersion, policyDigest, verifier: {bundleDigest, provenanceDigest}, kernelContractAuthority: {sourceCommit, digest}, trustedOrasBinaryDigests: [sha256:…, …]}";
|
|
64712
|
+
var PIN_SHAPE = "{schemaVersion, policyDigest, verifier: {bundleDigest, provenanceDigest}, kernelContractAuthority: {sourceCommit, digest} | null, trustedOrasBinaryDigests: [sha256:…, …]}";
|
|
64708
64713
|
var exactKeys2 = (value, keys) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).sort().join() === keys.join();
|
|
64709
64714
|
function requireDigest(value, what) {
|
|
64710
64715
|
if (typeof value !== "string" || !SHA256_DIGEST.test(value)) {
|
|
@@ -64739,7 +64744,7 @@ function readEvidenceTrustPin({ dataRoot: dataRoot2, declaration }) {
|
|
|
64739
64744
|
MAX_PIN_BYTES
|
|
64740
64745
|
);
|
|
64741
64746
|
const pin = parseStrictJson(bytes.toString("utf8"), declaration.pin);
|
|
64742
|
-
if (!exactKeys2(pin, PIN_KEYS) || !exactKeys2(pin.verifier, VERIFIER_KEYS) || !exactKeys2(pin.kernelContractAuthority, AUTHORITY_PIN_KEYS) || !Array.isArray(pin.trustedOrasBinaryDigests)) {
|
|
64747
|
+
if (!exactKeys2(pin, PIN_KEYS) || !exactKeys2(pin.verifier, VERIFIER_KEYS) || !(pin.kernelContractAuthority === null || exactKeys2(pin.kernelContractAuthority, AUTHORITY_PIN_KEYS)) || !Array.isArray(pin.trustedOrasBinaryDigests)) {
|
|
64743
64748
|
throw new Error(`${declaration.pin}: the pin is exactly ${PIN_SHAPE}`);
|
|
64744
64749
|
}
|
|
64745
64750
|
if (pin.schemaVersion !== EVIDENCE_TRUST_PIN_SCHEMA_VERSION) {
|
|
@@ -64757,7 +64762,7 @@ function readEvidenceTrustPin({ dataRoot: dataRoot2, declaration }) {
|
|
|
64757
64762
|
"verifier.provenanceDigest"
|
|
64758
64763
|
)
|
|
64759
64764
|
}),
|
|
64760
|
-
kernelContractAuthority: Object.freeze({
|
|
64765
|
+
kernelContractAuthority: pin.kernelContractAuthority === null ? null : Object.freeze({
|
|
64761
64766
|
sourceCommit: requireCommit(pin.kernelContractAuthority.sourceCommit),
|
|
64762
64767
|
digest: requireDigest(pin.kernelContractAuthority.digest, "kernelContractAuthority.digest")
|
|
64763
64768
|
}),
|
|
@@ -64802,6 +64807,12 @@ function readStatedKernelContractAuthority({ dataRoot: dataRoot2, schemas: schem
|
|
|
64802
64807
|
}
|
|
64803
64808
|
function assertPinnedKernelContractAuthority({ pin, authority, label: label2 }) {
|
|
64804
64809
|
const actual = kernelContractAuthorityPin(authority);
|
|
64810
|
+
if (pin.kernelContractAuthority === null) {
|
|
64811
|
+
throw new KernelContractAuthorityPinError(
|
|
64812
|
+
KERNEL_CONTRACT_AUTHORITY_NOT_PINNED_ONE,
|
|
64813
|
+
`${label2} states kernel contract authority ${actual.sourceCommit} (${actual.digest}) but the pin records none (it was written for a data root without a runtime image source index); review the index, then re-pin with \`${REPIN_COMMAND}\``
|
|
64814
|
+
);
|
|
64815
|
+
}
|
|
64805
64816
|
if (actual.sourceCommit !== pin.kernelContractAuthority.sourceCommit || actual.digest !== pin.kernelContractAuthority.digest) {
|
|
64806
64817
|
throw new KernelContractAuthorityPinError(
|
|
64807
64818
|
KERNEL_CONTRACT_AUTHORITY_NOT_PINNED_ONE,
|
|
@@ -65169,6 +65180,259 @@ async function loadVerifierKernelSchemas() {
|
|
|
65169
65180
|
return { schemas, editionProtocol: edition_protocol_default };
|
|
65170
65181
|
}
|
|
65171
65182
|
|
|
65183
|
+
// edition/sigstore-bundle-media-type.mjs
|
|
65184
|
+
var BUNDLE_MEDIA_TYPE = "application/vnd.dev.sigstore.bundle.v0.3+json";
|
|
65185
|
+
|
|
65186
|
+
// edition/trust-policy-arming.mjs
|
|
65187
|
+
var TrustPolicyArmingState = Object.freeze({
|
|
65188
|
+
ARMED: "armed",
|
|
65189
|
+
UNARMED: "unarmed",
|
|
65190
|
+
PARTIAL: "partial"
|
|
65191
|
+
});
|
|
65192
|
+
function assertArmingChannel(schemas2, channel) {
|
|
65193
|
+
if (!schemas2.EVIDENCE_CHANNELS.includes(channel)) {
|
|
65194
|
+
throw new Error(
|
|
65195
|
+
`trust-policy arming is judged for one channel; got ${JSON.stringify(channel)}, expected ${schemas2.EVIDENCE_CHANNELS.join(" | ")}`
|
|
65196
|
+
);
|
|
65197
|
+
}
|
|
65198
|
+
}
|
|
65199
|
+
function assessTrustPolicyArming(policy, schemas2, channel, { consumesCatalogue = false } = {}) {
|
|
65200
|
+
assertArmingChannel(schemas2, channel);
|
|
65201
|
+
const unmet = [];
|
|
65202
|
+
const stages = [
|
|
65203
|
+
["image-producer", schemas2.IMAGE_PRODUCER_PREDICATE_TYPE],
|
|
65204
|
+
["distribution-completeness", schemas2.DISTRIBUTION_IMAGES_PREDICATE_TYPE],
|
|
65205
|
+
["delivery-entitlement", schemas2.IMAGE_DELIVERY_ENTITLEMENT_PREDICATE_TYPE]
|
|
65206
|
+
];
|
|
65207
|
+
if (consumesCatalogue) {
|
|
65208
|
+
if (typeof schemas2.EDITION_CATALOGUE_PREDICATE_TYPE !== "string") {
|
|
65209
|
+
unmet.push("this kernel states no edition-catalogue predicate (EDITION_CATALOGUE_PREDICATE_TYPE), so a catalogue consumer cannot be armed against it.");
|
|
65210
|
+
} else {
|
|
65211
|
+
stages.push(["edition-catalogue", schemas2.EDITION_CATALOGUE_PREDICATE_TYPE]);
|
|
65212
|
+
}
|
|
65213
|
+
}
|
|
65214
|
+
const onChannel = policy.policies.filter((candidate) => candidate.channel === channel);
|
|
65215
|
+
const authorized = stages.filter(([, predicateType]) => onChannel.some((candidate) => candidate.predicateTypes.includes(predicateType)));
|
|
65216
|
+
if (policy.policies.length === 0) {
|
|
65217
|
+
unmet.push(
|
|
65218
|
+
'policies[] is empty — no signer identity is authorized, so verifySigstoreEvidenceDescriptor throws "offline trust policy authorizes no signer" for every predicate before reading any bundle.'
|
|
65219
|
+
);
|
|
65220
|
+
} else {
|
|
65221
|
+
for (const [stage, predicateType] of stages.filter((candidate) => !authorized.includes(candidate))) {
|
|
65222
|
+
unmet.push(
|
|
65223
|
+
`no policy authorizes the ${stage} predicate ${predicateType} on channel "${channel}" — that stage of a ${channel} delivery would still throw "authorizes no signer" while the policy reads as armed.`
|
|
65224
|
+
);
|
|
65225
|
+
}
|
|
65226
|
+
}
|
|
65227
|
+
if (policy.trustedCosignBinaryDigests.length === 0) {
|
|
65228
|
+
unmet.push(
|
|
65229
|
+
"trustedCosignBinaryDigests[] is empty — prepareDigestPinnedExecutable tests membership with [].includes(digest), which is false for every cosign binary, so no evidence can be verified on any host."
|
|
65230
|
+
);
|
|
65231
|
+
}
|
|
65232
|
+
if (policy.trustedGitBinaryDigests.length === 0) {
|
|
65233
|
+
unmet.push(
|
|
65234
|
+
"trustedGitBinaryDigests[] is empty — the same membership test rejects every git binary, so no verified release checkout can be established."
|
|
65235
|
+
);
|
|
65236
|
+
}
|
|
65237
|
+
const armed = unmet.length === 0;
|
|
65238
|
+
const bootstrap = policy.policies.length === 0 && policy.trustedCosignBinaryDigests.length === 0 && policy.trustedGitBinaryDigests.length === 0;
|
|
65239
|
+
const anotherChannelOnly = onChannel.length === 0 && policy.policies.length > 0;
|
|
65240
|
+
const state = armed ? TrustPolicyArmingState.ARMED : bootstrap || anotherChannelOnly ? TrustPolicyArmingState.UNARMED : TrustPolicyArmingState.PARTIAL;
|
|
65241
|
+
return { state, armed, unmet };
|
|
65242
|
+
}
|
|
65243
|
+
|
|
65244
|
+
// edition/verify-catalogue-evidence.mjs
|
|
65245
|
+
var CATALOGUE_EVIDENCE_MODE = "catalogue-evidence";
|
|
65246
|
+
var CATALOGUE_EVIDENCE_USAGE = "usage: catalogue-evidence arming --channel <release|develop> --data-root <dir> | catalogue-evidence verify --channel <c> --data-root <dir> --evidence-root <abs dir> --statement-digest <sha256:..> --bundle-digest <sha256:..> --workflow-sha <40-hex> --offering-id <id> --subject-digest <sha256:..> --catalogue-layer-digest <sha256:..> --lock-layer-digest <sha256:..> --cosign <abs path>";
|
|
65247
|
+
var CATALOGUE_EVIDENCE_STATEMENT_FILE = "statement.json";
|
|
65248
|
+
var CATALOGUE_EVIDENCE_BUNDLE_FILE = "bundle.sigstore.json";
|
|
65249
|
+
var CATALOGUE_EVIDENCE_UNARMED_CODE = EDITION_CONTRACT.evidenceTrust.refusal.code;
|
|
65250
|
+
var CatalogueEvidenceRefusalCode = Object.freeze({
|
|
65251
|
+
SubjectMismatch: "catalogue_evidence_subject_mismatch",
|
|
65252
|
+
OfferingMismatch: "catalogue_evidence_offering_mismatch",
|
|
65253
|
+
LockDigestMismatch: "catalogue_evidence_lock_digest_mismatch"
|
|
65254
|
+
});
|
|
65255
|
+
var SHA256_DIGEST3 = /^sha256:[a-f0-9]{64}$/;
|
|
65256
|
+
var COMMIT = /^[a-f0-9]{40}$/;
|
|
65257
|
+
var ARMING_FLAGS = Object.freeze(["--channel"]);
|
|
65258
|
+
var VERIFY_FLAGS = Object.freeze([
|
|
65259
|
+
"--channel",
|
|
65260
|
+
"--evidence-root",
|
|
65261
|
+
"--statement-digest",
|
|
65262
|
+
"--bundle-digest",
|
|
65263
|
+
"--workflow-sha",
|
|
65264
|
+
"--offering-id",
|
|
65265
|
+
"--subject-digest",
|
|
65266
|
+
"--catalogue-layer-digest",
|
|
65267
|
+
"--lock-layer-digest",
|
|
65268
|
+
"--cosign"
|
|
65269
|
+
]);
|
|
65270
|
+
var ABSOLUTE_FLAGS = Object.freeze(["--evidence-root", "--cosign"]);
|
|
65271
|
+
var DIGEST_FLAGS = Object.freeze(["--statement-digest", "--bundle-digest", "--subject-digest", "--catalogue-layer-digest", "--lock-layer-digest"]);
|
|
65272
|
+
var CatalogueEvidenceUsageError = class extends Error {
|
|
65273
|
+
constructor(message) {
|
|
65274
|
+
super(message);
|
|
65275
|
+
this.name = "CatalogueEvidenceUsageError";
|
|
65276
|
+
}
|
|
65277
|
+
};
|
|
65278
|
+
var CatalogueEvidenceUnarmedError = class extends Error {
|
|
65279
|
+
constructor(message) {
|
|
65280
|
+
super(`${CATALOGUE_EVIDENCE_UNARMED_CODE}: ${message}`);
|
|
65281
|
+
this.name = "EvidenceTrustRequiredError";
|
|
65282
|
+
this.code = CATALOGUE_EVIDENCE_UNARMED_CODE;
|
|
65283
|
+
}
|
|
65284
|
+
};
|
|
65285
|
+
var CatalogueEvidenceRefusal = class extends Error {
|
|
65286
|
+
constructor(code, message) {
|
|
65287
|
+
super(`${code}: ${message}`);
|
|
65288
|
+
this.name = "CatalogueEvidenceRefusal";
|
|
65289
|
+
this.code = code;
|
|
65290
|
+
}
|
|
65291
|
+
};
|
|
65292
|
+
var hexOf = (digest) => digest.slice("sha256:".length);
|
|
65293
|
+
function parseFlags(args, allowed) {
|
|
65294
|
+
const flags = /* @__PURE__ */ new Map();
|
|
65295
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
65296
|
+
const flag = args[index];
|
|
65297
|
+
const value = args[index + 1];
|
|
65298
|
+
if (typeof flag !== "string" || !allowed.includes(flag)) {
|
|
65299
|
+
throw new CatalogueEvidenceUsageError(`unknown or unexpected argument ${JSON.stringify(flag)}`);
|
|
65300
|
+
}
|
|
65301
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
65302
|
+
throw new CatalogueEvidenceUsageError(`${flag} requires a value`);
|
|
65303
|
+
}
|
|
65304
|
+
if (flags.has(flag)) {
|
|
65305
|
+
throw new CatalogueEvidenceUsageError(`${flag} was given twice`);
|
|
65306
|
+
}
|
|
65307
|
+
flags.set(flag, value);
|
|
65308
|
+
}
|
|
65309
|
+
for (const flag of allowed) {
|
|
65310
|
+
if (!flags.has(flag)) {
|
|
65311
|
+
throw new CatalogueEvidenceUsageError(`${flag} is required`);
|
|
65312
|
+
}
|
|
65313
|
+
}
|
|
65314
|
+
for (const flag of ABSOLUTE_FLAGS.filter((candidate) => flags.has(candidate))) {
|
|
65315
|
+
if (!flags.get(flag).startsWith("/")) {
|
|
65316
|
+
throw new CatalogueEvidenceUsageError(`${flag} must be an absolute path`);
|
|
65317
|
+
}
|
|
65318
|
+
}
|
|
65319
|
+
for (const flag of DIGEST_FLAGS.filter((candidate) => flags.has(candidate))) {
|
|
65320
|
+
if (!SHA256_DIGEST3.test(flags.get(flag))) {
|
|
65321
|
+
throw new CatalogueEvidenceUsageError(`${flag} must be sha256:<64 lowercase hex>`);
|
|
65322
|
+
}
|
|
65323
|
+
}
|
|
65324
|
+
if (flags.has("--workflow-sha") && !COMMIT.test(flags.get("--workflow-sha"))) {
|
|
65325
|
+
throw new CatalogueEvidenceUsageError("--workflow-sha must be a 40-character lowercase hexadecimal git commit SHA");
|
|
65326
|
+
}
|
|
65327
|
+
return flags;
|
|
65328
|
+
}
|
|
65329
|
+
async function armedAnchors({ channel, env, bundlePath }) {
|
|
65330
|
+
assertSanitizedOperatorEnvironment(env);
|
|
65331
|
+
const running = readRunningVerifier(bundlePath);
|
|
65332
|
+
const { schemas: schemas2 } = await loadVerifierKernelSchemas();
|
|
65333
|
+
resetEditionWorkspaceConfig();
|
|
65334
|
+
const declaration = editionWorkspaceConfig().evidenceTrustPolicy;
|
|
65335
|
+
if (declaration.policy === null) {
|
|
65336
|
+
throw new CatalogueEvidenceUnarmedError(`${dataRoot()} declares no evidenceTrustPolicy in edition-workspace.json`);
|
|
65337
|
+
}
|
|
65338
|
+
const { policy, policyDigest } = loadDeclaredTrustPolicy({ dataRoot: dataRoot(), declaration, schemas: schemas2 });
|
|
65339
|
+
const pin = readEvidenceTrustPin({ dataRoot: dataRoot(), declaration });
|
|
65340
|
+
assertPinnedPolicy({ pin, policyDigest });
|
|
65341
|
+
assertPinnedVerifier({ pin, actual: verifierDigests(running) });
|
|
65342
|
+
const { state, unmet } = assessTrustPolicyArming(policy, schemas2, channel, { consumesCatalogue: true });
|
|
65343
|
+
if (state !== TrustPolicyArmingState.ARMED) {
|
|
65344
|
+
throw new CatalogueEvidenceUnarmedError(
|
|
65345
|
+
`the declared policy is ${state.toUpperCase()} on the ${channel} channel for a catalogue consumer: ${unmet.join("; ")}`
|
|
65346
|
+
);
|
|
65347
|
+
}
|
|
65348
|
+
return { schemas: schemas2, policy };
|
|
65349
|
+
}
|
|
65350
|
+
async function runCatalogueEvidence({ argv, env, bundlePath }) {
|
|
65351
|
+
const [mode, phase, ...tail] = argv;
|
|
65352
|
+
if (mode !== CATALOGUE_EVIDENCE_MODE || phase !== "arming" && phase !== "verify") {
|
|
65353
|
+
throw new CatalogueEvidenceUsageError(CATALOGUE_EVIDENCE_USAGE);
|
|
65354
|
+
}
|
|
65355
|
+
let rest;
|
|
65356
|
+
try {
|
|
65357
|
+
rest = configureEditionWorkspaceFromArgv(tail, [WorkspaceRoot.Data], { refuseOthers: true });
|
|
65358
|
+
} catch (error) {
|
|
65359
|
+
throw new CatalogueEvidenceUsageError(error.message);
|
|
65360
|
+
}
|
|
65361
|
+
const flags = parseFlags(rest, phase === "arming" ? ARMING_FLAGS : VERIFY_FLAGS);
|
|
65362
|
+
const channel = flags.get("--channel");
|
|
65363
|
+
const { schemas: schemas2, policy } = await armedAnchors({ channel, env, bundlePath });
|
|
65364
|
+
if (phase === "arming") {
|
|
65365
|
+
return `${JSON.stringify({ channel, state: TrustPolicyArmingState.ARMED })}
|
|
65366
|
+
`;
|
|
65367
|
+
}
|
|
65368
|
+
const offeringId = flags.get("--offering-id");
|
|
65369
|
+
const descriptor = {
|
|
65370
|
+
statementPath: CATALOGUE_EVIDENCE_STATEMENT_FILE,
|
|
65371
|
+
statementDigest: flags.get("--statement-digest"),
|
|
65372
|
+
bundlePath: CATALOGUE_EVIDENCE_BUNDLE_FILE,
|
|
65373
|
+
bundleDigest: flags.get("--bundle-digest"),
|
|
65374
|
+
bundleMediaType: BUNDLE_MEDIA_TYPE
|
|
65375
|
+
};
|
|
65376
|
+
const trustedCosign = prepareTrustedCosignBinary({ cosignBinary: flags.get("--cosign"), policy });
|
|
65377
|
+
let statement;
|
|
65378
|
+
try {
|
|
65379
|
+
({ statement } = verifySigstoreEvidenceDescriptor({
|
|
65380
|
+
evidenceRoot: flags.get("--evidence-root"),
|
|
65381
|
+
descriptor,
|
|
65382
|
+
policy,
|
|
65383
|
+
predicateType: schemas2.EDITION_CATALOGUE_PREDICATE_TYPE,
|
|
65384
|
+
channel,
|
|
65385
|
+
expectedWorkflowSha: flags.get("--workflow-sha"),
|
|
65386
|
+
parseStatement: schemas2.parseEditionCatalogueStatement,
|
|
65387
|
+
policyRoot: dataRoot(),
|
|
65388
|
+
trustedCosign
|
|
65389
|
+
}));
|
|
65390
|
+
} finally {
|
|
65391
|
+
trustedCosign.dispose();
|
|
65392
|
+
}
|
|
65393
|
+
const [subject] = statement.subject;
|
|
65394
|
+
const expectedSubject = `xema-edition-${offeringId}`;
|
|
65395
|
+
if (subject.digest.sha256 !== hexOf(flags.get("--subject-digest")) || subject.name !== expectedSubject) {
|
|
65396
|
+
throw new CatalogueEvidenceRefusal(
|
|
65397
|
+
CatalogueEvidenceRefusalCode.SubjectMismatch,
|
|
65398
|
+
`the statement is over ${subject.name}@sha256:${subject.digest.sha256}, not ${expectedSubject}@${flags.get("--subject-digest")}`
|
|
65399
|
+
);
|
|
65400
|
+
}
|
|
65401
|
+
const refusal = schemas2.editionCatalogueRefusal(statement.predicate, {
|
|
65402
|
+
channel,
|
|
65403
|
+
catalogueLayerDigest: flags.get("--catalogue-layer-digest")
|
|
65404
|
+
});
|
|
65405
|
+
if (refusal !== void 0) {
|
|
65406
|
+
throw new CatalogueEvidenceRefusal(
|
|
65407
|
+
refusal,
|
|
65408
|
+
`the statement states channel ${statement.predicate.channel} and catalogue ${statement.predicate.catalogueDigest}; this catalogue is on ${channel} with layer ${flags.get("--catalogue-layer-digest")}`
|
|
65409
|
+
);
|
|
65410
|
+
}
|
|
65411
|
+
if (statement.predicate.offeringId !== offeringId) {
|
|
65412
|
+
throw new CatalogueEvidenceRefusal(
|
|
65413
|
+
CatalogueEvidenceRefusalCode.OfferingMismatch,
|
|
65414
|
+
`the statement is for offering ${statement.predicate.offeringId}, not ${offeringId}`
|
|
65415
|
+
);
|
|
65416
|
+
}
|
|
65417
|
+
if (statement.predicate.lockDigest !== hexOf(flags.get("--lock-layer-digest"))) {
|
|
65418
|
+
throw new CatalogueEvidenceRefusal(
|
|
65419
|
+
CatalogueEvidenceRefusalCode.LockDigestMismatch,
|
|
65420
|
+
`the statement states lock digest ${statement.predicate.lockDigest}, not that of the lock layer ${flags.get("--lock-layer-digest")}`
|
|
65421
|
+
);
|
|
65422
|
+
}
|
|
65423
|
+
return `${JSON.stringify({ verified: true, offeringId, channel, statementDigest: descriptor.statementDigest })}
|
|
65424
|
+
`;
|
|
65425
|
+
}
|
|
65426
|
+
async function runCatalogueEvidenceEntry({ argv, env, bundlePath }) {
|
|
65427
|
+
try {
|
|
65428
|
+
process.stdout.write(await runCatalogueEvidence({ argv, env, bundlePath }));
|
|
65429
|
+
} catch (error) {
|
|
65430
|
+
process.stderr.write(`catalogue-evidence: ${error.name}: ${error.message}
|
|
65431
|
+
`);
|
|
65432
|
+
process.exitCode = error instanceof CatalogueEvidenceUsageError ? 2 : 1;
|
|
65433
|
+
}
|
|
65434
|
+
}
|
|
65435
|
+
|
|
65172
65436
|
// edition/verify-image-evidence.mjs
|
|
65173
65437
|
var USAGE = "usage: verify-image-evidence <distributionId> --evidence-root <dir> --descriptor <absolute-path> --state <absolute-path> --cosign <absolute-path> --git <absolute-path> [--evidence-set <relative-path>] [--json] [--entitlement <relative-path> --entitlement-workflow-sha <40-hex git sha> --customer <id> --mode <mode> [--target-registry <registry>]] --data-root <dir> --editions-root <dir> --kernel <dir>";
|
|
65174
65438
|
var EVIDENCE_CHANNEL_OF_EDITION_CHANNEL = Object.freeze({
|
|
@@ -65512,7 +65776,13 @@ edition read from ${editionsRoot()} at editions checkout commit ${releaseSourceC
|
|
|
65512
65776
|
trustedGit.dispose();
|
|
65513
65777
|
}
|
|
65514
65778
|
}
|
|
65515
|
-
if (isEntryModule(import.meta.url)) {
|
|
65779
|
+
if (isEntryModule(import.meta.url) && process.argv[2] === CATALOGUE_EVIDENCE_MODE) {
|
|
65780
|
+
await runCatalogueEvidenceEntry({
|
|
65781
|
+
argv: process.argv.slice(2),
|
|
65782
|
+
env: process.env,
|
|
65783
|
+
bundlePath: process.argv[1]
|
|
65784
|
+
});
|
|
65785
|
+
} else if (isEntryModule(import.meta.url)) {
|
|
65516
65786
|
await verifyEvidence({
|
|
65517
65787
|
argv: process.argv.slice(2),
|
|
65518
65788
|
env: process.env,
|