@xemahq/xema 0.40.3 → 0.41.0
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 +281 -9
- package/dist/edition/verify-image-evidence.bundle.provenance.json +65 -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/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 +7 -0
- 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/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",
|
|
@@ -63107,7 +63113,7 @@ function assertEngineImage(image) {
|
|
|
63107
63113
|
return image;
|
|
63108
63114
|
}
|
|
63109
63115
|
var COMMIT_PATTERN2 = /^[0-9a-f]{40}$/;
|
|
63110
|
-
var
|
|
63116
|
+
var DIGEST_PIN_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
63111
63117
|
var exactKeys = (value, keys, where) => {
|
|
63112
63118
|
if (value === null || typeof value !== "object" || Array.isArray(value) || Object.keys(value).sort().join() !== [...keys].sort().join()) {
|
|
63113
63119
|
throw new Error(`${where} must be exactly {${keys.join(", ")}}`);
|
|
@@ -63128,13 +63134,14 @@ function parseEditionDescriptor(value) {
|
|
|
63128
63134
|
}
|
|
63129
63135
|
const { pins, workflow } = value.provenance;
|
|
63130
63136
|
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 "${
|
|
63137
|
+
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
63138
|
}
|
|
63133
63139
|
for (const [key, pin] of Object.entries(pins)) {
|
|
63134
|
-
const
|
|
63140
|
+
const digestPin = DIGEST_PIN_KEYS.includes(key);
|
|
63141
|
+
const pattern = digestPin ? DIGEST_PIN_PATTERN : COMMIT_PATTERN2;
|
|
63135
63142
|
if (typeof pin !== "string" || !pattern.test(pin)) {
|
|
63136
63143
|
throw new Error(
|
|
63137
|
-
|
|
63144
|
+
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
63145
|
);
|
|
63139
63146
|
}
|
|
63140
63147
|
}
|
|
@@ -64704,7 +64711,7 @@ var PIN_KEYS = Object.freeze([
|
|
|
64704
64711
|
]);
|
|
64705
64712
|
var VERIFIER_KEYS = Object.freeze(["bundleDigest", "provenanceDigest"]);
|
|
64706
64713
|
var AUTHORITY_PIN_KEYS = Object.freeze(["digest", "sourceCommit"]);
|
|
64707
|
-
var PIN_SHAPE = "{schemaVersion, policyDigest, verifier: {bundleDigest, provenanceDigest}, kernelContractAuthority: {sourceCommit, digest}, trustedOrasBinaryDigests: [sha256:…, …]}";
|
|
64714
|
+
var PIN_SHAPE = "{schemaVersion, policyDigest, verifier: {bundleDigest, provenanceDigest}, kernelContractAuthority: {sourceCommit, digest} | null, trustedOrasBinaryDigests: [sha256:…, …]}";
|
|
64708
64715
|
var exactKeys2 = (value, keys) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).sort().join() === keys.join();
|
|
64709
64716
|
function requireDigest(value, what) {
|
|
64710
64717
|
if (typeof value !== "string" || !SHA256_DIGEST.test(value)) {
|
|
@@ -64739,7 +64746,7 @@ function readEvidenceTrustPin({ dataRoot: dataRoot2, declaration }) {
|
|
|
64739
64746
|
MAX_PIN_BYTES
|
|
64740
64747
|
);
|
|
64741
64748
|
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)) {
|
|
64749
|
+
if (!exactKeys2(pin, PIN_KEYS) || !exactKeys2(pin.verifier, VERIFIER_KEYS) || !(pin.kernelContractAuthority === null || exactKeys2(pin.kernelContractAuthority, AUTHORITY_PIN_KEYS)) || !Array.isArray(pin.trustedOrasBinaryDigests)) {
|
|
64743
64750
|
throw new Error(`${declaration.pin}: the pin is exactly ${PIN_SHAPE}`);
|
|
64744
64751
|
}
|
|
64745
64752
|
if (pin.schemaVersion !== EVIDENCE_TRUST_PIN_SCHEMA_VERSION) {
|
|
@@ -64757,7 +64764,7 @@ function readEvidenceTrustPin({ dataRoot: dataRoot2, declaration }) {
|
|
|
64757
64764
|
"verifier.provenanceDigest"
|
|
64758
64765
|
)
|
|
64759
64766
|
}),
|
|
64760
|
-
kernelContractAuthority: Object.freeze({
|
|
64767
|
+
kernelContractAuthority: pin.kernelContractAuthority === null ? null : Object.freeze({
|
|
64761
64768
|
sourceCommit: requireCommit(pin.kernelContractAuthority.sourceCommit),
|
|
64762
64769
|
digest: requireDigest(pin.kernelContractAuthority.digest, "kernelContractAuthority.digest")
|
|
64763
64770
|
}),
|
|
@@ -64802,6 +64809,12 @@ function readStatedKernelContractAuthority({ dataRoot: dataRoot2, schemas: schem
|
|
|
64802
64809
|
}
|
|
64803
64810
|
function assertPinnedKernelContractAuthority({ pin, authority, label: label2 }) {
|
|
64804
64811
|
const actual = kernelContractAuthorityPin(authority);
|
|
64812
|
+
if (pin.kernelContractAuthority === null) {
|
|
64813
|
+
throw new KernelContractAuthorityPinError(
|
|
64814
|
+
KERNEL_CONTRACT_AUTHORITY_NOT_PINNED_ONE,
|
|
64815
|
+
`${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}\``
|
|
64816
|
+
);
|
|
64817
|
+
}
|
|
64805
64818
|
if (actual.sourceCommit !== pin.kernelContractAuthority.sourceCommit || actual.digest !== pin.kernelContractAuthority.digest) {
|
|
64806
64819
|
throw new KernelContractAuthorityPinError(
|
|
64807
64820
|
KERNEL_CONTRACT_AUTHORITY_NOT_PINNED_ONE,
|
|
@@ -65169,6 +65182,259 @@ async function loadVerifierKernelSchemas() {
|
|
|
65169
65182
|
return { schemas, editionProtocol: edition_protocol_default };
|
|
65170
65183
|
}
|
|
65171
65184
|
|
|
65185
|
+
// edition/sigstore-bundle-media-type.mjs
|
|
65186
|
+
var BUNDLE_MEDIA_TYPE = "application/vnd.dev.sigstore.bundle.v0.3+json";
|
|
65187
|
+
|
|
65188
|
+
// edition/trust-policy-arming.mjs
|
|
65189
|
+
var TrustPolicyArmingState = Object.freeze({
|
|
65190
|
+
ARMED: "armed",
|
|
65191
|
+
UNARMED: "unarmed",
|
|
65192
|
+
PARTIAL: "partial"
|
|
65193
|
+
});
|
|
65194
|
+
function assertArmingChannel(schemas2, channel) {
|
|
65195
|
+
if (!schemas2.EVIDENCE_CHANNELS.includes(channel)) {
|
|
65196
|
+
throw new Error(
|
|
65197
|
+
`trust-policy arming is judged for one channel; got ${JSON.stringify(channel)}, expected ${schemas2.EVIDENCE_CHANNELS.join(" | ")}`
|
|
65198
|
+
);
|
|
65199
|
+
}
|
|
65200
|
+
}
|
|
65201
|
+
function assessTrustPolicyArming(policy, schemas2, channel, { consumesCatalogue = false } = {}) {
|
|
65202
|
+
assertArmingChannel(schemas2, channel);
|
|
65203
|
+
const unmet = [];
|
|
65204
|
+
const stages = [
|
|
65205
|
+
["image-producer", schemas2.IMAGE_PRODUCER_PREDICATE_TYPE],
|
|
65206
|
+
["distribution-completeness", schemas2.DISTRIBUTION_IMAGES_PREDICATE_TYPE],
|
|
65207
|
+
["delivery-entitlement", schemas2.IMAGE_DELIVERY_ENTITLEMENT_PREDICATE_TYPE]
|
|
65208
|
+
];
|
|
65209
|
+
if (consumesCatalogue) {
|
|
65210
|
+
if (typeof schemas2.EDITION_CATALOGUE_PREDICATE_TYPE !== "string") {
|
|
65211
|
+
unmet.push("this kernel states no edition-catalogue predicate (EDITION_CATALOGUE_PREDICATE_TYPE), so a catalogue consumer cannot be armed against it.");
|
|
65212
|
+
} else {
|
|
65213
|
+
stages.push(["edition-catalogue", schemas2.EDITION_CATALOGUE_PREDICATE_TYPE]);
|
|
65214
|
+
}
|
|
65215
|
+
}
|
|
65216
|
+
const onChannel = policy.policies.filter((candidate) => candidate.channel === channel);
|
|
65217
|
+
const authorized = stages.filter(([, predicateType]) => onChannel.some((candidate) => candidate.predicateTypes.includes(predicateType)));
|
|
65218
|
+
if (policy.policies.length === 0) {
|
|
65219
|
+
unmet.push(
|
|
65220
|
+
'policies[] is empty — no signer identity is authorized, so verifySigstoreEvidenceDescriptor throws "offline trust policy authorizes no signer" for every predicate before reading any bundle.'
|
|
65221
|
+
);
|
|
65222
|
+
} else {
|
|
65223
|
+
for (const [stage, predicateType] of stages.filter((candidate) => !authorized.includes(candidate))) {
|
|
65224
|
+
unmet.push(
|
|
65225
|
+
`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.`
|
|
65226
|
+
);
|
|
65227
|
+
}
|
|
65228
|
+
}
|
|
65229
|
+
if (policy.trustedCosignBinaryDigests.length === 0) {
|
|
65230
|
+
unmet.push(
|
|
65231
|
+
"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."
|
|
65232
|
+
);
|
|
65233
|
+
}
|
|
65234
|
+
if (policy.trustedGitBinaryDigests.length === 0) {
|
|
65235
|
+
unmet.push(
|
|
65236
|
+
"trustedGitBinaryDigests[] is empty — the same membership test rejects every git binary, so no verified release checkout can be established."
|
|
65237
|
+
);
|
|
65238
|
+
}
|
|
65239
|
+
const armed = unmet.length === 0;
|
|
65240
|
+
const bootstrap = policy.policies.length === 0 && policy.trustedCosignBinaryDigests.length === 0 && policy.trustedGitBinaryDigests.length === 0;
|
|
65241
|
+
const anotherChannelOnly = onChannel.length === 0 && policy.policies.length > 0;
|
|
65242
|
+
const state = armed ? TrustPolicyArmingState.ARMED : bootstrap || anotherChannelOnly ? TrustPolicyArmingState.UNARMED : TrustPolicyArmingState.PARTIAL;
|
|
65243
|
+
return { state, armed, unmet };
|
|
65244
|
+
}
|
|
65245
|
+
|
|
65246
|
+
// edition/verify-catalogue-evidence.mjs
|
|
65247
|
+
var CATALOGUE_EVIDENCE_MODE = "catalogue-evidence";
|
|
65248
|
+
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>";
|
|
65249
|
+
var CATALOGUE_EVIDENCE_STATEMENT_FILE = "statement.json";
|
|
65250
|
+
var CATALOGUE_EVIDENCE_BUNDLE_FILE = "bundle.sigstore.json";
|
|
65251
|
+
var CATALOGUE_EVIDENCE_UNARMED_CODE = EDITION_CONTRACT.evidenceTrust.refusal.code;
|
|
65252
|
+
var CatalogueEvidenceRefusalCode = Object.freeze({
|
|
65253
|
+
SubjectMismatch: "catalogue_evidence_subject_mismatch",
|
|
65254
|
+
OfferingMismatch: "catalogue_evidence_offering_mismatch",
|
|
65255
|
+
LockDigestMismatch: "catalogue_evidence_lock_digest_mismatch"
|
|
65256
|
+
});
|
|
65257
|
+
var SHA256_DIGEST3 = /^sha256:[a-f0-9]{64}$/;
|
|
65258
|
+
var COMMIT = /^[a-f0-9]{40}$/;
|
|
65259
|
+
var ARMING_FLAGS = Object.freeze(["--channel"]);
|
|
65260
|
+
var VERIFY_FLAGS = Object.freeze([
|
|
65261
|
+
"--channel",
|
|
65262
|
+
"--evidence-root",
|
|
65263
|
+
"--statement-digest",
|
|
65264
|
+
"--bundle-digest",
|
|
65265
|
+
"--workflow-sha",
|
|
65266
|
+
"--offering-id",
|
|
65267
|
+
"--subject-digest",
|
|
65268
|
+
"--catalogue-layer-digest",
|
|
65269
|
+
"--lock-layer-digest",
|
|
65270
|
+
"--cosign"
|
|
65271
|
+
]);
|
|
65272
|
+
var ABSOLUTE_FLAGS = Object.freeze(["--evidence-root", "--cosign"]);
|
|
65273
|
+
var DIGEST_FLAGS = Object.freeze(["--statement-digest", "--bundle-digest", "--subject-digest", "--catalogue-layer-digest", "--lock-layer-digest"]);
|
|
65274
|
+
var CatalogueEvidenceUsageError = class extends Error {
|
|
65275
|
+
constructor(message) {
|
|
65276
|
+
super(message);
|
|
65277
|
+
this.name = "CatalogueEvidenceUsageError";
|
|
65278
|
+
}
|
|
65279
|
+
};
|
|
65280
|
+
var CatalogueEvidenceUnarmedError = class extends Error {
|
|
65281
|
+
constructor(message) {
|
|
65282
|
+
super(`${CATALOGUE_EVIDENCE_UNARMED_CODE}: ${message}`);
|
|
65283
|
+
this.name = "EvidenceTrustRequiredError";
|
|
65284
|
+
this.code = CATALOGUE_EVIDENCE_UNARMED_CODE;
|
|
65285
|
+
}
|
|
65286
|
+
};
|
|
65287
|
+
var CatalogueEvidenceRefusal = class extends Error {
|
|
65288
|
+
constructor(code, message) {
|
|
65289
|
+
super(`${code}: ${message}`);
|
|
65290
|
+
this.name = "CatalogueEvidenceRefusal";
|
|
65291
|
+
this.code = code;
|
|
65292
|
+
}
|
|
65293
|
+
};
|
|
65294
|
+
var hexOf = (digest) => digest.slice("sha256:".length);
|
|
65295
|
+
function parseFlags(args, allowed) {
|
|
65296
|
+
const flags = /* @__PURE__ */ new Map();
|
|
65297
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
65298
|
+
const flag = args[index];
|
|
65299
|
+
const value = args[index + 1];
|
|
65300
|
+
if (typeof flag !== "string" || !allowed.includes(flag)) {
|
|
65301
|
+
throw new CatalogueEvidenceUsageError(`unknown or unexpected argument ${JSON.stringify(flag)}`);
|
|
65302
|
+
}
|
|
65303
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
65304
|
+
throw new CatalogueEvidenceUsageError(`${flag} requires a value`);
|
|
65305
|
+
}
|
|
65306
|
+
if (flags.has(flag)) {
|
|
65307
|
+
throw new CatalogueEvidenceUsageError(`${flag} was given twice`);
|
|
65308
|
+
}
|
|
65309
|
+
flags.set(flag, value);
|
|
65310
|
+
}
|
|
65311
|
+
for (const flag of allowed) {
|
|
65312
|
+
if (!flags.has(flag)) {
|
|
65313
|
+
throw new CatalogueEvidenceUsageError(`${flag} is required`);
|
|
65314
|
+
}
|
|
65315
|
+
}
|
|
65316
|
+
for (const flag of ABSOLUTE_FLAGS.filter((candidate) => flags.has(candidate))) {
|
|
65317
|
+
if (!flags.get(flag).startsWith("/")) {
|
|
65318
|
+
throw new CatalogueEvidenceUsageError(`${flag} must be an absolute path`);
|
|
65319
|
+
}
|
|
65320
|
+
}
|
|
65321
|
+
for (const flag of DIGEST_FLAGS.filter((candidate) => flags.has(candidate))) {
|
|
65322
|
+
if (!SHA256_DIGEST3.test(flags.get(flag))) {
|
|
65323
|
+
throw new CatalogueEvidenceUsageError(`${flag} must be sha256:<64 lowercase hex>`);
|
|
65324
|
+
}
|
|
65325
|
+
}
|
|
65326
|
+
if (flags.has("--workflow-sha") && !COMMIT.test(flags.get("--workflow-sha"))) {
|
|
65327
|
+
throw new CatalogueEvidenceUsageError("--workflow-sha must be a 40-character lowercase hexadecimal git commit SHA");
|
|
65328
|
+
}
|
|
65329
|
+
return flags;
|
|
65330
|
+
}
|
|
65331
|
+
async function armedAnchors({ channel, env, bundlePath }) {
|
|
65332
|
+
assertSanitizedOperatorEnvironment(env);
|
|
65333
|
+
const running = readRunningVerifier(bundlePath);
|
|
65334
|
+
const { schemas: schemas2 } = await loadVerifierKernelSchemas();
|
|
65335
|
+
resetEditionWorkspaceConfig();
|
|
65336
|
+
const declaration = editionWorkspaceConfig().evidenceTrustPolicy;
|
|
65337
|
+
if (declaration.policy === null) {
|
|
65338
|
+
throw new CatalogueEvidenceUnarmedError(`${dataRoot()} declares no evidenceTrustPolicy in edition-workspace.json`);
|
|
65339
|
+
}
|
|
65340
|
+
const { policy, policyDigest } = loadDeclaredTrustPolicy({ dataRoot: dataRoot(), declaration, schemas: schemas2 });
|
|
65341
|
+
const pin = readEvidenceTrustPin({ dataRoot: dataRoot(), declaration });
|
|
65342
|
+
assertPinnedPolicy({ pin, policyDigest });
|
|
65343
|
+
assertPinnedVerifier({ pin, actual: verifierDigests(running) });
|
|
65344
|
+
const { state, unmet } = assessTrustPolicyArming(policy, schemas2, channel, { consumesCatalogue: true });
|
|
65345
|
+
if (state !== TrustPolicyArmingState.ARMED) {
|
|
65346
|
+
throw new CatalogueEvidenceUnarmedError(
|
|
65347
|
+
`the declared policy is ${state.toUpperCase()} on the ${channel} channel for a catalogue consumer: ${unmet.join("; ")}`
|
|
65348
|
+
);
|
|
65349
|
+
}
|
|
65350
|
+
return { schemas: schemas2, policy };
|
|
65351
|
+
}
|
|
65352
|
+
async function runCatalogueEvidence({ argv, env, bundlePath }) {
|
|
65353
|
+
const [mode, phase, ...tail] = argv;
|
|
65354
|
+
if (mode !== CATALOGUE_EVIDENCE_MODE || phase !== "arming" && phase !== "verify") {
|
|
65355
|
+
throw new CatalogueEvidenceUsageError(CATALOGUE_EVIDENCE_USAGE);
|
|
65356
|
+
}
|
|
65357
|
+
let rest;
|
|
65358
|
+
try {
|
|
65359
|
+
rest = configureEditionWorkspaceFromArgv(tail, [WorkspaceRoot.Data], { refuseOthers: true });
|
|
65360
|
+
} catch (error) {
|
|
65361
|
+
throw new CatalogueEvidenceUsageError(error.message);
|
|
65362
|
+
}
|
|
65363
|
+
const flags = parseFlags(rest, phase === "arming" ? ARMING_FLAGS : VERIFY_FLAGS);
|
|
65364
|
+
const channel = flags.get("--channel");
|
|
65365
|
+
const { schemas: schemas2, policy } = await armedAnchors({ channel, env, bundlePath });
|
|
65366
|
+
if (phase === "arming") {
|
|
65367
|
+
return `${JSON.stringify({ channel, state: TrustPolicyArmingState.ARMED })}
|
|
65368
|
+
`;
|
|
65369
|
+
}
|
|
65370
|
+
const offeringId = flags.get("--offering-id");
|
|
65371
|
+
const descriptor = {
|
|
65372
|
+
statementPath: CATALOGUE_EVIDENCE_STATEMENT_FILE,
|
|
65373
|
+
statementDigest: flags.get("--statement-digest"),
|
|
65374
|
+
bundlePath: CATALOGUE_EVIDENCE_BUNDLE_FILE,
|
|
65375
|
+
bundleDigest: flags.get("--bundle-digest"),
|
|
65376
|
+
bundleMediaType: BUNDLE_MEDIA_TYPE
|
|
65377
|
+
};
|
|
65378
|
+
const trustedCosign = prepareTrustedCosignBinary({ cosignBinary: flags.get("--cosign"), policy });
|
|
65379
|
+
let statement;
|
|
65380
|
+
try {
|
|
65381
|
+
({ statement } = verifySigstoreEvidenceDescriptor({
|
|
65382
|
+
evidenceRoot: flags.get("--evidence-root"),
|
|
65383
|
+
descriptor,
|
|
65384
|
+
policy,
|
|
65385
|
+
predicateType: schemas2.EDITION_CATALOGUE_PREDICATE_TYPE,
|
|
65386
|
+
channel,
|
|
65387
|
+
expectedWorkflowSha: flags.get("--workflow-sha"),
|
|
65388
|
+
parseStatement: schemas2.parseEditionCatalogueStatement,
|
|
65389
|
+
policyRoot: dataRoot(),
|
|
65390
|
+
trustedCosign
|
|
65391
|
+
}));
|
|
65392
|
+
} finally {
|
|
65393
|
+
trustedCosign.dispose();
|
|
65394
|
+
}
|
|
65395
|
+
const [subject] = statement.subject;
|
|
65396
|
+
const expectedSubject = `xema-edition-${offeringId}`;
|
|
65397
|
+
if (subject.digest.sha256 !== hexOf(flags.get("--subject-digest")) || subject.name !== expectedSubject) {
|
|
65398
|
+
throw new CatalogueEvidenceRefusal(
|
|
65399
|
+
CatalogueEvidenceRefusalCode.SubjectMismatch,
|
|
65400
|
+
`the statement is over ${subject.name}@sha256:${subject.digest.sha256}, not ${expectedSubject}@${flags.get("--subject-digest")}`
|
|
65401
|
+
);
|
|
65402
|
+
}
|
|
65403
|
+
const refusal = schemas2.editionCatalogueRefusal(statement.predicate, {
|
|
65404
|
+
channel,
|
|
65405
|
+
catalogueLayerDigest: flags.get("--catalogue-layer-digest")
|
|
65406
|
+
});
|
|
65407
|
+
if (refusal !== void 0) {
|
|
65408
|
+
throw new CatalogueEvidenceRefusal(
|
|
65409
|
+
refusal,
|
|
65410
|
+
`the statement states channel ${statement.predicate.channel} and catalogue ${statement.predicate.catalogueDigest}; this catalogue is on ${channel} with layer ${flags.get("--catalogue-layer-digest")}`
|
|
65411
|
+
);
|
|
65412
|
+
}
|
|
65413
|
+
if (statement.predicate.offeringId !== offeringId) {
|
|
65414
|
+
throw new CatalogueEvidenceRefusal(
|
|
65415
|
+
CatalogueEvidenceRefusalCode.OfferingMismatch,
|
|
65416
|
+
`the statement is for offering ${statement.predicate.offeringId}, not ${offeringId}`
|
|
65417
|
+
);
|
|
65418
|
+
}
|
|
65419
|
+
if (statement.predicate.lockDigest !== hexOf(flags.get("--lock-layer-digest"))) {
|
|
65420
|
+
throw new CatalogueEvidenceRefusal(
|
|
65421
|
+
CatalogueEvidenceRefusalCode.LockDigestMismatch,
|
|
65422
|
+
`the statement states lock digest ${statement.predicate.lockDigest}, not that of the lock layer ${flags.get("--lock-layer-digest")}`
|
|
65423
|
+
);
|
|
65424
|
+
}
|
|
65425
|
+
return `${JSON.stringify({ verified: true, offeringId, channel, statementDigest: descriptor.statementDigest })}
|
|
65426
|
+
`;
|
|
65427
|
+
}
|
|
65428
|
+
async function runCatalogueEvidenceEntry({ argv, env, bundlePath }) {
|
|
65429
|
+
try {
|
|
65430
|
+
process.stdout.write(await runCatalogueEvidence({ argv, env, bundlePath }));
|
|
65431
|
+
} catch (error) {
|
|
65432
|
+
process.stderr.write(`catalogue-evidence: ${error.name}: ${error.message}
|
|
65433
|
+
`);
|
|
65434
|
+
process.exitCode = error instanceof CatalogueEvidenceUsageError ? 2 : 1;
|
|
65435
|
+
}
|
|
65436
|
+
}
|
|
65437
|
+
|
|
65172
65438
|
// edition/verify-image-evidence.mjs
|
|
65173
65439
|
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
65440
|
var EVIDENCE_CHANNEL_OF_EDITION_CHANNEL = Object.freeze({
|
|
@@ -65512,7 +65778,13 @@ edition read from ${editionsRoot()} at editions checkout commit ${releaseSourceC
|
|
|
65512
65778
|
trustedGit.dispose();
|
|
65513
65779
|
}
|
|
65514
65780
|
}
|
|
65515
|
-
if (isEntryModule(import.meta.url)) {
|
|
65781
|
+
if (isEntryModule(import.meta.url) && process.argv[2] === CATALOGUE_EVIDENCE_MODE) {
|
|
65782
|
+
await runCatalogueEvidenceEntry({
|
|
65783
|
+
argv: process.argv.slice(2),
|
|
65784
|
+
env: process.env,
|
|
65785
|
+
bundlePath: process.argv[1]
|
|
65786
|
+
});
|
|
65787
|
+
} else if (isEntryModule(import.meta.url)) {
|
|
65516
65788
|
await verifyEvidence({
|
|
65517
65789
|
argv: process.argv.slice(2),
|
|
65518
65790
|
env: process.env,
|