@xemahq/xema 0.26.0 → 0.27.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 (229) hide show
  1. package/README.md +70 -37
  2. package/dist/assets/distribution-assets.manifest.json +1 -2
  3. package/dist/cli.js +2 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/dev.js +1 -0
  6. package/dist/commands/dev.js.map +1 -1
  7. package/dist/commands/down.d.ts.map +1 -1
  8. package/dist/commands/down.js +7 -1
  9. package/dist/commands/down.js.map +1 -1
  10. package/dist/commands/edition.d.ts +3 -0
  11. package/dist/commands/edition.d.ts.map +1 -0
  12. package/dist/commands/edition.js +46 -0
  13. package/dist/commands/edition.js.map +1 -0
  14. package/dist/commands/init.js +2 -2
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/serve.js +1 -0
  17. package/dist/commands/serve.js.map +1 -1
  18. package/dist/commands/up.js +1 -0
  19. package/dist/commands/up.js.map +1 -1
  20. package/dist/dev/env-defaults.d.ts.map +1 -1
  21. package/dist/dev/env-defaults.js +5 -1
  22. package/dist/dev/env-defaults.js.map +1 -1
  23. package/dist/dev/infra-preflight.d.ts +3 -2
  24. package/dist/dev/infra-preflight.d.ts.map +1 -1
  25. package/dist/dev/infra-preflight.js +18 -8
  26. package/dist/dev/infra-preflight.js.map +1 -1
  27. package/dist/edition/verify-image-evidence.bundle.mjs +54179 -0
  28. package/dist/edition/verify-image-evidence.bundle.provenance.json +2871 -0
  29. package/dist/runtime/config/schema.d.ts +1 -0
  30. package/dist/runtime/config/schema.d.ts.map +1 -1
  31. package/dist/runtime/custody/local-custody.d.ts +12 -0
  32. package/dist/runtime/custody/local-custody.d.ts.map +1 -0
  33. package/dist/runtime/custody/local-custody.js +22 -0
  34. package/dist/runtime/custody/local-custody.js.map +1 -0
  35. package/dist/runtime/custody/openbao-custody-bootstrap.d.ts +18 -0
  36. package/dist/runtime/custody/openbao-custody-bootstrap.d.ts.map +1 -0
  37. package/dist/runtime/custody/openbao-custody-bootstrap.js +50 -0
  38. package/dist/runtime/custody/openbao-custody-bootstrap.js.map +1 -0
  39. package/dist/runtime/custody/openbao-custody-store.d.ts +18 -0
  40. package/dist/runtime/custody/openbao-custody-store.d.ts.map +1 -0
  41. package/dist/runtime/custody/openbao-custody-store.js +55 -0
  42. package/dist/runtime/custody/openbao-custody-store.js.map +1 -0
  43. package/dist/runtime/custody/openbao-http-client.d.ts +34 -0
  44. package/dist/runtime/custody/openbao-http-client.d.ts.map +1 -0
  45. package/dist/runtime/custody/openbao-http-client.js +154 -0
  46. package/dist/runtime/custody/openbao-http-client.js.map +1 -0
  47. package/dist/runtime/custody/openbao-local-constants.d.ts +22 -0
  48. package/dist/runtime/custody/openbao-local-constants.d.ts.map +1 -0
  49. package/dist/runtime/custody/openbao-local-constants.js +48 -0
  50. package/dist/runtime/custody/openbao-local-constants.js.map +1 -0
  51. package/dist/runtime/custody/openbao-local-runner.d.ts +16 -0
  52. package/dist/runtime/custody/openbao-local-runner.d.ts.map +1 -0
  53. package/dist/runtime/custody/openbao-local-runner.js +86 -0
  54. package/dist/runtime/custody/openbao-local-runner.js.map +1 -0
  55. package/dist/runtime/edition/run-entry.d.ts +9 -0
  56. package/dist/runtime/edition/run-entry.d.ts.map +1 -0
  57. package/dist/runtime/edition/run-entry.js +36 -0
  58. package/dist/runtime/edition/run-entry.js.map +1 -0
  59. package/dist/runtime/edition/verb-map.d.ts +60 -0
  60. package/dist/runtime/edition/verb-map.d.ts.map +1 -0
  61. package/dist/runtime/edition/verb-map.js +146 -0
  62. package/dist/runtime/edition/verb-map.js.map +1 -0
  63. package/dist/runtime/env-seeder.d.ts +2 -0
  64. package/dist/runtime/env-seeder.d.ts.map +1 -1
  65. package/dist/runtime/env-seeder.js +18 -3
  66. package/dist/runtime/env-seeder.js.map +1 -1
  67. package/dist/runtime/launch.d.ts +2 -1
  68. package/dist/runtime/launch.d.ts.map +1 -1
  69. package/dist/runtime/launch.js +31 -7
  70. package/dist/runtime/launch.js.map +1 -1
  71. package/dist/runtime/paths.d.ts +1 -2
  72. package/dist/runtime/paths.d.ts.map +1 -1
  73. package/dist/runtime/paths.js +0 -10
  74. package/dist/runtime/paths.js.map +1 -1
  75. package/dist/runtime/runner/chart-local-provenance.d.ts +20 -0
  76. package/dist/runtime/runner/chart-local-provenance.d.ts.map +1 -0
  77. package/dist/runtime/runner/chart-local-provenance.js +114 -0
  78. package/dist/runtime/runner/chart-local-provenance.js.map +1 -0
  79. package/dist/runtime/runner/chart-preflight.d.ts +8 -8
  80. package/dist/runtime/runner/chart-preflight.d.ts.map +1 -1
  81. package/dist/runtime/runner/chart-preflight.js +30 -69
  82. package/dist/runtime/runner/chart-preflight.js.map +1 -1
  83. package/dist/runtime/runner/chart-pull.d.ts +14 -0
  84. package/dist/runtime/runner/chart-pull.d.ts.map +1 -0
  85. package/dist/runtime/runner/chart-pull.js +96 -0
  86. package/dist/runtime/runner/chart-pull.js.map +1 -0
  87. package/dist/runtime/runner/chart-resolver.d.ts +4 -12
  88. package/dist/runtime/runner/chart-resolver.d.ts.map +1 -1
  89. package/dist/runtime/runner/chart-resolver.js +16 -62
  90. package/dist/runtime/runner/chart-resolver.js.map +1 -1
  91. package/dist/runtime/runner/chart-set-lock.d.ts +6 -20
  92. package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
  93. package/dist/runtime/runner/chart-set-lock.js +55 -99
  94. package/dist/runtime/runner/chart-set-lock.js.map +1 -1
  95. package/dist/runtime/runner/helm-cli.d.ts +3 -1
  96. package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
  97. package/dist/runtime/runner/helm-cli.js +9 -7
  98. package/dist/runtime/runner/helm-cli.js.map +1 -1
  99. package/dist/runtime/runner/helm-runner.d.ts +4 -4
  100. package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
  101. package/dist/runtime/runner/helm-runner.js +18 -16
  102. package/dist/runtime/runner/helm-runner.js.map +1 -1
  103. package/dist/runtime/sources/kinds/git.d.ts +1 -2
  104. package/dist/runtime/sources/kinds/git.d.ts.map +1 -1
  105. package/dist/runtime/sources/kinds/git.js +8 -5
  106. package/dist/runtime/sources/kinds/git.js.map +1 -1
  107. package/dist/runtime/sources/manifest-walk.d.ts +3 -1
  108. package/dist/runtime/sources/manifest-walk.d.ts.map +1 -1
  109. package/dist/runtime/sources/manifest-walk.js +8 -3
  110. package/dist/runtime/sources/manifest-walk.js.map +1 -1
  111. package/dist/runtime/sources/schema.d.ts +3 -0
  112. package/dist/runtime/sources/schema.d.ts.map +1 -1
  113. package/dist/runtime/sources/schema.js +6 -1
  114. package/dist/runtime/sources/schema.js.map +1 -1
  115. package/dist/runtime/supervisor.d.ts +2 -0
  116. package/dist/runtime/supervisor.d.ts.map +1 -1
  117. package/dist/runtime/supervisor.js +1 -0
  118. package/dist/runtime/supervisor.js.map +1 -1
  119. package/edition/README.md +315 -0
  120. package/edition/assemble-producer-input.mjs +1251 -0
  121. package/edition/authorization-base.mjs +347 -0
  122. package/edition/biome-content.mjs +268 -0
  123. package/edition/build-biome-index.mjs +175 -0
  124. package/edition/build-platform-service-index.mjs +288 -0
  125. package/edition/build-runtime-image-source-index.mjs +821 -0
  126. package/edition/built-verifier.mjs +55 -0
  127. package/edition/catalogue-edition.mjs +49 -0
  128. package/edition/catalogue-fetch.mjs +64 -0
  129. package/edition/catalogue.mjs +135 -0
  130. package/edition/chart-set-source-commit.mjs +92 -0
  131. package/edition/check-committed-image-evidence.mjs +383 -0
  132. package/edition/checkout-indexed-source-repositories.mjs +105 -0
  133. package/edition/comment-blanker.mjs +75 -0
  134. package/edition/connector-source-authority.mjs +60 -0
  135. package/edition/customer-definition.mjs +183 -0
  136. package/edition/customer-flags.mjs +7 -0
  137. package/edition/develop-customer-edition.mjs +99 -0
  138. package/edition/develop-edition-resolve.mjs +65 -0
  139. package/edition/develop-edition.mjs +274 -0
  140. package/edition/develop-freeze.mjs +173 -0
  141. package/edition/develop-image-coordinate.mjs +54 -0
  142. package/edition/develop-seal.mjs +77 -0
  143. package/edition/develop-snapshot-inputs.mjs +104 -0
  144. package/edition/dist-check.mjs +986 -0
  145. package/edition/distribution-evidence-set.mjs +987 -0
  146. package/edition/distribution-paths.mjs +191 -0
  147. package/edition/edition-artifact.mjs +270 -0
  148. package/edition/edition-biome-manifests.mjs +85 -0
  149. package/edition/edition-channel.mjs +6 -0
  150. package/edition/edition-chart-set.mjs +93 -0
  151. package/edition/edition-config.mjs +207 -0
  152. package/edition/edition-constants.mjs +57 -0
  153. package/edition/edition-contract.json +23 -0
  154. package/edition/edition-list.mjs +35 -0
  155. package/edition/edition-locks.mjs +69 -0
  156. package/edition/edition-protocol.mjs +228 -0
  157. package/edition/edition-workspace.mjs +176 -0
  158. package/edition/evidence-checkout.mjs +418 -0
  159. package/edition/evidence-grammars.mjs +16 -0
  160. package/edition/evidence-trust-pin.mjs +304 -0
  161. package/edition/evidence-trust-policy.mjs +256 -0
  162. package/edition/external-source-walk.mjs +463 -0
  163. package/edition/first-party-source-ids.mjs +59 -0
  164. package/edition/fleet-coordinate-finality.mjs +350 -0
  165. package/edition/git-source-auth.mjs +52 -0
  166. package/edition/image-evidence-replay-state.mjs +242 -0
  167. package/edition/image-evidence.mjs +1225 -0
  168. package/edition/kernel-contract-authority.mjs +443 -0
  169. package/edition/lib.mjs +3018 -0
  170. package/edition/no-follow-file.mjs +94 -0
  171. package/edition/oci-evidence-verbs.mjs +392 -0
  172. package/edition/oci-image-source-evidence.mjs +239 -0
  173. package/edition/oras.mjs +648 -0
  174. package/edition/pin-source.mjs +227 -0
  175. package/edition/plan-image-evidence-refresh.mjs +408 -0
  176. package/edition/platform-lock-readability.mjs +892 -0
  177. package/edition/platform-service-class.mjs +18 -0
  178. package/edition/platform-service-source-authority.mjs +105 -0
  179. package/edition/positive-closure.mjs +162 -0
  180. package/edition/produce-distribution-completeness-evidence.mjs +311 -0
  181. package/edition/produce-image-delivery-entitlement-evidence.mjs +294 -0
  182. package/edition/produce-image-source-evidence.mjs +765 -0
  183. package/edition/producer-evidence-bundle.mjs +157 -0
  184. package/edition/registry-credentials.mjs +193 -0
  185. package/edition/release-catalogue.mjs +39 -0
  186. package/edition/release-channel-guard.mjs +99 -0
  187. package/edition/release-edition-artifact.mjs +158 -0
  188. package/edition/remedy-command.mjs +89 -0
  189. package/edition/repin-evidence-trust.mjs +101 -0
  190. package/edition/required-resource-provider.mjs +58 -0
  191. package/edition/resolve-chart-set.mjs +415 -0
  192. package/edition/resolve-customer-edition.mjs +288 -0
  193. package/edition/resolve-distribution.mjs +2318 -0
  194. package/edition/resolve-edition.mjs +186 -0
  195. package/edition/resolve-images.mjs +1606 -0
  196. package/edition/seal-image-digests.mjs +855 -0
  197. package/edition/sealed-evidence-accept.mjs +82 -0
  198. package/edition/source-authority.mjs +725 -0
  199. package/edition/source-closure.mjs +116 -0
  200. package/edition/strict-json.mjs +121 -0
  201. package/edition/string-order.mjs +5 -0
  202. package/edition/trust-policy-arming.mjs +120 -0
  203. package/edition/trusted-executable.mjs +166 -0
  204. package/edition/verifier-binding.mjs +45 -0
  205. package/edition/verifier-files.mjs +8 -0
  206. package/edition/verifier-kernel-schemas.mjs +22 -0
  207. package/edition/verifier-provenance.mjs +64 -0
  208. package/edition/verify-image-evidence.mjs +430 -0
  209. package/edition/verify-image-platforms.mjs +495 -0
  210. package/edition/why.mjs +56 -0
  211. package/edition/workspace-sections.mjs +195 -0
  212. package/examples/06-biome-sources.yaml +1 -0
  213. package/package.json +14 -6
  214. package/dist/commands/dist.d.ts +0 -16
  215. package/dist/commands/dist.d.ts.map +0 -1
  216. package/dist/commands/dist.js +0 -182
  217. package/dist/commands/dist.js.map +0 -1
  218. package/dist/runtime/distribution/available-biome.d.ts +0 -10
  219. package/dist/runtime/distribution/available-biome.d.ts.map +0 -1
  220. package/dist/runtime/distribution/available-biome.js +0 -156
  221. package/dist/runtime/distribution/available-biome.js.map +0 -1
  222. package/dist/runtime/distribution/compose.d.ts +0 -23
  223. package/dist/runtime/distribution/compose.d.ts.map +0 -1
  224. package/dist/runtime/distribution/compose.js +0 -48
  225. package/dist/runtime/distribution/compose.js.map +0 -1
  226. package/dist/runtime/runner/chart-trust.d.ts +0 -57
  227. package/dist/runtime/runner/chart-trust.d.ts.map +0 -1
  228. package/dist/runtime/runner/chart-trust.js +0 -270
  229. package/dist/runtime/runner/chart-trust.js.map +0 -1
@@ -0,0 +1,239 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+
5
+ import { SHA256_DIGEST_PATTERN } from './evidence-grammars.mjs';
6
+ import { canonicalJson, sha256Digest } from './image-evidence.mjs';
7
+ import { compareStrings } from './lib.mjs';
8
+ import { OrasRegistry } from './oras.mjs';
9
+ import { BUNDLE_MEDIA_TYPE } from './produce-image-source-evidence.mjs';
10
+ import {
11
+ PRODUCER_EVIDENCE_INDEX_FILE,
12
+ PRODUCER_EVIDENCE_INDEX_MEDIA_TYPE,
13
+ PRODUCER_STATEMENT_MEDIA_TYPE,
14
+ producerEvidenceLayerFiles,
15
+ readProducerEvidenceDirectory,
16
+ } from './producer-evidence-bundle.mjs';
17
+ import {
18
+ loadRegistryCredentialDeclarations,
19
+ resolveRegistryCredential,
20
+ splitImageRef,
21
+ } from './registry-credentials.mjs';
22
+
23
+ export const IMAGE_SOURCE_EVIDENCE_ARTIFACT_TYPE =
24
+ 'application/vnd.xema.image-source-evidence.v2';
25
+ export const OCI_IMAGE_MANIFEST_MEDIA_TYPE =
26
+ 'application/vnd.oci.image.manifest.v1+json';
27
+ const OCI_TITLE_ANNOTATION = 'org.opencontainers.image.title';
28
+ const OCI_CREATED_ANNOTATION = 'org.opencontainers.image.created';
29
+
30
+ /** One registry reader per authority, each with the credential the data root declares for it. */
31
+ export function createRegistryClientResolver({ dataRoot, factory }) {
32
+ const declarations = loadRegistryCredentialDeclarations(dataRoot);
33
+ const clients = new Map();
34
+ return (authority) => {
35
+ if (clients.has(authority)) {return clients.get(authority);}
36
+ const client = factory
37
+ ? factory(authority)
38
+ : new OrasRegistry({
39
+ credential: resolveRegistryCredential({ authority, declarations }),
40
+ });
41
+ clients.set(authority, client);
42
+ return client;
43
+ };
44
+ }
45
+
46
+ function exactRelativePath(value, label) {
47
+ if (
48
+ typeof value !== 'string' ||
49
+ value.length === 0 ||
50
+ value.startsWith('/') ||
51
+ value.split('/').some((part) => !part || part === '.' || part === '..')
52
+ ) {
53
+ throw new Error(`${label} is not a contained relative path`);
54
+ }
55
+ return value;
56
+ }
57
+
58
+ function expectedMediaType(path) {
59
+ if (path === PRODUCER_EVIDENCE_INDEX_FILE) {
60
+ return PRODUCER_EVIDENCE_INDEX_MEDIA_TYPE;
61
+ }
62
+ if (path.startsWith('statements/')) {return PRODUCER_STATEMENT_MEDIA_TYPE;}
63
+ if (path.startsWith('bundles/')) {return BUNDLE_MEDIA_TYPE;}
64
+ throw new Error(`OCI evidence layer has unsupported title "${path}"`);
65
+ }
66
+
67
+ export function validateImageSourceEvidenceManifest({
68
+ manifest,
69
+ subjectDigest,
70
+ }) {
71
+ if (
72
+ manifest?.schemaVersion !== 2 ||
73
+ manifest.mediaType !== OCI_IMAGE_MANIFEST_MEDIA_TYPE ||
74
+ manifest.artifactType !== IMAGE_SOURCE_EVIDENCE_ARTIFACT_TYPE ||
75
+ manifest.subject?.digest !== subjectDigest ||
76
+ !Array.isArray(manifest.layers) ||
77
+ manifest.layers.length < 3
78
+ ) {
79
+ throw new Error(
80
+ 'image-source evidence artifact is not an OCI 1.1 manifest for the exact subject',
81
+ );
82
+ }
83
+ const created = manifest.annotations?.[OCI_CREATED_ANNOTATION];
84
+ if (typeof created !== 'string' || !Number.isFinite(Date.parse(created))) {
85
+ throw new Error(`image-source evidence artifact has no valid ${OCI_CREATED_ANNOTATION}`);
86
+ }
87
+ const seen = new Set();
88
+ let indexCount = 0;
89
+ for (const layer of manifest.layers) {
90
+ const title = exactRelativePath(
91
+ layer?.annotations?.[OCI_TITLE_ANNOTATION],
92
+ 'OCI evidence layer title',
93
+ );
94
+ if (seen.has(title)) {
95
+ throw new Error(`OCI evidence manifest duplicates layer title "${title}"`);
96
+ }
97
+ seen.add(title);
98
+ if (
99
+ !SHA256_DIGEST_PATTERN.test(layer.digest ?? '') ||
100
+ !Number.isSafeInteger(layer.size) ||
101
+ layer.size < 0 ||
102
+ layer.mediaType !== expectedMediaType(title)
103
+ ) {
104
+ throw new Error(`OCI evidence layer "${title}" has an invalid descriptor`);
105
+ }
106
+ if (title === PRODUCER_EVIDENCE_INDEX_FILE) {indexCount += 1;}
107
+ }
108
+ if (indexCount !== 1) {
109
+ throw new Error('OCI evidence manifest requires exactly one producer evidence index');
110
+ }
111
+ return { created, layers: manifest.layers };
112
+ }
113
+
114
+ export function validateMaterializedImageSourceEvidence({
115
+ directory,
116
+ manifest,
117
+ subjectDigest,
118
+ schemas,
119
+ }) {
120
+ const { created, layers } = validateImageSourceEvidenceManifest({
121
+ manifest,
122
+ subjectDigest,
123
+ });
124
+ const evidence = readProducerEvidenceDirectory({ directory, schemas });
125
+ const declared = producerEvidenceLayerFiles(evidence.records)
126
+ .map(({ path, mediaType }) => `${path}\0${mediaType}`)
127
+ .sort(compareStrings);
128
+ const materialized = layers
129
+ .map(
130
+ (layer) => `${layer.annotations[OCI_TITLE_ANNOTATION]}\0${layer.mediaType}`,
131
+ )
132
+ .sort(compareStrings);
133
+ if (canonicalJson(declared) !== canonicalJson(materialized)) {
134
+ throw new Error(
135
+ 'OCI evidence layers are not the exact files declared by producer-evidence.json',
136
+ );
137
+ }
138
+ for (const record of evidence.records) {
139
+ const artifact = record.statement.predicate.artifact;
140
+ if (
141
+ artifact.manifestDigest !== subjectDigest ||
142
+ record.statement.subject[0]?.name !== artifact.imageRepository ||
143
+ `sha256:${record.statement.subject[0]?.digest?.sha256}` !== subjectDigest
144
+ ) {
145
+ throw new Error(`${record.key} statement is not bound to OCI subject ${subjectDigest}`);
146
+ }
147
+ if (record.statement.predicate.producerRun.issuedAt !== created) {
148
+ throw new Error(
149
+ `${record.key} issuance does not match the OCI artifact creation annotation`,
150
+ );
151
+ }
152
+ }
153
+ return { ...evidence, created };
154
+ }
155
+
156
+ /**
157
+ * Fetch, content-address validate and decode one OCI evidence artifact.
158
+ * `getBlob` verifies every digest again, independently of the manifest.
159
+ */
160
+ export async function readImageSourceEvidenceArtifact({
161
+ client,
162
+ repository,
163
+ subjectDigest,
164
+ artifactDigest,
165
+ schemas,
166
+ }) {
167
+ const fetched = await client.getManifestByDigest(repository, artifactDigest);
168
+ const { created, layers } = validateImageSourceEvidenceManifest({
169
+ manifest: fetched.document,
170
+ subjectDigest,
171
+ });
172
+ const directory = mkdtempSync(join(tmpdir(), 'xema-oci-evidence-'));
173
+ try {
174
+ for (const layer of layers) {
175
+ const title = layer.annotations[OCI_TITLE_ANNOTATION];
176
+ const bytes = await client.getBlob(repository, layer.digest);
177
+ if (bytes.length !== layer.size || sha256Digest(bytes) !== layer.digest) {
178
+ throw new Error(`OCI evidence layer "${title}" does not match its descriptor`);
179
+ }
180
+ const path = join(directory, title);
181
+ mkdirSync(dirname(path), { recursive: true });
182
+ writeFileSync(path, bytes, { flag: 'wx', mode: 0o600 });
183
+ }
184
+ const evidence = validateMaterializedImageSourceEvidence({
185
+ directory,
186
+ manifest: fetched.document,
187
+ subjectDigest,
188
+ schemas,
189
+ });
190
+ return { ...evidence, artifactDigest, created };
191
+ } finally {
192
+ rmSync(directory, { recursive: true, force: true });
193
+ }
194
+ }
195
+
196
+ /** Resolve the newest valid evidence referrer deterministically. */
197
+ export async function resolveImageSourceEvidenceReferrer({
198
+ client,
199
+ imageRepository,
200
+ subjectDigest,
201
+ schemas,
202
+ }) {
203
+ const { authority, repository } = splitImageRef(imageRepository);
204
+ if (client.authority !== authority) {
205
+ throw new Error(
206
+ `registry client for ${client.authority} cannot read ${imageRepository}`,
207
+ );
208
+ }
209
+ const descriptors = await client.listReferrers(
210
+ repository,
211
+ subjectDigest,
212
+ IMAGE_SOURCE_EVIDENCE_ARTIFACT_TYPE,
213
+ );
214
+ const matching = descriptors.filter(
215
+ (descriptor) =>
216
+ descriptor?.artifactType === IMAGE_SOURCE_EVIDENCE_ARTIFACT_TYPE,
217
+ );
218
+ if (matching.length === 0) {return null;}
219
+ const candidates = [];
220
+ for (const descriptor of matching) {
221
+ if (!SHA256_DIGEST_PATTERN.test(descriptor.digest ?? '')) {
222
+ throw new Error('registry returned a non-canonical evidence referrer digest');
223
+ }
224
+ candidates.push(
225
+ await readImageSourceEvidenceArtifact({
226
+ client,
227
+ repository,
228
+ subjectDigest,
229
+ artifactDigest: descriptor.digest,
230
+ schemas,
231
+ }),
232
+ );
233
+ }
234
+ candidates.sort((left, right) => {
235
+ const byCreated = compareStrings(right.created, left.created);
236
+ return byCreated || compareStrings(left.artifactDigest, right.artifactDigest);
237
+ });
238
+ return candidates[0];
239
+ }