@xemahq/xema 0.49.0 → 0.50.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/biome.d.ts.map +1 -1
- package/dist/commands/biome.js +6 -0
- package/dist/commands/biome.js.map +1 -1
- package/dist/dev/env-defaults.d.ts.map +1 -1
- package/dist/dev/env-defaults.js +0 -5
- package/dist/dev/env-defaults.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.provenance.json +57 -57
- package/dist/runtime/biome-publish.d.ts +4 -1
- package/dist/runtime/biome-publish.d.ts.map +1 -1
- package/dist/runtime/biome-publish.js +79 -25
- package/dist/runtime/biome-publish.js.map +1 -1
- package/dist/runtime/biome-published-content.d.ts +1 -0
- package/dist/runtime/biome-published-content.d.ts.map +1 -1
- package/dist/runtime/biome-published-content.js +3 -1
- package/dist/runtime/biome-published-content.js.map +1 -1
- package/dist/runtime/biome-published-cosign.d.ts +13 -5
- package/dist/runtime/biome-published-cosign.d.ts.map +1 -1
- package/dist/runtime/biome-published-cosign.js +47 -86
- package/dist/runtime/biome-published-cosign.js.map +1 -1
- package/edition/dist-check.mjs +2 -1
- package/edition/locked-bundle-signer.mjs +23 -6
- package/edition/resolve-distribution.mjs +49 -7
- package/edition/third-party-subjects.mjs +2 -1
- package/package.json +2 -2
package/edition/dist-check.mjs
CHANGED
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
validateServiceDependencyOwnerParity,
|
|
65
65
|
} from './lib.mjs';
|
|
66
66
|
import { commandFor } from './remedy-command.mjs';
|
|
67
|
-
import { resolveDistributionWithIndex } from './resolve-distribution.mjs';
|
|
67
|
+
import { assertReleaseGitSourcesDeclareNoBundle, resolveDistributionWithIndex } from './resolve-distribution.mjs';
|
|
68
68
|
import {
|
|
69
69
|
resolveDistributionDeclaredSources,
|
|
70
70
|
resolveDistributionRequiredPlatforms,
|
|
@@ -160,6 +160,7 @@ export async function checkDistributionLock(id) {
|
|
|
160
160
|
const lockPath = join(editionsRoot(), id, 'distribution.lock.json');
|
|
161
161
|
try {
|
|
162
162
|
const { lock: fresh, index } = await resolveDistributionWithIndex(id);
|
|
163
|
+
assertReleaseGitSourcesDeclareNoBundle(fresh, index);
|
|
163
164
|
const verdict = await compareCommittedLock(lockPath, fresh);
|
|
164
165
|
if (verdict === LockCheck.MISSING) {
|
|
165
166
|
console.error(
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// Every `bundleSource: {kind: 'oci'}` row of a lock (a biome's, or one of its host modules') states WHO signs its bundle:
|
|
4
4
|
// `bundleSigner: {policyId, signer}`, the kernel's `lockedBundleSignerOf` projection of ONE row of the data root's evidence
|
|
5
5
|
// trust policy. The row is selected, never defaulted:
|
|
6
|
-
// - a
|
|
6
|
+
// - a git-sourced row (one with a lock `source`) carries no OCI bundle at all, and a lock stating one is refused;
|
|
7
|
+
// - a third-party row (no `source`, `isThirdPartyOciRow`) by its pin (`third-party-source-pins.json`), whose `bundle.signerPolicyId` names an
|
|
7
8
|
// `oidc-certificate` row held to the checks `thirdPartySignerRow` makes;
|
|
8
9
|
// - a first-party row by the data root's `firstPartyBundleSigner.policyId`, a `public-key` row whose key is bound to
|
|
9
10
|
// no other row.
|
|
@@ -20,7 +21,7 @@ import { dataRoot, kernelDistPath, kernelPackageDir } from './edition-workspace.
|
|
|
20
21
|
import { loadPinnedDeclaredTrustPolicy } from './evidence-trust-pin.mjs';
|
|
21
22
|
import { loadTrustedThirdPartySourcePins } from './third-party-pins-trusted.mjs';
|
|
22
23
|
import { thirdPartySignerRow } from './third-party-source-evidence.mjs';
|
|
23
|
-
import { OCI_BUNDLE_SOURCE_KIND,
|
|
24
|
+
import { OCI_BUNDLE_SOURCE_KIND, isThirdPartyOciRow } from './third-party-subjects.mjs';
|
|
24
25
|
|
|
25
26
|
const requireFromHere = createRequire(import.meta.url);
|
|
26
27
|
|
|
@@ -47,6 +48,10 @@ export class BundleSignerKeyBoundElsewhereError extends BundleSignerError {}
|
|
|
47
48
|
export class BundleSignerPolicyNotSelectedError extends BundleSignerError {}
|
|
48
49
|
/** An OCI row of a lock states no `bundleSigner`. */
|
|
49
50
|
export class BundleSignerMissingError extends BundleSignerError {}
|
|
51
|
+
/** A lock row drawn from a git `source` states an OCI `bundleSource`: a git-sourced biome is built from that source, never pulled as a bundle. */
|
|
52
|
+
export class BundleSourceOnGitRowError extends BundleSignerError {}
|
|
53
|
+
/** A resolve found an OCI host-module bundle on a git-sourced biome: such a bundle reaches an edition only through an `oci` source. */
|
|
54
|
+
export class GitSourcedHostModuleBundleError extends BundleSignerError {}
|
|
50
55
|
/** The loaded kernel does not export what the bundle signer plane needs. */
|
|
51
56
|
export class BundleSignerKernelUnsupportedError extends BundleSignerError {}
|
|
52
57
|
|
|
@@ -71,6 +76,16 @@ export function loadBundleSignerKernel() {
|
|
|
71
76
|
const describeSubject = ({ biomeId, componentKey }) =>
|
|
72
77
|
componentKey === undefined ? `biome "${biomeId}"` : `host module "${componentKey}" of biome "${biomeId}"`;
|
|
73
78
|
|
|
79
|
+
/** A git-sourced row carries no OCI bundle (resolve omits it), so no signer is selected for one; a lock stating one is refused. */
|
|
80
|
+
function assertNotGitSourced(subject) {
|
|
81
|
+
if (subject.source !== undefined) {
|
|
82
|
+
throw new BundleSourceOnGitRowError(
|
|
83
|
+
`${describeSubject(subject)} is drawn from git source "${subject.source?.name ?? subject.source}" and states an OCI bundleSource; a git-sourced biome carries none ` +
|
|
84
|
+
'(a third-party biome is an OCI bundle only as an `oci` source with a third-party pin)',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
const rowsOf = (policy, policyId) => policy.policies.filter((row) => row.policyId === policyId);
|
|
75
90
|
|
|
76
91
|
/**
|
|
@@ -93,7 +108,7 @@ export function createBundleSignerSelection({ policy, thirdPartyPins, firstParty
|
|
|
93
108
|
function requireKind(subject, row, kind) {
|
|
94
109
|
if (row.kind !== kind) {
|
|
95
110
|
throw new BundleSignerKindMismatchError(
|
|
96
|
-
`${describeSubject(subject)}: trust policy row "${row.policyId}" is of kind ${JSON.stringify(row.kind)}, but a ${
|
|
111
|
+
`${describeSubject(subject)}: trust policy row "${row.policyId}" is of kind ${JSON.stringify(row.kind)}, but a ${isThirdPartyOciRow(subject) ? 'third-party' : 'first-party'} bundle is signed by a "${kind}" row`,
|
|
97
112
|
);
|
|
98
113
|
}
|
|
99
114
|
}
|
|
@@ -137,8 +152,9 @@ export function createBundleSignerSelection({ policy, thirdPartyPins, firstParty
|
|
|
137
152
|
}
|
|
138
153
|
|
|
139
154
|
function selectedRow(subject) {
|
|
155
|
+
assertNotGitSourced(subject);
|
|
140
156
|
requirePolicy(subject);
|
|
141
|
-
return
|
|
157
|
+
return isThirdPartyOciRow(subject) ? thirdPartyRow(subject) : firstPartyRow(subject);
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
const signerFor = (subject) => {
|
|
@@ -199,12 +215,12 @@ const isOci = (row) => row.bundleSource?.kind === OCI_BUNDLE_SOURCE_KIND;
|
|
|
199
215
|
/** Every OCI subject of a lock: each biome and each host module whose `bundleSource` is OCI, with the biome's origin and repo. */
|
|
200
216
|
export function lockBundleSubjects(lock) {
|
|
201
217
|
return lock.biomes.flatMap((biome) => [
|
|
202
|
-
...(isOci(biome) ? [{ row: biome, subject: { biomeId: biome.id, origin: biome.origin, repo: biome.repo, bundleSource: biome.bundleSource } }] : []),
|
|
218
|
+
...(isOci(biome) ? [{ row: biome, subject: { biomeId: biome.id, origin: biome.origin, repo: biome.repo, source: biome.source, bundleSource: biome.bundleSource } }] : []),
|
|
203
219
|
...(biome.hostModules ?? [])
|
|
204
220
|
.filter(isOci)
|
|
205
221
|
.map((hostModule) => ({
|
|
206
222
|
row: hostModule,
|
|
207
|
-
subject: { biomeId: biome.id, componentKey: hostModule.componentKey, origin: biome.origin, repo: biome.repo, bundleSource: hostModule.bundleSource },
|
|
223
|
+
subject: { biomeId: biome.id, componentKey: hostModule.componentKey, origin: biome.origin, repo: biome.repo, source: biome.source, bundleSource: hostModule.bundleSource },
|
|
208
224
|
})),
|
|
209
225
|
]);
|
|
210
226
|
}
|
|
@@ -219,6 +235,7 @@ export function assertLockBundleSigners({ lock, selection, schemas }) {
|
|
|
219
235
|
if (subjects.length === 0) {return;}
|
|
220
236
|
const chosen = selection ?? loadBundleSignerSelection({ dataRoot: dataRoot(), schemas });
|
|
221
237
|
for (const { row, subject } of subjects) {
|
|
238
|
+
assertNotGitSourced(subject);
|
|
222
239
|
chosen.assertSealed(subject, row.bundleSigner);
|
|
223
240
|
}
|
|
224
241
|
}
|
|
@@ -95,7 +95,7 @@ import {
|
|
|
95
95
|
validateImmutableDistributionLock,
|
|
96
96
|
validateServiceDependencyOwnerParity,
|
|
97
97
|
} from './lib.mjs';
|
|
98
|
-
import { BundleSignerPolicyAbsentError, loadBundleSignerSelection } from './locked-bundle-signer.mjs';
|
|
98
|
+
import { BundleSignerPolicyAbsentError, GitSourcedHostModuleBundleError, loadBundleSignerSelection } from './locked-bundle-signer.mjs';
|
|
99
99
|
import {
|
|
100
100
|
assertInstalledOperationCeilingAdmitted,
|
|
101
101
|
assertOperationGrantsAdmitted,
|
|
@@ -1805,11 +1805,29 @@ export function resolveDistributionFromInputs(
|
|
|
1805
1805
|
}
|
|
1806
1806
|
return composed.bundleSigners.signerFor(subject);
|
|
1807
1807
|
};
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1808
|
+
// A row drawn from a GIT source carries no OCI bundle (biome-level; an OCI host-module bundle is refused below), hence no bundleSigner: it is built
|
|
1809
|
+
// from the pinned source, and the manifest's `bundleSource` names a release artifact not built from this commit.
|
|
1810
|
+
// Omitted here, at the copy point. (An entry of a declared `oci` source has a stamp but no record: not git.)
|
|
1811
|
+
const isGitSourced = (e) => e.source !== undefined && sourceRecords.has(e.source);
|
|
1812
|
+
const bundleSourceOf = (e) => (e.bundleSource === undefined || isGitSourced(e) ? {} : { bundleSource: e.bundleSource });
|
|
1813
|
+
const bundleSignerFor = (e) => (e.bundleSource?.kind === 'oci' && !isGitSourced(e) ? { bundleSigner: bundleSignerOf(e, e.bundleSource) } : {});
|
|
1814
|
+
const hostModuleOf = (e, hostModule) => {
|
|
1815
|
+
if (isGitSourced(e)) {
|
|
1816
|
+
// The lock schema requires a host module's bundleSource, so an OCI one cannot be omitted: it is refused, in every
|
|
1817
|
+
// channel. A non-OCI one (npm, ...) is untouched.
|
|
1818
|
+
if (hostModule.bundleSource?.kind === 'oci') {
|
|
1819
|
+
throw new GitSourcedHostModuleBundleError(
|
|
1820
|
+
`host module "${hostModule.componentKey}" of biome "${e.id}" (git source "${e.source}") declares an OCI bundleSource (${hostModule.bundleSource.ociRef}); ` +
|
|
1821
|
+
'such a host module bundle reaches an edition only through an `oci` source with a third-party source pin',
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
return hostModule;
|
|
1825
|
+
}
|
|
1826
|
+
return hostModule.bundleSource?.kind === 'oci'
|
|
1827
|
+
? { ...hostModule, bundleSigner: bundleSignerOf(e, hostModule.bundleSource, hostModule.componentKey) }
|
|
1828
|
+
: hostModule;
|
|
1829
|
+
};
|
|
1830
|
+
const hostModulesOf = (e) => (Array.isArray(e.hostModules) ? e.hostModules : []).map((hostModule) => hostModuleOf(e, hostModule));
|
|
1813
1831
|
|
|
1814
1832
|
// (5d) Dispatched runtime components — every `runtime-image` edge of a
|
|
1815
1833
|
// shipped biome must resolve to a runtime component of a biome in the index.
|
|
@@ -1994,7 +2012,7 @@ export function resolveDistributionFromInputs(
|
|
|
1994
2012
|
// unconditional key would move the bytes of every committed lock for a
|
|
1995
2013
|
// fact none of their entries carries. A lock in which SOME biome carries
|
|
1996
2014
|
// it still is the one v5 grammar once sealed.
|
|
1997
|
-
...(e
|
|
2015
|
+
...bundleSourceOf(e),
|
|
1998
2016
|
...bundleSignerFor(e),
|
|
1999
2017
|
serviceDependencies: Array.isArray(e.serviceDependencies)
|
|
2000
2018
|
? e.serviceDependencies
|
|
@@ -2401,6 +2419,29 @@ export async function resolveDistributionWithIndex(id, { materialize, inputs, re
|
|
|
2401
2419
|
return { lock, index };
|
|
2402
2420
|
}
|
|
2403
2421
|
|
|
2422
|
+
/**
|
|
2423
|
+
* A release carries a third-party biome only as an `oci` source with a third-party pin. A git-sourced third-party biome
|
|
2424
|
+
* whose manifest declares an OCI bundleSource (biome-level or host module) names a main-release artifact that is not
|
|
2425
|
+
* built from the pinned commit, so a release resolve refuses it. `lock` is the resolved lock (a git row carries
|
|
2426
|
+
* `source`), `index` the index it was resolved from (which still holds the manifest's bundleSource).
|
|
2427
|
+
*/
|
|
2428
|
+
export function assertReleaseGitSourcesDeclareNoBundle(lock, index) {
|
|
2429
|
+
const byId = new Map(index.map((entry) => [entry.id, entry]));
|
|
2430
|
+
for (const biome of lock.biomes) {
|
|
2431
|
+
if (biome.source === undefined) {continue;}
|
|
2432
|
+
const entry = byId.get(biome.id);
|
|
2433
|
+
const declared = [entry?.bundleSource, ...(entry?.hostModules ?? []).map((hostModule) => hostModule.bundleSource)].find(
|
|
2434
|
+
(bundleSource) => bundleSource?.kind === 'oci',
|
|
2435
|
+
);
|
|
2436
|
+
if (declared !== undefined) {
|
|
2437
|
+
throw new Error(
|
|
2438
|
+
`biome "${biome.id}" is drawn from git source "${biome.source.name}" and declares an OCI bundleSource (${declared.ociRef}); ` +
|
|
2439
|
+
'a release does not carry it: declare the biome as an `oci` source with a third-party source pin instead',
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2404
2445
|
/**
|
|
2405
2446
|
* Resolve and persist one distribution lock. The CLI and the plane refresh use
|
|
2406
2447
|
* this exact writer so an in-process refresh cannot drift from the standalone
|
|
@@ -2410,6 +2451,7 @@ export async function writeResolvedDistribution(id, { log = true, materialize, f
|
|
|
2410
2451
|
const { lock, index } = await resolveDistributionWithIndex(id, { materialize });
|
|
2411
2452
|
await assertAdapterBundlesMatchHost({ lock, hostRow: adapterHostRow(index), fetchManifest });
|
|
2412
2453
|
const outPath = join(editionsRoot(), id, 'distribution.lock.json');
|
|
2454
|
+
assertReleaseGitSourcesDeclareNoBundle(lock, index);
|
|
2413
2455
|
assertReleaseContent(lock, `the distribution lock for "${id}"`);
|
|
2414
2456
|
assertReleaseEditionTree(join(editionsRoot(), id));
|
|
2415
2457
|
mkdirSync(join(editionsRoot(), id), { recursive: true });
|
|
@@ -32,7 +32,8 @@ export const isThirdPartyOciEntry = (entry) => hasOciBundleSource(entry) && isTh
|
|
|
32
32
|
/**
|
|
33
33
|
* Refuse an OCI `bundleSource` on a row whose origin is not third-party: only a third-party biome is pulled from an OCI
|
|
34
34
|
* bundle by digest, so the vendor's origin on such a row would place an unevidenced bundle in the vendor's trust domain.
|
|
35
|
-
* A row drawn from a git `source` is
|
|
35
|
+
* A row drawn from a git `source` is not this key and carries no OCI `bundleSource` at all: resolve omits the manifest's
|
|
36
|
+
* (it names a release artifact not built from the pinned commit), and a lock stating one is refused (`locked-bundle-signer.mjs`).
|
|
36
37
|
*/
|
|
37
38
|
export function assertOciBundleSourceOrigin(row, label) {
|
|
38
39
|
if (row.source === undefined && hasOciBundleSource(row) && !isThirdPartyOrigin(row)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/xema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/xema-dev/xema-cli.git",
|
|
@@ -92,6 +92,6 @@
|
|
|
92
92
|
"typecheck": "tsc --noEmit"
|
|
93
93
|
},
|
|
94
94
|
"xemaRelease": {
|
|
95
|
-
"sourceDigest": "sha256:
|
|
95
|
+
"sourceDigest": "sha256:b773e64002a4791ce49ea6b379942d02a68268b8cc6000f2d7f9fe6886732cb4"
|
|
96
96
|
}
|
|
97
97
|
}
|