@xemahq/xema 0.32.0 → 0.33.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 +3 -1
- package/dist/commands/edition.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +243 -160
- package/dist/edition/verify-image-evidence.bundle.provenance.json +46 -40
- 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 +19 -0
- package/edition/checkout-indexed-source-repositories.mjs +57 -2
- package/edition/customer-definition.mjs +8 -0
- package/edition/develop-snapshot-inputs.mjs +4 -15
- package/edition/dist-check.mjs +11 -7
- package/edition/edition-biome-manifests.mjs +3 -2
- package/edition/edition-pin-verbs.mjs +183 -0
- package/edition/edition-pin.mjs +17 -0
- package/edition/edition-pinned-inputs.mjs +116 -0
- package/edition/edition-source-pins.mjs +209 -0
- package/edition/edition-unpin.mjs +16 -0
- package/edition/image-evidence.mjs +13 -7
- package/edition/indexes-at-pins.mjs +29 -0
- package/edition/lib.mjs +30 -9
- package/edition/release-catalogue.mjs +9 -8
- package/edition/release-edition-artifact.mjs +3 -2
- package/edition/resolve-distribution.mjs +13 -25
- package/edition/resolve-images.mjs +12 -11
- package/edition/seal-image-digests.mjs +12 -5
- package/edition/source-authority.mjs +1 -1
- package/package.json +2 -2
|
@@ -22802,9 +22802,9 @@ ${code}
|
|
|
22802
22802
|
doc.write(`const ${v} = typeof ${accessor} === "string" && ${inDoubt} ? ${cpLen}(${accessor}) : ${accessor}.length;`);
|
|
22803
22803
|
return v;
|
|
22804
22804
|
}
|
|
22805
|
-
function numericOperand(value,
|
|
22805
|
+
function numericOperand(value, label2) {
|
|
22806
22806
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
22807
|
-
throw new ZodCompileUnsupportedError(`${
|
|
22807
|
+
throw new ZodCompileUnsupportedError(`${label2} bound of type ${typeof value}`);
|
|
22808
22808
|
}
|
|
22809
22809
|
return `${value}`;
|
|
22810
22810
|
}
|
|
@@ -32037,10 +32037,10 @@ var require_space_ref_parser = __commonJS({
|
|
|
32037
32037
|
const example = head === SpaceRefScopeHead.Apps ? "xema://orgs/<orgId>/projects/<projectId>/apps/<appId>" : "xema://orgs/<orgId>/projects/<projectId>";
|
|
32038
32038
|
return `SpaceRef URI opens with "${head}", which addresses a tier beneath an organisation and is valid only NESTED — e.g. "${example}". A URI may open with: ${SPACE_REF_ROOT_SCOPE_HEADS.join(", ")}.`;
|
|
32039
32039
|
}
|
|
32040
|
-
function requireSegment(segments, index, raw,
|
|
32040
|
+
function requireSegment(segments, index, raw, label2) {
|
|
32041
32041
|
const value = segments[index];
|
|
32042
32042
|
if (value === void 0 || value.length === 0) {
|
|
32043
|
-
throw new space_1.SpaceRefParseError(`SpaceRef URI is missing the "${
|
|
32043
|
+
throw new space_1.SpaceRefParseError(`SpaceRef URI is missing the "${label2}" segment.`, raw);
|
|
32044
32044
|
}
|
|
32045
32045
|
return value;
|
|
32046
32046
|
}
|
|
@@ -33216,7 +33216,7 @@ var require_effective_capability_set = __commonJS({
|
|
|
33216
33216
|
}
|
|
33217
33217
|
addSecretMaterialIssues(set, context);
|
|
33218
33218
|
}
|
|
33219
|
-
function addStrictlySortedIssues(values, keyOf, context, path,
|
|
33219
|
+
function addStrictlySortedIssues(values, keyOf, context, path, label2) {
|
|
33220
33220
|
for (let index = 1; index < values.length; index += 1) {
|
|
33221
33221
|
const previous = values[index - 1];
|
|
33222
33222
|
const current = values[index];
|
|
@@ -33229,7 +33229,7 @@ var require_effective_capability_set = __commonJS({
|
|
|
33229
33229
|
context.addIssue({
|
|
33230
33230
|
code: zod_1.z.ZodIssueCode.custom,
|
|
33231
33231
|
path: [...path, index],
|
|
33232
|
-
message: currentKey === previousKey ? `${
|
|
33232
|
+
message: currentKey === previousKey ? `${label2} must not contain duplicates.` : `${label2} must be sorted in ascending canonical order.`
|
|
33233
33233
|
});
|
|
33234
33234
|
}
|
|
33235
33235
|
}
|
|
@@ -37436,10 +37436,10 @@ var require_xema_object_ref = __commonJS({
|
|
|
37436
37436
|
throw err;
|
|
37437
37437
|
}
|
|
37438
37438
|
}
|
|
37439
|
-
function requireSegment(segments, index, ref,
|
|
37439
|
+
function requireSegment(segments, index, ref, label2) {
|
|
37440
37440
|
const value = segments[index];
|
|
37441
37441
|
if (value === void 0 || value.length === 0) {
|
|
37442
|
-
throw new XemaObjectRefParseError(ref, `missing ${
|
|
37442
|
+
throw new XemaObjectRefParseError(ref, `missing ${label2} segment`);
|
|
37443
37443
|
}
|
|
37444
37444
|
return value;
|
|
37445
37445
|
}
|
|
@@ -37762,7 +37762,7 @@ ${oneOff.text}`
|
|
|
37762
37762
|
function resourceBindingKey(entry) {
|
|
37763
37763
|
return entry.ref;
|
|
37764
37764
|
}
|
|
37765
|
-
function addStrictlySortedIssues(values, keyOf, context, path,
|
|
37765
|
+
function addStrictlySortedIssues(values, keyOf, context, path, label2) {
|
|
37766
37766
|
for (let index = 1; index < values.length; index += 1) {
|
|
37767
37767
|
const previous = values[index - 1];
|
|
37768
37768
|
const current = values[index];
|
|
@@ -37775,7 +37775,7 @@ ${oneOff.text}`
|
|
|
37775
37775
|
context.addIssue({
|
|
37776
37776
|
code: zod_1.z.ZodIssueCode.custom,
|
|
37777
37777
|
path: [...path, index],
|
|
37778
|
-
message: currentKey === previousKey ? `${
|
|
37778
|
+
message: currentKey === previousKey ? `${label2} must not contain duplicates.` : `${label2} must be sorted in ascending canonical order.`
|
|
37779
37779
|
});
|
|
37780
37780
|
}
|
|
37781
37781
|
}
|
|
@@ -39355,7 +39355,7 @@ var require_launch_definition = __commonJS({
|
|
|
39355
39355
|
function isMutableRevisionAlias(revisionId) {
|
|
39356
39356
|
return /(?:^|[@:/])(?:active|current|latest)$/iu.test(revisionId);
|
|
39357
39357
|
}
|
|
39358
|
-
function addStrictlySortedIssues(values, keyOf, context, path,
|
|
39358
|
+
function addStrictlySortedIssues(values, keyOf, context, path, label2) {
|
|
39359
39359
|
for (let index = 1; index < values.length; index += 1) {
|
|
39360
39360
|
const previous = values[index - 1];
|
|
39361
39361
|
const current = values[index];
|
|
@@ -39368,7 +39368,7 @@ var require_launch_definition = __commonJS({
|
|
|
39368
39368
|
context.addIssue({
|
|
39369
39369
|
code: zod_1.z.ZodIssueCode.custom,
|
|
39370
39370
|
path: [...path, index],
|
|
39371
|
-
message: currentKey === previousKey ? `${
|
|
39371
|
+
message: currentKey === previousKey ? `${label2} must not contain duplicates.` : `${label2} must be sorted in ascending canonical order.`
|
|
39372
39372
|
});
|
|
39373
39373
|
}
|
|
39374
39374
|
}
|
|
@@ -49796,7 +49796,7 @@ var require_resolved_session_composition = __commonJS({
|
|
|
49796
49796
|
throw new TypeError(`runtimeProfileRevision.contentHash mismatch: expected ${expected}, received ${content.runtimeProfileRevision.contentHash}.`);
|
|
49797
49797
|
}
|
|
49798
49798
|
}
|
|
49799
|
-
function addStrictlySortedIssues(values, keyOf, context, path,
|
|
49799
|
+
function addStrictlySortedIssues(values, keyOf, context, path, label2) {
|
|
49800
49800
|
for (let index = 1; index < values.length; index += 1) {
|
|
49801
49801
|
const previous = values[index - 1];
|
|
49802
49802
|
const current = values[index];
|
|
@@ -49809,7 +49809,7 @@ var require_resolved_session_composition = __commonJS({
|
|
|
49809
49809
|
context.addIssue({
|
|
49810
49810
|
code: zod_1.z.ZodIssueCode.custom,
|
|
49811
49811
|
path: [...path, index],
|
|
49812
|
-
message: currentKey === previousKey ? `${
|
|
49812
|
+
message: currentKey === previousKey ? `${label2} must not contain duplicates.` : `${label2} must be sorted in ascending canonical order.`
|
|
49813
49813
|
});
|
|
49814
49814
|
}
|
|
49815
49815
|
}
|
|
@@ -50037,7 +50037,7 @@ var require_workspace = __commonJS({
|
|
|
50037
50037
|
function serializeWorkspace(input) {
|
|
50038
50038
|
return (0, content_address_1.canonicalJsonStringify)(createWorkspace(input));
|
|
50039
50039
|
}
|
|
50040
|
-
function addSortedUniqueIssues(values, context, path,
|
|
50040
|
+
function addSortedUniqueIssues(values, context, path, label2) {
|
|
50041
50041
|
if (values === void 0) {
|
|
50042
50042
|
return;
|
|
50043
50043
|
}
|
|
@@ -50051,7 +50051,7 @@ var require_workspace = __commonJS({
|
|
|
50051
50051
|
context.addIssue({
|
|
50052
50052
|
code: zod_1.z.ZodIssueCode.custom,
|
|
50053
50053
|
path: [...path, index],
|
|
50054
|
-
message: current === previous ? `${
|
|
50054
|
+
message: current === previous ? `${label2} must not contain duplicates.` : `${label2} must be sorted in ascending canonical order.`
|
|
50055
50055
|
});
|
|
50056
50056
|
}
|
|
50057
50057
|
}
|
|
@@ -55261,11 +55261,11 @@ var require_biome_closure = __commonJS({
|
|
|
55261
55261
|
function compareEdges(a, b) {
|
|
55262
55262
|
return compare(a.relationship, b.relationship) || compare(edgeTargetKey(a), edgeTargetKey(b)) || compare(a.consumer.componentKey ?? "", b.consumer.componentKey ?? "");
|
|
55263
55263
|
}
|
|
55264
|
-
function indexBiomes(facts,
|
|
55264
|
+
function indexBiomes(facts, label2) {
|
|
55265
55265
|
const map = /* @__PURE__ */ new Map();
|
|
55266
55266
|
for (const biome of facts?.biomes ?? []) {
|
|
55267
55267
|
if (map.has(biome.id)) {
|
|
55268
|
-
throw new BiomeClosureError(BiomeClosureErrorCode.AmbiguousPeer, `${
|
|
55268
|
+
throw new BiomeClosureError(BiomeClosureErrorCode.AmbiguousPeer, `${label2} names biome "${biome.id}" twice — a closure over an ambiguous catalogue cannot choose`);
|
|
55269
55269
|
}
|
|
55270
55270
|
map.set(biome.id, biome);
|
|
55271
55271
|
}
|
|
@@ -58041,7 +58041,7 @@ var require_image_producer_evidence = __commonJS({
|
|
|
58041
58041
|
return false;
|
|
58042
58042
|
if (parsed.hostname.startsWith("["))
|
|
58043
58043
|
return true;
|
|
58044
|
-
return parsed.hostname.split(".").every((
|
|
58044
|
+
return parsed.hostname.split(".").every((label2) => label2.length > 0 && label2.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label2));
|
|
58045
58045
|
} catch {
|
|
58046
58046
|
return false;
|
|
58047
58047
|
}
|
|
@@ -58287,10 +58287,10 @@ var require_image_producer_evidence = __commonJS({
|
|
|
58287
58287
|
});
|
|
58288
58288
|
}
|
|
58289
58289
|
});
|
|
58290
|
-
function parseWithSchema(
|
|
58290
|
+
function parseWithSchema(label2, schema, raw) {
|
|
58291
58291
|
const result = schema.safeParse(raw);
|
|
58292
58292
|
if (!result.success) {
|
|
58293
|
-
throw new Error(`Invalid ${
|
|
58293
|
+
throw new Error(`Invalid ${label2}: ${result.error.issues.map((issue) => `[${issue.path.join(".")}] ${issue.message}`).join("; ")}`);
|
|
58294
58294
|
}
|
|
58295
58295
|
return result.data;
|
|
58296
58296
|
}
|
|
@@ -59682,16 +59682,16 @@ import {
|
|
|
59682
59682
|
statSync as statSync2
|
|
59683
59683
|
} from "node:fs";
|
|
59684
59684
|
import { basename, dirname, isAbsolute, join as join2, relative, resolve as resolve2, sep } from "node:path";
|
|
59685
|
-
function readBoundedNoFollowFile(root2, candidate,
|
|
59685
|
+
function readBoundedNoFollowFile(root2, candidate, label2, maxBytes) {
|
|
59686
59686
|
if (typeof candidate !== "string" || candidate.length === 0 || isAbsolute(candidate)) {
|
|
59687
|
-
throw new Error(`${
|
|
59687
|
+
throw new Error(`${label2} must be a repository-relative path`);
|
|
59688
59688
|
}
|
|
59689
59689
|
const rootReal = realpathSync2(root2);
|
|
59690
59690
|
const lexicalPath = resolve2(rootReal, candidate);
|
|
59691
59691
|
const directoryReal = realpathSync2(dirname(lexicalPath));
|
|
59692
59692
|
const rel = relative(rootReal, directoryReal);
|
|
59693
59693
|
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
59694
|
-
throw new Error(`${
|
|
59694
|
+
throw new Error(`${label2} escapes its evidence root`);
|
|
59695
59695
|
}
|
|
59696
59696
|
const path = join2(directoryReal, basename(lexicalPath));
|
|
59697
59697
|
const directoryChecked = statSync2(directoryReal);
|
|
@@ -59704,7 +59704,7 @@ function readBoundedNoFollowFile(root2, candidate, label, maxBytes) {
|
|
|
59704
59704
|
} catch (error) {
|
|
59705
59705
|
if (error?.code === "ELOOP") {
|
|
59706
59706
|
throw new Error(
|
|
59707
|
-
`${
|
|
59707
|
+
`${label2} escapes its evidence root or is not a no-follow file`
|
|
59708
59708
|
);
|
|
59709
59709
|
}
|
|
59710
59710
|
throw error;
|
|
@@ -59712,14 +59712,14 @@ function readBoundedNoFollowFile(root2, candidate, label, maxBytes) {
|
|
|
59712
59712
|
try {
|
|
59713
59713
|
const directoryOpened = statSync2(directoryReal);
|
|
59714
59714
|
if (directoryOpened.dev !== directoryChecked.dev || directoryOpened.ino !== directoryChecked.ino || realpathSync2(directoryReal) !== directoryReal) {
|
|
59715
|
-
throw new Error(`${
|
|
59715
|
+
throw new Error(`${label2}'s directory changed while it was opened`);
|
|
59716
59716
|
}
|
|
59717
59717
|
const before = fstatSync(descriptor);
|
|
59718
59718
|
if (!before.isFile()) {
|
|
59719
|
-
throw new Error(`${
|
|
59719
|
+
throw new Error(`${label2} must be a no-follow regular file`);
|
|
59720
59720
|
}
|
|
59721
59721
|
if (before.size > maxBytes) {
|
|
59722
|
-
throw new Error(`${
|
|
59722
|
+
throw new Error(`${label2} exceeds ${maxBytes} bytes`);
|
|
59723
59723
|
}
|
|
59724
59724
|
const bytes = Buffer.alloc(before.size);
|
|
59725
59725
|
let offset = 0;
|
|
@@ -59732,17 +59732,17 @@ function readBoundedNoFollowFile(root2, candidate, label, maxBytes) {
|
|
|
59732
59732
|
offset
|
|
59733
59733
|
);
|
|
59734
59734
|
if (count === 0) {
|
|
59735
|
-
throw new Error(`${
|
|
59735
|
+
throw new Error(`${label2} was truncated while reading`);
|
|
59736
59736
|
}
|
|
59737
59737
|
offset += count;
|
|
59738
59738
|
}
|
|
59739
59739
|
if (readSync(descriptor, Buffer.alloc(1), 0, 1, offset) !== 0) {
|
|
59740
|
-
throw new Error(`${
|
|
59740
|
+
throw new Error(`${label2} grew while reading`);
|
|
59741
59741
|
}
|
|
59742
59742
|
const after = fstatSync(descriptor);
|
|
59743
59743
|
for (const field of ["dev", "ino", "size", "mtimeMs", "ctimeMs"]) {
|
|
59744
59744
|
if (after[field] !== before[field]) {
|
|
59745
|
-
throw new Error(`${
|
|
59745
|
+
throw new Error(`${label2} changed while reading`);
|
|
59746
59746
|
}
|
|
59747
59747
|
}
|
|
59748
59748
|
return { path, bytes };
|
|
@@ -59788,31 +59788,31 @@ var EditionProtocolMismatchError = class extends Error {
|
|
|
59788
59788
|
};
|
|
59789
59789
|
var describe = ({ version, contentDigest }) => `edition protocol v${version} (${contentDigest})`;
|
|
59790
59790
|
var supportedList = () => SUPPORTED_EDITION_PROTOCOLS.map((protocol) => `v${protocol.version}`).join(", ");
|
|
59791
|
-
function assertFinalIdentity(identity,
|
|
59791
|
+
function assertFinalIdentity(identity, label2) {
|
|
59792
59792
|
if (identity.provisional !== false) {
|
|
59793
59793
|
throw new EditionProtocolMismatchError(
|
|
59794
59794
|
EditionProtocolMismatchReason.Provisional,
|
|
59795
|
-
`${
|
|
59795
|
+
`${label2} declares ${describe(identity)} as provisional (a branch-local working identity); a provisional edition protocol is never pinned`
|
|
59796
59796
|
);
|
|
59797
59797
|
}
|
|
59798
59798
|
}
|
|
59799
|
-
function parseEditionProtocolIdentity(bytes,
|
|
59799
|
+
function parseEditionProtocolIdentity(bytes, label2) {
|
|
59800
59800
|
let identity;
|
|
59801
59801
|
try {
|
|
59802
59802
|
identity = JSON.parse(bytes.toString("utf8"));
|
|
59803
59803
|
} catch (error) {
|
|
59804
59804
|
throw new EditionProtocolMismatchError(
|
|
59805
59805
|
EditionProtocolMismatchReason.Malformed,
|
|
59806
|
-
`${
|
|
59806
|
+
`${label2} is not JSON: ${error.message}`
|
|
59807
59807
|
);
|
|
59808
59808
|
}
|
|
59809
59809
|
if (identity === null || typeof identity !== "object" || Array.isArray(identity) || Object.keys(identity).sort().join() !== "contentDigest,provisional,version" || typeof identity.provisional !== "boolean" || !Number.isSafeInteger(identity.version) || identity.version < 1 || typeof identity.contentDigest !== "string" || !SHA256_HEX.test(identity.contentDigest)) {
|
|
59810
59810
|
throw new EditionProtocolMismatchError(
|
|
59811
59811
|
EditionProtocolMismatchReason.Malformed,
|
|
59812
|
-
`${
|
|
59812
|
+
`${label2} must be exactly {"contentDigest":"<64 lowercase hex>","provisional":<boolean>,"version":<positive integer>}`
|
|
59813
59813
|
);
|
|
59814
59814
|
}
|
|
59815
|
-
assertFinalIdentity(identity,
|
|
59815
|
+
assertFinalIdentity(identity, label2);
|
|
59816
59816
|
return Object.freeze({
|
|
59817
59817
|
contentDigest: identity.contentDigest,
|
|
59818
59818
|
provisional: identity.provisional,
|
|
@@ -60406,7 +60406,7 @@ function isCanonicalDnsHost(host) {
|
|
|
60406
60406
|
}
|
|
60407
60407
|
const labels = host.split(".");
|
|
60408
60408
|
return labels.every(
|
|
60409
|
-
(
|
|
60409
|
+
(label2) => label2.length >= 1 && label2.length <= MAX_DNS_LABEL_LENGTH && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label2)
|
|
60410
60410
|
);
|
|
60411
60411
|
}
|
|
60412
60412
|
function isCanonicalRegistryAuthority(authority) {
|
|
@@ -60603,17 +60603,17 @@ var PREPARED_EXECUTABLES = /* @__PURE__ */ new WeakMap();
|
|
|
60603
60603
|
function sha256Digest(bytes) {
|
|
60604
60604
|
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
60605
60605
|
}
|
|
60606
|
-
function validatePreparedExecutable(record,
|
|
60606
|
+
function validatePreparedExecutable(record, label2) {
|
|
60607
60607
|
const stats = lstatSync(record.path);
|
|
60608
60608
|
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
60609
60609
|
throw new Error(
|
|
60610
|
-
`prepared trusted ${
|
|
60610
|
+
`prepared trusted ${label2} must be a no-follow regular file`
|
|
60611
60611
|
);
|
|
60612
60612
|
}
|
|
60613
60613
|
if (record.kind === "root-owned") {
|
|
60614
60614
|
for (const field of ["dev", "ino", "uid", "mode", "size"]) {
|
|
60615
60615
|
if (stats[field] !== record.snapshot[field]) {
|
|
60616
|
-
throw new Error(`prepared trusted ${
|
|
60616
|
+
throw new Error(`prepared trusted ${label2} file identity changed`);
|
|
60617
60617
|
}
|
|
60618
60618
|
}
|
|
60619
60619
|
let current = record.path;
|
|
@@ -60621,7 +60621,7 @@ function validatePreparedExecutable(record, label) {
|
|
|
60621
60621
|
const currentStats = lstatSync(current);
|
|
60622
60622
|
if (currentStats.uid !== 0 || (currentStats.mode & 18) !== 0) {
|
|
60623
60623
|
throw new Error(
|
|
60624
|
-
`${
|
|
60624
|
+
`${label2} and every ancestor must be root-owned and not group/world writable`
|
|
60625
60625
|
);
|
|
60626
60626
|
}
|
|
60627
60627
|
if (current === "/") {
|
|
@@ -60630,34 +60630,34 @@ function validatePreparedExecutable(record, label) {
|
|
|
60630
60630
|
current = dirname2(current);
|
|
60631
60631
|
}
|
|
60632
60632
|
if ((stats.mode & 511) !== 493) {
|
|
60633
|
-
throw new Error(`prepared trusted ${
|
|
60633
|
+
throw new Error(`prepared trusted ${label2} must have exact mode 0755`);
|
|
60634
60634
|
}
|
|
60635
60635
|
}
|
|
60636
60636
|
if (sha256Digest(readFileSync2(record.path)) !== record.digest) {
|
|
60637
|
-
throw new Error(`prepared trusted ${
|
|
60637
|
+
throw new Error(`prepared trusted ${label2} bytes changed`);
|
|
60638
60638
|
}
|
|
60639
60639
|
}
|
|
60640
60640
|
function prepareDigestPinnedExecutable({
|
|
60641
60641
|
executablePath,
|
|
60642
60642
|
trustedDigests,
|
|
60643
|
-
label,
|
|
60643
|
+
label: label2,
|
|
60644
60644
|
verifyVersion
|
|
60645
60645
|
}) {
|
|
60646
60646
|
if (!isAbsolute2(executablePath)) {
|
|
60647
|
-
throw new Error(`${
|
|
60647
|
+
throw new Error(`${label2} executable path must be absolute`);
|
|
60648
60648
|
}
|
|
60649
60649
|
const sourceStats = lstatSync(executablePath);
|
|
60650
60650
|
if (!sourceStats.isFile() || sourceStats.isSymbolicLink()) {
|
|
60651
|
-
throw new Error(`${
|
|
60651
|
+
throw new Error(`${label2} executable must be a no-follow regular file`);
|
|
60652
60652
|
}
|
|
60653
60653
|
const bytes = readFileSync2(executablePath);
|
|
60654
60654
|
const digest = sha256Digest(bytes);
|
|
60655
60655
|
if (!trustedDigests.includes(digest)) {
|
|
60656
60656
|
throw new Error(
|
|
60657
|
-
`${
|
|
60657
|
+
`${label2} executable digest ${digest} is not trusted by policy`
|
|
60658
60658
|
);
|
|
60659
60659
|
}
|
|
60660
|
-
const directory = mkdtempSync(join4(tmpdir(), `xema-trusted-${
|
|
60660
|
+
const directory = mkdtempSync(join4(tmpdir(), `xema-trusted-${label2}-`));
|
|
60661
60661
|
const privatePath = join4(directory, basename2(executablePath));
|
|
60662
60662
|
try {
|
|
60663
60663
|
let descriptor;
|
|
@@ -60695,15 +60695,15 @@ function prepareDigestPinnedExecutable({
|
|
|
60695
60695
|
function prepareRootOwnedPinnedExecutable({
|
|
60696
60696
|
executablePath,
|
|
60697
60697
|
trustedDigests,
|
|
60698
|
-
label,
|
|
60698
|
+
label: label2,
|
|
60699
60699
|
verifyVersion
|
|
60700
60700
|
}) {
|
|
60701
60701
|
if (!isAbsolute2(executablePath)) {
|
|
60702
|
-
throw new Error(`${
|
|
60702
|
+
throw new Error(`${label2} executable path must be absolute`);
|
|
60703
60703
|
}
|
|
60704
60704
|
const initialStats = lstatSync(executablePath);
|
|
60705
60705
|
if (!initialStats.isFile() || initialStats.isSymbolicLink()) {
|
|
60706
|
-
throw new Error(`${
|
|
60706
|
+
throw new Error(`${label2} executable must be a no-follow regular file`);
|
|
60707
60707
|
}
|
|
60708
60708
|
const handle = { path: executablePath, dispose() {
|
|
60709
60709
|
} };
|
|
@@ -60720,22 +60720,22 @@ function prepareRootOwnedPinnedExecutable({
|
|
|
60720
60720
|
}
|
|
60721
60721
|
};
|
|
60722
60722
|
PREPARED_EXECUTABLES.set(handle, record);
|
|
60723
|
-
validatePreparedExecutable(record,
|
|
60723
|
+
validatePreparedExecutable(record, label2);
|
|
60724
60724
|
if (!trustedDigests.includes(record.digest)) {
|
|
60725
60725
|
throw new Error(
|
|
60726
|
-
`${
|
|
60726
|
+
`${label2} executable digest ${record.digest} is not trusted by policy`
|
|
60727
60727
|
);
|
|
60728
60728
|
}
|
|
60729
60729
|
verifyVersion(executablePath);
|
|
60730
|
-
validatePreparedExecutable(record,
|
|
60730
|
+
validatePreparedExecutable(record, label2);
|
|
60731
60731
|
return handle;
|
|
60732
60732
|
}
|
|
60733
|
-
function assertPreparedExecutable(handle,
|
|
60733
|
+
function assertPreparedExecutable(handle, label2) {
|
|
60734
60734
|
const record = PREPARED_EXECUTABLES.get(handle);
|
|
60735
60735
|
if (!record) {
|
|
60736
|
-
throw new Error(`a prepared trusted ${
|
|
60736
|
+
throw new Error(`a prepared trusted ${label2} executable is required`);
|
|
60737
60737
|
}
|
|
60738
|
-
validatePreparedExecutable(record,
|
|
60738
|
+
validatePreparedExecutable(record, label2);
|
|
60739
60739
|
return record.path;
|
|
60740
60740
|
}
|
|
60741
60741
|
function exactExecutableOutput(path, args, options = {}) {
|
|
@@ -60805,13 +60805,13 @@ function prepareTrustedGitBinary({ gitBinary, policy }) {
|
|
|
60805
60805
|
}
|
|
60806
60806
|
});
|
|
60807
60807
|
}
|
|
60808
|
-
function assertContained(root2, candidate,
|
|
60808
|
+
function assertContained(root2, candidate, label2, within) {
|
|
60809
60809
|
const rel = relative2(root2, candidate);
|
|
60810
60810
|
if (rel === ".." || rel.startsWith(`..${sep2}`) || isAbsolute3(rel)) {
|
|
60811
|
-
throw new Error(`${
|
|
60811
|
+
throw new Error(`${label2} is outside ${within}`);
|
|
60812
60812
|
}
|
|
60813
60813
|
}
|
|
60814
|
-
function assertProtectedPathAncestry(path,
|
|
60814
|
+
function assertProtectedPathAncestry(path, label2) {
|
|
60815
60815
|
const currentUid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
60816
60816
|
let current = realpathSync3(path);
|
|
60817
60817
|
while (true) {
|
|
@@ -60820,7 +60820,7 @@ function assertProtectedPathAncestry(path, label) {
|
|
|
60820
60820
|
const groupOrWorldWritable = (stats.mode & 18) !== 0;
|
|
60821
60821
|
if (foreignOwner || groupOrWorldWritable) {
|
|
60822
60822
|
throw new Error(
|
|
60823
|
-
`${
|
|
60823
|
+
`${label2} and every ancestor must be verifier/root-owned and not group/world writable — ${current} is ${foreignOwner ? `owned by uid ${stats.uid} (running as uid ${currentUid})` : `mode ${(stats.mode & 4095).toString(8).padStart(4, "0")}`}`
|
|
60824
60824
|
);
|
|
60825
60825
|
}
|
|
60826
60826
|
if (current === "/") {
|
|
@@ -60848,31 +60848,31 @@ function dirtyPaths(trustedGit, repositoryRoot) {
|
|
|
60848
60848
|
}
|
|
60849
60849
|
return entries;
|
|
60850
60850
|
}
|
|
60851
|
-
function assertSafeWorktreeFile(path, expectedMode,
|
|
60851
|
+
function assertSafeWorktreeFile(path, expectedMode, label2) {
|
|
60852
60852
|
const stats = lstatSync2(path);
|
|
60853
60853
|
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
60854
|
-
throw new Error(`${
|
|
60854
|
+
throw new Error(`${label2} must be a no-follow regular file`);
|
|
60855
60855
|
}
|
|
60856
60856
|
if (typeof process.getuid === "function" && stats.uid !== process.getuid()) {
|
|
60857
|
-
throw new Error(`${
|
|
60857
|
+
throw new Error(`${label2} is not owned by the current user`);
|
|
60858
60858
|
}
|
|
60859
60859
|
const actualMode = stats.mode & 511;
|
|
60860
60860
|
if (actualMode !== expectedMode || (actualMode & 18) !== 0) {
|
|
60861
60861
|
throw new Error(
|
|
60862
|
-
`${
|
|
60862
|
+
`${label2} must have exact mode ${expectedMode.toString(8)}`
|
|
60863
60863
|
);
|
|
60864
60864
|
}
|
|
60865
60865
|
}
|
|
60866
|
-
function parseTreeRecord(record, expectedPath,
|
|
60866
|
+
function parseTreeRecord(record, expectedPath, label2) {
|
|
60867
60867
|
const match = /^(100644|100755) blob ([a-f0-9]{40,64})\t(.+)$/.exec(record);
|
|
60868
60868
|
if (!match || match[3] !== expectedPath) {
|
|
60869
60869
|
throw new Error(
|
|
60870
|
-
`${
|
|
60870
|
+
`${label2} "${expectedPath}" is absent or has an untrusted HEAD mode`
|
|
60871
60871
|
);
|
|
60872
60872
|
}
|
|
60873
60873
|
return { mode: match[1], objectId: match[2] };
|
|
60874
60874
|
}
|
|
60875
|
-
function assertNormalIndexFlags(trustedGit, repositoryRoot, path,
|
|
60875
|
+
function assertNormalIndexFlags(trustedGit, repositoryRoot, path, label2) {
|
|
60876
60876
|
const tagged = git(
|
|
60877
60877
|
trustedGit,
|
|
60878
60878
|
repositoryRoot,
|
|
@@ -60881,7 +60881,7 @@ function assertNormalIndexFlags(trustedGit, repositoryRoot, path, label) {
|
|
|
60881
60881
|
).toString("utf8").split("\0").filter(Boolean);
|
|
60882
60882
|
if (tagged.length !== 1 || !tagged[0].startsWith("H ")) {
|
|
60883
60883
|
throw new Error(
|
|
60884
|
-
`${
|
|
60884
|
+
`${label2} "${path}" has skip-worktree, assume-unchanged, or unexpected index state`
|
|
60885
60885
|
);
|
|
60886
60886
|
}
|
|
60887
60887
|
const debug = git(trustedGit, repositoryRoot, [
|
|
@@ -60892,10 +60892,10 @@ function assertNormalIndexFlags(trustedGit, repositoryRoot, path, label) {
|
|
|
60892
60892
|
]);
|
|
60893
60893
|
const flags = /\bflags:\s+(\d+)\s*$/m.exec(debug);
|
|
60894
60894
|
if (!flags || flags[1] !== "0") {
|
|
60895
|
-
throw new Error(`${
|
|
60895
|
+
throw new Error(`${label2} "${path}" has non-zero Git index flags`);
|
|
60896
60896
|
}
|
|
60897
60897
|
}
|
|
60898
|
-
function assertHeadBoundPath(trustedGit, repositoryRoot, path,
|
|
60898
|
+
function assertHeadBoundPath(trustedGit, repositoryRoot, path, label2) {
|
|
60899
60899
|
const records = git(
|
|
60900
60900
|
trustedGit,
|
|
60901
60901
|
repositoryRoot,
|
|
@@ -60903,21 +60903,21 @@ function assertHeadBoundPath(trustedGit, repositoryRoot, path, label) {
|
|
|
60903
60903
|
"buffer"
|
|
60904
60904
|
).toString("utf8").split("\0").filter(Boolean);
|
|
60905
60905
|
if (records.length !== 1) {
|
|
60906
|
-
throw new Error(`${
|
|
60906
|
+
throw new Error(`${label2} "${path}" is not one exact HEAD blob`);
|
|
60907
60907
|
}
|
|
60908
|
-
const { mode, objectId } = parseTreeRecord(records[0], path,
|
|
60909
|
-
assertNormalIndexFlags(trustedGit, repositoryRoot, path,
|
|
60908
|
+
const { mode, objectId } = parseTreeRecord(records[0], path, label2);
|
|
60909
|
+
assertNormalIndexFlags(trustedGit, repositoryRoot, path, label2);
|
|
60910
60910
|
const absolutePath = resolve3(repositoryRoot, path);
|
|
60911
60911
|
assertContained(
|
|
60912
60912
|
repositoryRoot,
|
|
60913
60913
|
absolutePath,
|
|
60914
|
-
`${
|
|
60914
|
+
`${label2} "${path}"`,
|
|
60915
60915
|
`the checkout ${repositoryRoot}`
|
|
60916
60916
|
);
|
|
60917
60917
|
assertSafeWorktreeFile(
|
|
60918
60918
|
absolutePath,
|
|
60919
60919
|
mode === "100755" ? 493 : 420,
|
|
60920
|
-
`${
|
|
60920
|
+
`${label2} "${path}"`
|
|
60921
60921
|
);
|
|
60922
60922
|
const headBytes = git(
|
|
60923
60923
|
trustedGit,
|
|
@@ -60926,10 +60926,10 @@ function assertHeadBoundPath(trustedGit, repositoryRoot, path, label) {
|
|
|
60926
60926
|
"buffer"
|
|
60927
60927
|
);
|
|
60928
60928
|
if (!readFileSync3(absolutePath).equals(headBytes)) {
|
|
60929
|
-
throw new Error(`${
|
|
60929
|
+
throw new Error(`${label2} "${path}" does not match its HEAD blob`);
|
|
60930
60930
|
}
|
|
60931
60931
|
}
|
|
60932
|
-
function locateCheckout({ root: root2, trustedGit, label }) {
|
|
60932
|
+
function locateCheckout({ root: root2, trustedGit, label: label2 }) {
|
|
60933
60933
|
assertPreparedExecutable(trustedGit, "git");
|
|
60934
60934
|
let repositoryRoot;
|
|
60935
60935
|
try {
|
|
@@ -60937,16 +60937,16 @@ function locateCheckout({ root: root2, trustedGit, label }) {
|
|
|
60937
60937
|
git(trustedGit, root2, ["rev-parse", "--show-toplevel"]).trim()
|
|
60938
60938
|
);
|
|
60939
60939
|
} catch {
|
|
60940
|
-
throw new Error(`${
|
|
60940
|
+
throw new Error(`${label2} verification requires a trusted Git checkout`);
|
|
60941
60941
|
}
|
|
60942
60942
|
const gitDirectory = realpathSync3(
|
|
60943
60943
|
git(trustedGit, repositoryRoot, ["rev-parse", "--absolute-git-dir"]).trim()
|
|
60944
60944
|
);
|
|
60945
60945
|
return { repositoryRoot, gitDirectory };
|
|
60946
60946
|
}
|
|
60947
|
-
function assertTrustedCheckoutLocation({ repositoryRoot, gitDirectory },
|
|
60948
|
-
assertProtectedPathAncestry(repositoryRoot, `${
|
|
60949
|
-
assertProtectedPathAncestry(gitDirectory, `${
|
|
60947
|
+
function assertTrustedCheckoutLocation({ repositoryRoot, gitDirectory }, label2) {
|
|
60948
|
+
assertProtectedPathAncestry(repositoryRoot, `${label2} checkout`);
|
|
60949
|
+
assertProtectedPathAncestry(gitDirectory, `${label2} Git directory`);
|
|
60950
60950
|
}
|
|
60951
60951
|
function verifyCheckoutContents({
|
|
60952
60952
|
repositoryRoot,
|
|
@@ -60965,10 +60965,10 @@ function verifyCheckoutContents({
|
|
|
60965
60965
|
if (expectedCommit !== void 0 && commit !== expectedCommit) {
|
|
60966
60966
|
throw new Error(`${repositoryRoot} HEAD changed during verification`);
|
|
60967
60967
|
}
|
|
60968
|
-
for (const { root: root2, label, requiredPaths } of requirements) {
|
|
60968
|
+
for (const { root: root2, label: label2, requiredPaths } of requirements) {
|
|
60969
60969
|
if (requiredPaths.length === 0) {
|
|
60970
60970
|
throw new Error(
|
|
60971
|
-
`the ${
|
|
60971
|
+
`the ${label2} checkout is graded for at least one path; none was named`
|
|
60972
60972
|
);
|
|
60973
60973
|
}
|
|
60974
60974
|
for (const requiredPath of requiredPaths) {
|
|
@@ -60976,14 +60976,14 @@ function verifyCheckoutContents({
|
|
|
60976
60976
|
assertContained(
|
|
60977
60977
|
root2,
|
|
60978
60978
|
absolutePath,
|
|
60979
|
-
`${
|
|
60980
|
-
`the ${
|
|
60979
|
+
`${label2} path "${requiredPath}"`,
|
|
60980
|
+
`the ${label2} ${root2}`
|
|
60981
60981
|
);
|
|
60982
60982
|
assertHeadBoundPath(
|
|
60983
60983
|
trustedGit,
|
|
60984
60984
|
repositoryRoot,
|
|
60985
60985
|
relative2(repositoryRoot, absolutePath).split(sep2).join("/"),
|
|
60986
|
-
|
|
60986
|
+
label2
|
|
60987
60987
|
);
|
|
60988
60988
|
}
|
|
60989
60989
|
}
|
|
@@ -60999,16 +60999,16 @@ function verifyCheckoutContents({
|
|
|
60999
60999
|
}
|
|
61000
61000
|
function gradeTrustedCheckouts({ grades, trustedGit, expected }) {
|
|
61001
61001
|
const byRepository = /* @__PURE__ */ new Map();
|
|
61002
|
-
for (const { root: root2, label, requiredPaths } of grades) {
|
|
61002
|
+
for (const { root: root2, label: label2, requiredPaths } of grades) {
|
|
61003
61003
|
const canonicalRoot = realpathSync3(root2);
|
|
61004
|
-
const location = locateCheckout({ root: canonicalRoot, trustedGit, label });
|
|
61004
|
+
const location = locateCheckout({ root: canonicalRoot, trustedGit, label: label2 });
|
|
61005
61005
|
const group = byRepository.get(location.repositoryRoot) ?? {
|
|
61006
61006
|
location,
|
|
61007
61007
|
labels: [],
|
|
61008
61008
|
requirements: []
|
|
61009
61009
|
};
|
|
61010
|
-
group.labels.push(
|
|
61011
|
-
group.requirements.push({ root: canonicalRoot, label, requiredPaths });
|
|
61010
|
+
group.labels.push(label2);
|
|
61011
|
+
group.requirements.push({ root: canonicalRoot, label: label2, requiredPaths });
|
|
61012
61012
|
byRepository.set(location.repositoryRoot, group);
|
|
61013
61013
|
}
|
|
61014
61014
|
if (expected !== void 0) {
|
|
@@ -61044,12 +61044,12 @@ function gradeTrustedCheckouts({ grades, trustedGit, expected }) {
|
|
|
61044
61044
|
}
|
|
61045
61045
|
return result;
|
|
61046
61046
|
}
|
|
61047
|
-
function assertExternalInput(repositoryRoots, path,
|
|
61047
|
+
function assertExternalInput(repositoryRoots, path, label2) {
|
|
61048
61048
|
const canonical = realpathSync3(path);
|
|
61049
61049
|
for (const repositoryRoot of repositoryRoots) {
|
|
61050
61050
|
const rel = relative2(repositoryRoot, canonical);
|
|
61051
61051
|
if (rel === "" || !rel.startsWith(`..${sep2}`) && rel !== "..") {
|
|
61052
|
-
throw new Error(`${
|
|
61052
|
+
throw new Error(`${label2} must be outside the checkout ${repositoryRoot}`);
|
|
61053
61053
|
}
|
|
61054
61054
|
}
|
|
61055
61055
|
return canonical;
|
|
@@ -61075,6 +61075,87 @@ import {
|
|
|
61075
61075
|
} from "node:path";
|
|
61076
61076
|
import { TextDecoder as TextDecoder2 } from "node:util";
|
|
61077
61077
|
|
|
61078
|
+
// edition/edition-source-pins.mjs
|
|
61079
|
+
var SOURCE_PINS_KEY = "sourcePins";
|
|
61080
|
+
var SOURCE_PIN_KEYS = Object.freeze(["ref", "repo", "until"]);
|
|
61081
|
+
var GRAPH_REPO = /^(repos|submodules)\/[a-z0-9][a-z0-9-]*$/;
|
|
61082
|
+
var EditionSourcePinsError = class extends Error {
|
|
61083
|
+
constructor(message, options) {
|
|
61084
|
+
super(message, options);
|
|
61085
|
+
this.name = "EditionSourcePinsError";
|
|
61086
|
+
}
|
|
61087
|
+
};
|
|
61088
|
+
var label = (id) => id === void 0 ? "" : `edition "${id}": `;
|
|
61089
|
+
function parseEditionSourcePins(raw, id) {
|
|
61090
|
+
if (raw === void 0) {
|
|
61091
|
+
return [];
|
|
61092
|
+
}
|
|
61093
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
61094
|
+
throw new EditionSourcePinsError(`${label(id)}"${SOURCE_PINS_KEY}" must be a non-empty array of {repo, ref, until}; got ${JSON.stringify(raw)}`);
|
|
61095
|
+
}
|
|
61096
|
+
const seen = /* @__PURE__ */ new Set();
|
|
61097
|
+
return raw.map((entry, index) => {
|
|
61098
|
+
const where = `${label(id)}${SOURCE_PINS_KEY}[${index}] ${JSON.stringify(entry)}`;
|
|
61099
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).sort().join() !== SOURCE_PIN_KEYS.join()) {
|
|
61100
|
+
throw new EditionSourcePinsError(`${where} must be exactly {repo, ref, until}; \`until\` (the exit condition of the hold) is required`);
|
|
61101
|
+
}
|
|
61102
|
+
if (typeof entry.repo !== "string" || !GRAPH_REPO.test(entry.repo)) {
|
|
61103
|
+
throw new EditionSourcePinsError(`${where}: repo must be a repository path (repos/<name> or submodules/<name>)`);
|
|
61104
|
+
}
|
|
61105
|
+
if (typeof entry.ref !== "string" || !COMMIT_PATTERN.test(entry.ref)) {
|
|
61106
|
+
throw new EditionSourcePinsError(`${where}: ref must be an exact lowercase 40-hex commit, not a branch, tag or abbreviation`);
|
|
61107
|
+
}
|
|
61108
|
+
if (typeof entry.until !== "string" || entry.until.trim().length === 0) {
|
|
61109
|
+
throw new EditionSourcePinsError(`${where}: until must be non-empty text stating when the hold ends`);
|
|
61110
|
+
}
|
|
61111
|
+
if (seen.has(entry.repo)) {
|
|
61112
|
+
throw new EditionSourcePinsError(`${where}: repo ${entry.repo} is pinned twice`);
|
|
61113
|
+
}
|
|
61114
|
+
seen.add(entry.repo);
|
|
61115
|
+
return Object.freeze({ repo: entry.repo, ref: entry.ref, until: entry.until });
|
|
61116
|
+
});
|
|
61117
|
+
}
|
|
61118
|
+
function splitEditionDefinition(raw, id) {
|
|
61119
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw) || !Object.hasOwn(raw, SOURCE_PINS_KEY)) {
|
|
61120
|
+
return { document: raw, sourcePins: [] };
|
|
61121
|
+
}
|
|
61122
|
+
const { [SOURCE_PINS_KEY]: declared, ...document } = raw;
|
|
61123
|
+
return { document, sourcePins: parseEditionSourcePins(declared, id) };
|
|
61124
|
+
}
|
|
61125
|
+
var canonicalRepositoryUrl = (url) => String(url).replace(/\.git$/u, "");
|
|
61126
|
+
function effectiveRepositoryPins(committedRepositoryPins, sourcePins, { id, contractAuthority } = {}) {
|
|
61127
|
+
if (sourcePins === void 0 || sourcePins.length === 0) {
|
|
61128
|
+
return committedRepositoryPins;
|
|
61129
|
+
}
|
|
61130
|
+
if (typeof contractAuthority?.repositoryUrl !== "string") {
|
|
61131
|
+
throw new EditionSourcePinsError(`${label(id)}held pins are computed against the index's contractAuthority, and none was given`);
|
|
61132
|
+
}
|
|
61133
|
+
const kernelUrl = canonicalRepositoryUrl(contractAuthority.repositoryUrl);
|
|
61134
|
+
const held = new Map(sourcePins.map((pin) => [pin.repo, pin.ref]));
|
|
61135
|
+
for (const repo of held.keys()) {
|
|
61136
|
+
const committed = committedRepositoryPins.find((pin) => pin.repo === repo);
|
|
61137
|
+
if (committed === void 0) {
|
|
61138
|
+
throw new EditionSourcePinsError(
|
|
61139
|
+
`${label(id)}${SOURCE_PINS_KEY} holds ${repo}, which has no repository pin in runtime-image-source-index.json (not an image source repository)`
|
|
61140
|
+
);
|
|
61141
|
+
}
|
|
61142
|
+
const ref = held.get(repo);
|
|
61143
|
+
if (committed.sourceCommit === ref && committed.vendoredSourceCommit === ref) {
|
|
61144
|
+
throw new EditionSourcePinsError(
|
|
61145
|
+
`${label(id)}${SOURCE_PINS_KEY} entry for ${repo} is not a hold: ${ref} is the shared repository pin; remove it (xema edition unpin)`
|
|
61146
|
+
);
|
|
61147
|
+
}
|
|
61148
|
+
if (canonicalRepositoryUrl(committed.repositoryUrl) === kernelUrl) {
|
|
61149
|
+
throw new EditionSourcePinsError(
|
|
61150
|
+
`${label(id)}${SOURCE_PINS_KEY} may not hold ${repo}: it is the kernel contract repository, whose authority is the data root's alone`
|
|
61151
|
+
);
|
|
61152
|
+
}
|
|
61153
|
+
}
|
|
61154
|
+
return committedRepositoryPins.map(
|
|
61155
|
+
(pin) => held.has(pin.repo) ? { ...pin, sourceCommit: held.get(pin.repo), vendoredSourceCommit: held.get(pin.repo) } : pin
|
|
61156
|
+
);
|
|
61157
|
+
}
|
|
61158
|
+
|
|
61078
61159
|
// edition/kernel-contract-authority.mjs
|
|
61079
61160
|
var GIT_ENV2 = Object.freeze({
|
|
61080
61161
|
GIT_CONFIG_NOSYSTEM: "1",
|
|
@@ -61099,10 +61180,10 @@ var GIT_MAX_BUFFER = 1024 * 1024 * 1024;
|
|
|
61099
61180
|
function exactAuthorityJson(value) {
|
|
61100
61181
|
return JSON.stringify(value);
|
|
61101
61182
|
}
|
|
61102
|
-
function assertKernelContractAuthorityEqual(actual, expected,
|
|
61183
|
+
function assertKernelContractAuthorityEqual(actual, expected, label2) {
|
|
61103
61184
|
if (exactAuthorityJson(actual) !== exactAuthorityJson(expected)) {
|
|
61104
61185
|
throw new Error(
|
|
61105
|
-
`${
|
|
61186
|
+
`${label2} does not exactly match kernel contract authority`
|
|
61106
61187
|
);
|
|
61107
61188
|
}
|
|
61108
61189
|
return actual;
|
|
@@ -61151,56 +61232,56 @@ function canonicalJsonBytes(value) {
|
|
|
61151
61232
|
function sha256Digest2(bytes) {
|
|
61152
61233
|
return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
|
|
61153
61234
|
}
|
|
61154
|
-
function decodeExactUtf8(bytes,
|
|
61235
|
+
function decodeExactUtf8(bytes, label2) {
|
|
61155
61236
|
let text2;
|
|
61156
61237
|
try {
|
|
61157
61238
|
text2 = STRICT_UTF8.decode(bytes);
|
|
61158
61239
|
} catch {
|
|
61159
|
-
throw new Error(`${
|
|
61240
|
+
throw new Error(`${label2} contains invalid UTF-8`);
|
|
61160
61241
|
}
|
|
61161
61242
|
if (!Buffer.from(text2, "utf8").equals(bytes)) {
|
|
61162
|
-
throw new Error(`${
|
|
61243
|
+
throw new Error(`${label2} is not exact UTF-8`);
|
|
61163
61244
|
}
|
|
61164
61245
|
return text2;
|
|
61165
61246
|
}
|
|
61166
|
-
function parseCanonicalJsonBytes(bytes,
|
|
61167
|
-
const text2 = decodeExactUtf8(bytes,
|
|
61247
|
+
function parseCanonicalJsonBytes(bytes, label2) {
|
|
61248
|
+
const text2 = decodeExactUtf8(bytes, label2);
|
|
61168
61249
|
let value;
|
|
61169
61250
|
try {
|
|
61170
61251
|
value = JSON.parse(text2);
|
|
61171
61252
|
} catch (error) {
|
|
61172
|
-
throw new Error(`${
|
|
61253
|
+
throw new Error(`${label2} is not valid JSON: ${error.message}`);
|
|
61173
61254
|
}
|
|
61174
61255
|
const canonical = canonicalJsonBytes(value);
|
|
61175
61256
|
if (!bytes.equals(canonical)) {
|
|
61176
61257
|
throw new Error(
|
|
61177
|
-
`${
|
|
61258
|
+
`${label2} is not canonical JSON (duplicate keys, non-canonical ordering, or whitespace are refused)`
|
|
61178
61259
|
);
|
|
61179
61260
|
}
|
|
61180
61261
|
return value;
|
|
61181
61262
|
}
|
|
61182
|
-
function readExactDescriptorFile(root2, path, expectedDigest,
|
|
61263
|
+
function readExactDescriptorFile(root2, path, expectedDigest, label2, maxBytes) {
|
|
61183
61264
|
if (!SHA256_DIGEST_PATTERN.test(expectedDigest)) {
|
|
61184
|
-
throw new Error(`${
|
|
61265
|
+
throw new Error(`${label2} has an invalid expected sha256 digest`);
|
|
61185
61266
|
}
|
|
61186
61267
|
const { path: resolved, bytes } = readBoundedNoFollowFile(
|
|
61187
61268
|
root2,
|
|
61188
61269
|
path,
|
|
61189
|
-
`${
|
|
61270
|
+
`${label2} path`,
|
|
61190
61271
|
maxBytes
|
|
61191
61272
|
);
|
|
61192
61273
|
const actual = sha256Digest2(bytes);
|
|
61193
61274
|
if (actual !== expectedDigest) {
|
|
61194
61275
|
throw new Error(
|
|
61195
|
-
`${
|
|
61276
|
+
`${label2} digest mismatch: expected ${expectedDigest}, got ${actual}`
|
|
61196
61277
|
);
|
|
61197
61278
|
}
|
|
61198
61279
|
return { path: resolved, bytes };
|
|
61199
61280
|
}
|
|
61200
|
-
function readCanonicalJsonFromRoot(root2, path,
|
|
61281
|
+
function readCanonicalJsonFromRoot(root2, path, label2, maxBytes = MAX_CANONICAL_JSON_BYTES) {
|
|
61201
61282
|
return parseCanonicalJsonBytes(
|
|
61202
|
-
readBoundedNoFollowFile(root2, path,
|
|
61203
|
-
|
|
61283
|
+
readBoundedNoFollowFile(root2, path, label2, maxBytes).bytes,
|
|
61284
|
+
label2
|
|
61204
61285
|
);
|
|
61205
61286
|
}
|
|
61206
61287
|
function validatePinnedTrustedRoot(policyRoot, policy) {
|
|
@@ -61222,24 +61303,24 @@ function validatePinnedTrustedRoot(policyRoot, policy) {
|
|
|
61222
61303
|
}
|
|
61223
61304
|
return { path: trustedRoot, bytes: rootBytes };
|
|
61224
61305
|
}
|
|
61225
|
-
function assertTimeWindow({ issuedAt, expiresAt, now, maxTtlSeconds, label }) {
|
|
61306
|
+
function assertTimeWindow({ issuedAt, expiresAt, now, maxTtlSeconds, label: label2 }) {
|
|
61226
61307
|
const issued = Date.parse(issuedAt);
|
|
61227
61308
|
const expires = Date.parse(expiresAt);
|
|
61228
61309
|
const instant = now instanceof Date ? now.getTime() : Date.parse(now);
|
|
61229
61310
|
if (![issued, expires, instant].every(Number.isFinite)) {
|
|
61230
|
-
throw new Error(`${
|
|
61311
|
+
throw new Error(`${label2} has an invalid time`);
|
|
61231
61312
|
}
|
|
61232
61313
|
if (issued > instant) {
|
|
61233
|
-
throw new Error(`${
|
|
61314
|
+
throw new Error(`${label2} is not yet valid`);
|
|
61234
61315
|
}
|
|
61235
61316
|
if (expires <= instant) {
|
|
61236
|
-
throw new Error(`${
|
|
61317
|
+
throw new Error(`${label2} is expired`);
|
|
61237
61318
|
}
|
|
61238
61319
|
if (expires <= issued) {
|
|
61239
|
-
throw new Error(`${
|
|
61320
|
+
throw new Error(`${label2} expiresAt must be after issuedAt`);
|
|
61240
61321
|
}
|
|
61241
61322
|
if (expires - issued > maxTtlSeconds * 1e3) {
|
|
61242
|
-
throw new Error(`${
|
|
61323
|
+
throw new Error(`${label2} exceeds the trusted maximum TTL`);
|
|
61243
61324
|
}
|
|
61244
61325
|
}
|
|
61245
61326
|
function sourceIdentity(subject) {
|
|
@@ -61254,24 +61335,24 @@ function comparePlatform(left, right) {
|
|
|
61254
61335
|
function platformIdentity(platform) {
|
|
61255
61336
|
return `${platform.os}/${platform.architecture}/${platform.variant ?? ""}`;
|
|
61256
61337
|
}
|
|
61257
|
-
function assertPlatforms(platforms, requiredPlatforms,
|
|
61338
|
+
function assertPlatforms(platforms, requiredPlatforms, label2) {
|
|
61258
61339
|
if (platforms.length === 0) {
|
|
61259
|
-
throw new Error(`${
|
|
61340
|
+
throw new Error(`${label2} omits its complete platform set`);
|
|
61260
61341
|
}
|
|
61261
61342
|
const sorted = [...platforms].sort(comparePlatform);
|
|
61262
61343
|
if (canonicalJson(platforms) !== canonicalJson(sorted)) {
|
|
61263
|
-
throw new Error(`${
|
|
61344
|
+
throw new Error(`${label2} platforms are not canonically ordered`);
|
|
61264
61345
|
}
|
|
61265
61346
|
const identities = /* @__PURE__ */ new Set();
|
|
61266
61347
|
const digests = /* @__PURE__ */ new Set();
|
|
61267
61348
|
for (const platform of platforms) {
|
|
61268
61349
|
const identity = platformIdentity(platform);
|
|
61269
61350
|
if (identities.has(identity)) {
|
|
61270
|
-
throw new Error(`${
|
|
61351
|
+
throw new Error(`${label2} has duplicate platform "${identity}"`);
|
|
61271
61352
|
}
|
|
61272
61353
|
if (digests.has(platform.manifestDigest)) {
|
|
61273
61354
|
throw new Error(
|
|
61274
|
-
`${
|
|
61355
|
+
`${label2} reuses platform digest "${platform.manifestDigest}"`
|
|
61275
61356
|
);
|
|
61276
61357
|
}
|
|
61277
61358
|
identities.add(identity);
|
|
@@ -61283,7 +61364,7 @@ function assertPlatforms(platforms, requiredPlatforms, label) {
|
|
|
61283
61364
|
].filter((identity) => !actualIdentities.has(identity)).sort();
|
|
61284
61365
|
if (missing.length > 0) {
|
|
61285
61366
|
throw new Error(
|
|
61286
|
-
`${
|
|
61367
|
+
`${label2} attests platforms ${[...actualIdentities].sort().join(", ")}, which omit ${missing.join(", ")} required by images.lock.json`
|
|
61287
61368
|
);
|
|
61288
61369
|
}
|
|
61289
61370
|
}
|
|
@@ -61310,24 +61391,24 @@ function validateProducerStatementBinding({
|
|
|
61310
61391
|
);
|
|
61311
61392
|
}
|
|
61312
61393
|
const { predicate } = statement;
|
|
61313
|
-
const
|
|
61394
|
+
const label2 = `producer evidence for ${subject.kind}:${subject.id}`;
|
|
61314
61395
|
assertKernelContractAuthorityEqual(
|
|
61315
61396
|
predicate.contractAuthority,
|
|
61316
61397
|
expectedContractAuthority,
|
|
61317
|
-
|
|
61398
|
+
label2
|
|
61318
61399
|
);
|
|
61319
61400
|
assertTimeWindow({
|
|
61320
61401
|
issuedAt: predicate.producerRun.issuedAt,
|
|
61321
61402
|
expiresAt: predicate.producerRun.expiresAt,
|
|
61322
61403
|
now,
|
|
61323
61404
|
maxTtlSeconds,
|
|
61324
|
-
label
|
|
61405
|
+
label: label2
|
|
61325
61406
|
});
|
|
61326
61407
|
if (consumedNonces.has(predicate.producerRun.nonce)) {
|
|
61327
|
-
throw new Error(`${
|
|
61408
|
+
throw new Error(`${label2} replays an already-consumed nonce`);
|
|
61328
61409
|
}
|
|
61329
61410
|
if (repositoryPin?.repo !== subject.repo || repositoryPin.sourceCommit !== subject.sourceCommit) {
|
|
61330
|
-
throw new Error(`${
|
|
61411
|
+
throw new Error(`${label2} does not match the committed repository pin`);
|
|
61331
61412
|
}
|
|
61332
61413
|
const expectedSource = {
|
|
61333
61414
|
kind: subject.kind,
|
|
@@ -61344,22 +61425,22 @@ function validateProducerStatementBinding({
|
|
|
61344
61425
|
sourceTreeHash: subject.contentHash
|
|
61345
61426
|
};
|
|
61346
61427
|
if (canonicalJson(predicate.source) !== canonicalJson(expectedSource)) {
|
|
61347
|
-
throw new Error(`${
|
|
61428
|
+
throw new Error(`${label2} source identity does not match images.lock.json`);
|
|
61348
61429
|
}
|
|
61349
61430
|
if (statement.subject[0].name !== subject.imageRef || `sha256:${statement.subject[0].digest.sha256}` !== predicate.artifact.manifestDigest || predicate.artifact.imageRepository !== subject.imageRef) {
|
|
61350
|
-
throw new Error(`${
|
|
61431
|
+
throw new Error(`${label2} has an OCI subject/digest substitution`);
|
|
61351
61432
|
}
|
|
61352
61433
|
if (typeof subject.imageDigest !== "string") {
|
|
61353
61434
|
throw new Error(
|
|
61354
|
-
`${
|
|
61435
|
+
`${label2}: images.lock.json seals no imageDigest for ${subject.kind}:${subject.id}, so the attested manifest is bound to nothing. Run \`xema edition seal\` first.`
|
|
61355
61436
|
);
|
|
61356
61437
|
}
|
|
61357
61438
|
if (subject.imageDigest !== predicate.artifact.manifestDigest) {
|
|
61358
61439
|
throw new Error(
|
|
61359
|
-
`${
|
|
61440
|
+
`${label2} attests manifest ${predicate.artifact.manifestDigest} but images.lock.json seals ${subject.imageDigest} for ${subject.imageRef}`
|
|
61360
61441
|
);
|
|
61361
61442
|
}
|
|
61362
|
-
assertPlatforms(predicate.build.platforms, subject.requiredPlatforms,
|
|
61443
|
+
assertPlatforms(predicate.build.platforms, subject.requiredPlatforms, label2);
|
|
61363
61444
|
const authorizedBuildIdentity = Boolean(producerPolicy) && producerPolicy.builderIds.includes(predicate.build.builderId) && producerPolicy.buildTypes.includes(predicate.build.buildType) && producerPolicy.manifestMediaTypes.includes(
|
|
61364
61445
|
predicate.artifact.manifestMediaType
|
|
61365
61446
|
) && pinnedAllowlistAdmits(
|
|
@@ -61371,7 +61452,7 @@ function validateProducerStatementBinding({
|
|
|
61371
61452
|
);
|
|
61372
61453
|
if (!authorizedBuildIdentity) {
|
|
61373
61454
|
throw new Error(
|
|
61374
|
-
`${
|
|
61455
|
+
`${label2} build identity, definition, parameters, or OCI media type is not authorized`
|
|
61375
61456
|
);
|
|
61376
61457
|
}
|
|
61377
61458
|
const protectedDigests = /* @__PURE__ */ new Set([
|
|
@@ -61381,7 +61462,7 @@ function validateProducerStatementBinding({
|
|
|
61381
61462
|
]);
|
|
61382
61463
|
if (protectedDigests.size !== 3) {
|
|
61383
61464
|
throw new Error(
|
|
61384
|
-
`${
|
|
61465
|
+
`${label2} substitutes an image, SBOM, or provenance digest`
|
|
61385
61466
|
);
|
|
61386
61467
|
}
|
|
61387
61468
|
return statement;
|
|
@@ -61404,32 +61485,32 @@ function validateCompletenessStatementBinding({
|
|
|
61404
61485
|
evidenceNonces = /* @__PURE__ */ new Set()
|
|
61405
61486
|
}) {
|
|
61406
61487
|
const { predicate } = statement;
|
|
61407
|
-
const
|
|
61488
|
+
const label2 = `distribution completeness for ${imageLock.distributionId}`;
|
|
61408
61489
|
assertTimeWindow({
|
|
61409
61490
|
issuedAt: predicate.issuedAt,
|
|
61410
61491
|
expiresAt: predicate.expiresAt,
|
|
61411
61492
|
now,
|
|
61412
61493
|
maxTtlSeconds,
|
|
61413
|
-
label
|
|
61494
|
+
label: label2
|
|
61414
61495
|
});
|
|
61415
61496
|
if (evidenceNonces.has(predicate.nonce)) {
|
|
61416
|
-
throw new Error(`${
|
|
61497
|
+
throw new Error(`${label2} reuses a producer nonce`);
|
|
61417
61498
|
}
|
|
61418
61499
|
if (predicate.releaseSourceCommit !== expectedReleaseSourceCommit) {
|
|
61419
61500
|
throw new Error(
|
|
61420
|
-
`${
|
|
61501
|
+
`${label2} does not bind the exact editions release source commit`
|
|
61421
61502
|
);
|
|
61422
61503
|
}
|
|
61423
61504
|
const lockDigest = normalizedImageLockDigest(imageLock);
|
|
61424
61505
|
if (predicate.distributionId !== distributionLock.distributionId || predicate.distributionInputHash !== distributionLock.inputHash || predicate.normalizedImageLockDigest !== lockDigest || statement.subject[0].name !== `xema-distribution/${distributionLock.distributionId}` || `sha256:${statement.subject[0].digest.sha256}` !== lockDigest) {
|
|
61425
|
-
throw new Error(`${
|
|
61506
|
+
throw new Error(`${label2} does not bind the selected distribution locks`);
|
|
61426
61507
|
}
|
|
61427
61508
|
const expectedImages = imageLock.subjects.map((subject) => {
|
|
61428
61509
|
const key = sourceIdentity(subject);
|
|
61429
61510
|
const producer = verifiedProducers.get(key);
|
|
61430
61511
|
const descriptor = evidenceDescriptors.get(key);
|
|
61431
61512
|
if (!producer || !descriptor) {
|
|
61432
|
-
throw new Error(`${
|
|
61513
|
+
throw new Error(`${label2} omits producer evidence for ${key}`);
|
|
61433
61514
|
}
|
|
61434
61515
|
return {
|
|
61435
61516
|
subjectKind: subject.kind,
|
|
@@ -61444,7 +61525,7 @@ function validateCompletenessStatementBinding({
|
|
|
61444
61525
|
const expected = normalizedCompletenessImages(expectedImages);
|
|
61445
61526
|
if (canonicalJson(predicate.images) !== canonicalJson(actual) || canonicalJson(actual) !== canonicalJson(expected)) {
|
|
61446
61527
|
throw new Error(
|
|
61447
|
-
`${
|
|
61528
|
+
`${label2} image set is incomplete, contains extras, or is not canonically ordered`
|
|
61448
61529
|
);
|
|
61449
61530
|
}
|
|
61450
61531
|
return statement;
|
|
@@ -61461,30 +61542,30 @@ function validateEntitlementStatementBinding({
|
|
|
61461
61542
|
consumedNonces = /* @__PURE__ */ new Set()
|
|
61462
61543
|
}) {
|
|
61463
61544
|
const { predicate } = statement;
|
|
61464
|
-
const
|
|
61545
|
+
const label2 = `delivery entitlement for ${customerId}`;
|
|
61465
61546
|
assertTimeWindow({
|
|
61466
61547
|
issuedAt: predicate.issuedAt,
|
|
61467
61548
|
expiresAt: predicate.expiresAt,
|
|
61468
61549
|
now,
|
|
61469
61550
|
maxTtlSeconds,
|
|
61470
|
-
label
|
|
61551
|
+
label: label2
|
|
61471
61552
|
});
|
|
61472
61553
|
if (consumedNonces.has(predicate.nonce)) {
|
|
61473
|
-
throw new Error(`${
|
|
61554
|
+
throw new Error(`${label2} is replayed`);
|
|
61474
61555
|
}
|
|
61475
61556
|
if (predicate.customerId !== customerId || predicate.distributionId !== distributionId || predicate.distributionStatementDigest !== completenessStatementDigest || predicate.mode !== mode || predicate.targetRegistry !== targetRegistry) {
|
|
61476
61557
|
throw new Error(
|
|
61477
|
-
`${
|
|
61558
|
+
`${label2} does not authorize the requested customer, distribution, mode, and target`
|
|
61478
61559
|
);
|
|
61479
61560
|
}
|
|
61480
61561
|
if (statement.subject[0].name !== `xema-distribution/${distributionId}` || `sha256:${statement.subject[0].digest.sha256}` !== completenessStatementDigest) {
|
|
61481
|
-
throw new Error(`${
|
|
61562
|
+
throw new Error(`${label2} substitutes the distribution release`);
|
|
61482
61563
|
}
|
|
61483
61564
|
return statement;
|
|
61484
61565
|
}
|
|
61485
|
-
function assertKernelPredicateAccepted(
|
|
61566
|
+
function assertKernelPredicateAccepted(label2, refusal) {
|
|
61486
61567
|
if (refusal !== void 0) {
|
|
61487
|
-
throw new Error(`${
|
|
61568
|
+
throw new Error(`${label2} is refused: ${refusal}`);
|
|
61488
61569
|
}
|
|
61489
61570
|
}
|
|
61490
61571
|
function assertEvidenceChannel(channel) {
|
|
@@ -62014,19 +62095,21 @@ async function loadValidatedImageLocks(distributionId, dataRoot2, schemas2, { ed
|
|
|
62014
62095
|
"images.lock.json kernel contract authority does not exactly match runtime-image-source-index.json"
|
|
62015
62096
|
);
|
|
62016
62097
|
}
|
|
62017
|
-
const
|
|
62018
|
-
runtimeSourceIndex.repositoryPins.map((repository) => [
|
|
62019
|
-
repository.repo,
|
|
62020
|
-
repository
|
|
62021
|
-
])
|
|
62022
|
-
);
|
|
62023
|
-
const distribution2 = schemas2.parseDistribution(
|
|
62098
|
+
const { document, sourcePins } = splitEditionDefinition(
|
|
62024
62099
|
JSON.parse(
|
|
62025
62100
|
readFileSync4(
|
|
62026
62101
|
join5(editionsRoot2, distributionId, "xema-distribution.json"),
|
|
62027
62102
|
"utf8"
|
|
62028
62103
|
)
|
|
62029
|
-
)
|
|
62104
|
+
),
|
|
62105
|
+
distributionId
|
|
62106
|
+
);
|
|
62107
|
+
const distribution2 = schemas2.parseDistribution(document);
|
|
62108
|
+
const repositoryPins = new Map(
|
|
62109
|
+
effectiveRepositoryPins(runtimeSourceIndex.repositoryPins, sourcePins, { id: distributionId, contractAuthority: runtimeSourceIndex.contractAuthority }).map((repository) => [
|
|
62110
|
+
repository.repo,
|
|
62111
|
+
repository
|
|
62112
|
+
])
|
|
62030
62113
|
);
|
|
62031
62114
|
if (distribution2.id !== distributionId) {
|
|
62032
62115
|
throw new Error(
|
|
@@ -62100,11 +62183,11 @@ function loadDeclaredTrustPolicy({ dataRoot: dataRoot2, declaration, schemas: sc
|
|
|
62100
62183
|
return Object.freeze({ policy, policyDigest: trustPolicyDigest(policy) });
|
|
62101
62184
|
}
|
|
62102
62185
|
function readEvidenceTrustPin({ dataRoot: dataRoot2, declaration }) {
|
|
62103
|
-
const
|
|
62186
|
+
const label2 = "declared image evidence trust pin";
|
|
62104
62187
|
const { bytes } = readBoundedNoFollowFile(
|
|
62105
62188
|
dataRoot2,
|
|
62106
62189
|
declaration.pin,
|
|
62107
|
-
|
|
62190
|
+
label2,
|
|
62108
62191
|
MAX_PIN_BYTES
|
|
62109
62192
|
);
|
|
62110
62193
|
const pin = parseStrictJson(bytes.toString("utf8"), declaration.pin);
|
|
@@ -62169,12 +62252,12 @@ function readStatedKernelContractAuthority({ dataRoot: dataRoot2, schemas: schem
|
|
|
62169
62252
|
);
|
|
62170
62253
|
return schemas2.parseRuntimeImageSourceIndex(JSON.parse(bytes.toString("utf8"))).contractAuthority;
|
|
62171
62254
|
}
|
|
62172
|
-
function assertPinnedKernelContractAuthority({ pin, authority, label }) {
|
|
62255
|
+
function assertPinnedKernelContractAuthority({ pin, authority, label: label2 }) {
|
|
62173
62256
|
const actual = kernelContractAuthorityPin(authority);
|
|
62174
62257
|
if (actual.sourceCommit !== pin.kernelContractAuthority.sourceCommit || actual.digest !== pin.kernelContractAuthority.digest) {
|
|
62175
62258
|
throw new KernelContractAuthorityPinError(
|
|
62176
62259
|
KERNEL_CONTRACT_AUTHORITY_NOT_PINNED_ONE,
|
|
62177
|
-
`${
|
|
62260
|
+
`${label2} states kernel contract authority ${actual.sourceCommit} (${actual.digest}) but the pin records ${pin.kernelContractAuthority.sourceCommit} (${pin.kernelContractAuthority.digest}); review the kernel contract change, then re-pin with \`${REPIN_COMMAND}\``
|
|
62178
62261
|
);
|
|
62179
62262
|
}
|
|
62180
62263
|
return authority;
|