@xemahq/xema 0.27.0 → 0.29.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.
Files changed (63) hide show
  1. package/dist/commands/edition.d.ts.map +1 -1
  2. package/dist/commands/edition.js +8 -7
  3. package/dist/commands/edition.js.map +1 -1
  4. package/dist/dev/env-defaults.d.ts.map +1 -1
  5. package/dist/dev/env-defaults.js +0 -1
  6. package/dist/dev/env-defaults.js.map +1 -1
  7. package/dist/edition/verify-image-evidence.bundle.mjs +231 -50
  8. package/dist/edition/verify-image-evidence.bundle.provenance.json +55 -43
  9. package/dist/runtime/edition/verb-map.d.ts +2 -1
  10. package/dist/runtime/edition/verb-map.d.ts.map +1 -1
  11. package/dist/runtime/edition/verb-map.js +2 -0
  12. package/dist/runtime/edition/verb-map.js.map +1 -1
  13. package/dist/runtime/runner/chart-set-lock.d.ts +4 -1
  14. package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
  15. package/dist/runtime/runner/chart-set-lock.js +39 -4
  16. package/dist/runtime/runner/chart-set-lock.js.map +1 -1
  17. package/edition/README.md +76 -41
  18. package/edition/build-biome-index.mjs +1 -1
  19. package/edition/build-platform-service-index.mjs +1 -1
  20. package/edition/build-runtime-image-source-index.mjs +64 -20
  21. package/edition/catalogue-fetch.mjs +36 -9
  22. package/edition/catalogue-pin.mjs +71 -0
  23. package/edition/catalogue.mjs +11 -0
  24. package/edition/chart-set-source-commit.mjs +17 -7
  25. package/edition/checkout-indexed-source-repositories.mjs +17 -17
  26. package/edition/customer-flags.mjs +0 -1
  27. package/edition/develop-customer-edition.mjs +23 -5
  28. package/edition/develop-edition.mjs +147 -48
  29. package/edition/develop-freeze.mjs +69 -25
  30. package/edition/develop-seal.mjs +39 -25
  31. package/edition/dist-check.mjs +1 -1
  32. package/edition/edition-artifact.mjs +79 -18
  33. package/edition/edition-biome-manifests.mjs +2 -3
  34. package/edition/edition-chart-set.mjs +59 -6
  35. package/edition/edition-config.mjs +45 -21
  36. package/edition/edition-constants.mjs +10 -4
  37. package/edition/edition-contract-parse.mjs +52 -0
  38. package/edition/edition-contract.json +21 -5
  39. package/edition/edition-workspace.mjs +23 -9
  40. package/edition/evidence-checkout.mjs +22 -1
  41. package/edition/evidence-trust-policy.mjs +142 -78
  42. package/edition/external-source-walk.mjs +48 -61
  43. package/edition/git-source-auth.mjs +104 -25
  44. package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
  45. package/edition/image-evidence.mjs +21 -3
  46. package/edition/kernel-contract-authority.mjs +23 -1
  47. package/edition/lib.mjs +16 -24
  48. package/edition/oras.mjs +1 -1
  49. package/edition/pin-source.mjs +6 -1
  50. package/edition/produce-image-delivery-entitlement-evidence.mjs +12 -12
  51. package/edition/registry-credentials.mjs +11 -1
  52. package/edition/release-channel-guard.mjs +11 -1
  53. package/edition/release-edition-artifact.mjs +5 -6
  54. package/edition/resolve-chart-set.mjs +50 -30
  55. package/edition/resolve-customer-edition.mjs +63 -7
  56. package/edition/resolve-distribution.mjs +1 -1
  57. package/edition/resolve-edition.mjs +12 -11
  58. package/edition/resolve-images.mjs +36 -15
  59. package/edition/seal-image-digests.mjs +31 -7
  60. package/edition/source-closure.mjs +1 -2
  61. package/edition/trust-policy-arming-verb.mjs +68 -0
  62. package/edition/verify-image-evidence.mjs +40 -12
  63. package/package.json +3 -3
@@ -49,6 +49,11 @@ import {
49
49
  trustPolicyDigest,
50
50
  verifierDigests,
51
51
  } from './evidence-trust-pin.mjs';
52
+ import {
53
+ assertEntitlementDescriptorStates,
54
+ entitlementEvidenceRoot,
55
+ parseEntitlementDescriptorDocument,
56
+ } from './image-delivery-entitlement-descriptor.mjs';
52
57
  import { verifyAndAdvanceReplayState } from './image-evidence-replay-state.mjs';
53
58
  import {
54
59
  readCanonicalJsonFromRoot,
@@ -64,13 +69,14 @@ import { loadVerifierKernelSchemas } from './verifier-kernel-schemas.mjs';
64
69
  const USAGE =
65
70
  'usage: verify-image-evidence <distributionId> --evidence-root <dir> --descriptor <absolute-path> --state <absolute-path> ' +
66
71
  '--cosign <absolute-path> --git <absolute-path> [--evidence-set <relative-path>] [--json] ' +
67
- '[--entitlement <relative-path> --customer <id> --mode <mode> [--target-registry <registry>]] ' +
72
+ '[--entitlement <relative-path> --entitlement-workflow-sha <40-hex git sha> --customer <id> --mode <mode> [--target-registry <registry>]] ' +
68
73
  '--data-root <dir> --editions-root <dir> --kernel <dir>';
69
74
  // The edition channel -> the kernel's evidence channel, stated once and explicitly.
70
75
  const EVIDENCE_CHANNEL_OF_EDITION_CHANNEL = Object.freeze({
71
76
  [EditionChannel.Release]: 'release',
72
77
  [EditionChannel.Develop]: 'develop',
73
78
  });
79
+ const GIT_COMMIT_SHA = /^[a-f0-9]{40}$/;
74
80
  const DATA_ROOT_FILES = Object.freeze(['edition-workspace.json', 'runtime-image-source-index.json']);
75
81
 
76
82
  function requireOptionValue(args, index, flag) {
@@ -101,6 +107,7 @@ function parseArgs(args) {
101
107
  cosignBinary: null,
102
108
  gitBinary: null,
103
109
  entitlementDescriptorPath: null,
110
+ entitlementWorkflowSha: null,
104
111
  customerId: null,
105
112
  mode: null,
106
113
  targetRegistry: null,
@@ -118,6 +125,7 @@ function parseArgs(args) {
118
125
  else if (flag === '--cosign') {options.cosignBinary = value;}
119
126
  else if (flag === '--git') {options.gitBinary = value;}
120
127
  else if (flag === '--entitlement') {options.entitlementDescriptorPath = value;}
128
+ else if (flag === '--entitlement-workflow-sha') {options.entitlementWorkflowSha = value;}
121
129
  else if (flag === '--customer') {options.customerId = value;}
122
130
  else if (flag === '--mode') {options.mode = value;}
123
131
  else if (flag === '--target-registry') {options.targetRegistry = value;}
@@ -137,9 +145,12 @@ function parseArgs(args) {
137
145
  }
138
146
  }
139
147
  // An entitlement is verified whole or not at all: the descriptor names the
140
- // signed statement, and the customer and mode are what it must be bound to.
148
+ // signed statement, the workflow SHA is the exact commit its certificate is
149
+ // held to (stated by the caller, never read from the evidence), and the
150
+ // customer and mode are what it must be bound to.
141
151
  const entitlementFlags = [
142
152
  ['--entitlement', options.entitlementDescriptorPath],
153
+ ['--entitlement-workflow-sha', options.entitlementWorkflowSha],
143
154
  ['--customer', options.customerId],
144
155
  ['--mode', options.mode],
145
156
  ];
@@ -150,7 +161,10 @@ function parseArgs(args) {
150
161
  );
151
162
  }
152
163
  if (stated.length === 0 && options.targetRegistry !== null) {
153
- throw new Error('--target-registry needs --entitlement, --customer and --mode');
164
+ throw new Error('--target-registry needs --entitlement, --entitlement-workflow-sha, --customer and --mode');
165
+ }
166
+ if (options.entitlementWorkflowSha !== null && !GIT_COMMIT_SHA.test(options.entitlementWorkflowSha)) {
167
+ throw new Error('--entitlement-workflow-sha must be a 40-character lowercase hexadecimal git commit SHA');
154
168
  }
155
169
  if (options.entitlementDescriptorPath !== null && isAbsolute(options.entitlementDescriptorPath)) {
156
170
  throw new Error('--entitlement must be an evidence-root-relative path');
@@ -185,6 +199,7 @@ export const EDITION_EVIDENCE_VERIFICATION_SCHEMA = 'xema.edition-evidence-verif
185
199
  * What `--json` prints once the evidence is verified: the channel it was verified
186
200
  * on, the release sequence it proved, and the statementDigest (the digest of the verified in-toto statement, not of the image) of every statement verified
187
201
  * (producers by subject, then completeness, then the entitlement if one was asked for).
202
+ * `entitlementDescriptor` is the Sigstore evidence descriptor `entitlement.json` holds.
188
203
  */
189
204
  export function evidenceVerificationDocument({ distributionId, channel, evidenceSet, verified, entitlementDescriptor }) {
190
205
  const producers = evidenceSet.producerEvidence
@@ -350,19 +365,32 @@ export async function verifyEvidence({ argv, env, bundlePath }) {
350
365
  cosignBinary: options.cosignBinary,
351
366
  schemas,
352
367
  });
353
- const entitlementDescriptor =
368
+ const entitlement =
354
369
  options.entitlementDescriptorPath === null
355
370
  ? null
356
- : readCanonicalJsonFromRoot(
357
- evidenceRoot,
358
- options.entitlementDescriptorPath,
359
- 'delivery entitlement evidence descriptor',
371
+ : parseEntitlementDescriptorDocument(
372
+ readCanonicalJsonFromRoot(
373
+ evidenceRoot,
374
+ options.entitlementDescriptorPath,
375
+ 'delivery entitlement descriptor',
376
+ ),
377
+ schemas,
360
378
  );
379
+ if (entitlement !== null) {
380
+ assertEntitlementDescriptorStates(entitlement, {
381
+ distributionId: options.distributionId,
382
+ customerId: options.customerId,
383
+ mode: options.mode,
384
+ targetRegistry: options.targetRegistry,
385
+ distributionStatementDigest: result.completenessStatementDigest,
386
+ });
387
+ }
388
+ const entitlementDescriptor = entitlement === null ? null : entitlement.evidence;
361
389
  const entitlementStatement =
362
390
  entitlementDescriptor === null
363
391
  ? null
364
392
  : await verifyImageDeliveryEntitlement({
365
- evidenceRoot,
393
+ evidenceRoot: entitlementEvidenceRoot(evidenceRoot, options.entitlementDescriptorPath),
366
394
  descriptor: entitlementDescriptor,
367
395
  policy: anchors.policy,
368
396
  policyRoot: dataRoot(),
@@ -373,9 +401,9 @@ export async function verifyEvidence({ argv, env, bundlePath }) {
373
401
  mode: options.mode,
374
402
  targetRegistry: options.targetRegistry,
375
403
  now: new Date(),
376
- // The entitlement predicate carries no producer run, so there
377
- // is no workflow SHA to demand and none is invented.
378
- expectedWorkflowSha: undefined,
404
+ // The entitlement predicate carries no producer run: the commit
405
+ // its certificate is held to is the caller's statement.
406
+ expectedWorkflowSha: options.entitlementWorkflowSha,
379
407
  // A nonce spent by a producer or completeness statement of
380
408
  // this release cannot be reused by its entitlement.
381
409
  consumedNonces: result.verifiedNonces,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/xema",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/xema-dev/xema-cli.git",
@@ -51,6 +51,7 @@
51
51
  "@nestjs/platform-express": "^11.1.28",
52
52
  "@xemahq/biome-sdk": "^18.1.0",
53
53
  "@xemahq/biome-supply-chain": "^0.5.0",
54
+ "@xemahq/distribution-source-hash": "^0.3.0",
54
55
  "@xemahq/dsl": "^0.23.0",
55
56
  "@xemahq/identity-client": "^0.16.0",
56
57
  "@xemahq/contracts": "^0.76.0",
@@ -68,7 +69,6 @@
68
69
  "@types/jest": "^30.0.0",
69
70
  "@types/node": "25.2.3",
70
71
  "@xemahq/biome-host-api-client": "^0.3.31",
71
- "@xemahq/distribution-source-hash": "^0.3.0",
72
72
  "@xemahq/eslint-config": "^0.9.2",
73
73
  "esbuild": "0.28.2",
74
74
  "eslint": "^9.39.4",
@@ -92,6 +92,6 @@
92
92
  "typecheck": "tsc --noEmit"
93
93
  },
94
94
  "xemaRelease": {
95
- "sourceDigest": "sha256:15b541996401a23867e6e902df97718dd508235377acf51ed81d915fd136d8d8"
95
+ "sourceDigest": "sha256:a0dc6a3fdb7cce15b031dcd04476f19924d13d36924070b1527af1fa613b258e"
96
96
  }
97
97
  }