@kungfu-tech/buildchain 2.8.5-alpha.0 → 2.8.5-alpha.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.
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"product": {
|
|
5
5
|
"name": "Buildchain",
|
|
6
6
|
"package": "@kungfu-tech/buildchain",
|
|
7
|
-
"version": "2.8.5-alpha.
|
|
7
|
+
"version": "2.8.5-alpha.1",
|
|
8
8
|
"repository": "https://github.com/kungfu-systems/buildchain"
|
|
9
9
|
},
|
|
10
10
|
"majorLine": "v2",
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
"release-state SHA is recorded as a durable audit entrance"
|
|
194
194
|
],
|
|
195
195
|
"breakingDigest": "sha256:b627e1b2ece9b6049517a942c5139e342e6077cdd3d8962d61cef8481b1f70ad",
|
|
196
|
-
"auditDigest": "sha256:
|
|
196
|
+
"auditDigest": "sha256:3476a0837292b81a6efeb7c989dc1ee975b2bd8e8882273fd4eb5f865d330bc1"
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
"contractVersion": 1,
|
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
"KFD self contract witnesses record source/artifact hashes, self-hosting boundary, and responsibility state"
|
|
219
219
|
],
|
|
220
220
|
"breakingDigest": "sha256:f46c1f67b94957ad956cf70db59a73fd5690bd67c91edf9c2d0eb8f3d1e3cf8d",
|
|
221
|
-
"auditDigest": "sha256:
|
|
221
|
+
"auditDigest": "sha256:534707144fa2c440fe6958073a8a161790f12b8fb4695e3b06e43f43a6c87ffb"
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
"contractVersion": 1,
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
"prose-only public claims downgrade the release trust passport audit"
|
|
243
243
|
],
|
|
244
244
|
"breakingDigest": "sha256:f0b636eb925ddbf45ff1a8872454ab7b4dd98857cac12bb75217830130e62a16",
|
|
245
|
-
"auditDigest": "sha256:
|
|
245
|
+
"auditDigest": "sha256:3476a0837292b81a6efeb7c989dc1ee975b2bd8e8882273fd4eb5f865d330bc1"
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"contractVersion": 1,
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
"KFD-3 passports expose releaseStatus, witness hashes, declared capability verification, reverse audit boundary, residual risk, and responsibility state"
|
|
272
272
|
],
|
|
273
273
|
"breakingDigest": "sha256:33654f3444c893754aacc42dde3051f0a371bd17c6ef7c657c579a07159cd7c1",
|
|
274
|
-
"auditDigest": "sha256:
|
|
274
|
+
"auditDigest": "sha256:534707144fa2c440fe6958073a8a161790f12b8fb4695e3b06e43f43a6c87ffb"
|
|
275
275
|
},
|
|
276
276
|
{
|
|
277
277
|
"contractVersion": 1,
|
|
@@ -342,9 +342,9 @@
|
|
|
342
342
|
"agents can discover supported Node APIs without importing internal file paths"
|
|
343
343
|
],
|
|
344
344
|
"breakingDigest": "sha256:48f925608d3e2131d90936b07dc2a30341204cae3e6e785c0f77d61ad755c945",
|
|
345
|
-
"auditDigest": "sha256:
|
|
345
|
+
"auditDigest": "sha256:0660c503639f5ca6d23ba9c7492b31ee9b05f036ffe76ed5dc8b6cb3bfde367b"
|
|
346
346
|
}
|
|
347
347
|
],
|
|
348
348
|
"compatibilityDigest": "sha256:36421b592a782decbfe31a7d8d42d70ce1100a56a2c4f4a0ccc21f0617dc72bf",
|
|
349
|
-
"contractDigest": "sha256:
|
|
349
|
+
"contractDigest": "sha256:05ae2c418b9eddbd53670c00fc2ddd778c13410f6406ac92573026a5414a1539"
|
|
350
350
|
}
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"specifier": "@kungfu-tech/buildchain/kfd-gate",
|
|
51
51
|
"export": "./kfd-gate",
|
|
52
52
|
"target": "./packages/core/kfd-gate.js",
|
|
53
|
-
"digest": "sha256:
|
|
53
|
+
"digest": "sha256:534707144fa2c440fe6958073a8a161790f12b8fb4695e3b06e43f43a6c87ffb"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"specifier": "@kungfu-tech/buildchain/release-candidate",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"specifier": "@kungfu-tech/buildchain/release-passport",
|
|
63
63
|
"export": "./release-passport",
|
|
64
64
|
"target": "./packages/core/release-passport.js",
|
|
65
|
-
"digest": "sha256:
|
|
65
|
+
"digest": "sha256:3476a0837292b81a6efeb7c989dc1ee975b2bd8e8882273fd4eb5f865d330bc1"
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
"specifier": "@kungfu-tech/buildchain/release-propagation",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.8.5-alpha.
|
|
3
|
+
"version": "2.8.5-alpha.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"vitest": "^4.1.9"
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
|
-
"@kungfu-tech/kfd": "
|
|
101
|
+
"@kungfu-tech/kfd": "github:kungfu-systems/kfd#1731900b81150ae29095329892b8d7d5670f2314",
|
|
102
102
|
"smol-toml": "1.7.0"
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -58,6 +58,14 @@ function readJsonPackageExport(exportPath) {
|
|
|
58
58
|
return JSON.parse(fs.readFileSync(require.resolve(exportPath), "utf8"));
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function tryReadJsonPackageExport(exportPath) {
|
|
62
|
+
try {
|
|
63
|
+
return readJsonPackageExport(exportPath);
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
function loadKfdPackageMetadata() {
|
|
62
70
|
const packageJson = readJsonPackageExport("@kungfu-tech/kfd/package.json");
|
|
63
71
|
const standards = readJsonPackageExport("@kungfu-tech/kfd/standards.json");
|
|
@@ -148,6 +156,50 @@ export function resolveKfd3Metadata({ requireSchemas = false } = {}) {
|
|
|
148
156
|
};
|
|
149
157
|
}
|
|
150
158
|
|
|
159
|
+
export function resolveKfd2Metadata({ requireTrustTaxonomy = false } = {}) {
|
|
160
|
+
const { packageJson, standards } = loadKfdPackageMetadata();
|
|
161
|
+
const entries = Object.entries(standards.standards || {});
|
|
162
|
+
const entry = entries.find(([key, value]) => {
|
|
163
|
+
const names = [key, value?.key, value?.id, value?.label]
|
|
164
|
+
.filter(Boolean)
|
|
165
|
+
.map((name) => String(name).toLowerCase());
|
|
166
|
+
return names.includes("kfd-2");
|
|
167
|
+
});
|
|
168
|
+
if (!entry) {
|
|
169
|
+
throw new Error("KFD metadata package does not expose KFD-2 metadata");
|
|
170
|
+
}
|
|
171
|
+
const [key, standard] = entry;
|
|
172
|
+
const schemaIds = { ...(standard.schemaIds || {}) };
|
|
173
|
+
const schemaPaths = { ...(standard.schemaPaths || {}) };
|
|
174
|
+
const trustTaxonomyPath = schemaPaths.trustTaxonomy || "";
|
|
175
|
+
const trustTaxonomy = trustTaxonomyPath
|
|
176
|
+
? tryReadJsonPackageExport(`@kungfu-tech/kfd/${trustTaxonomyPath}`)
|
|
177
|
+
: undefined;
|
|
178
|
+
if (requireTrustTaxonomy && (!schemaIds.trustTaxonomy || !trustTaxonomyPath || !trustTaxonomy)) {
|
|
179
|
+
throw new Error("KFD metadata package does not expose the KFD-2 trust taxonomy schema");
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
key: standard.key || key,
|
|
183
|
+
id: standard.id || standard.label || key,
|
|
184
|
+
label: standard.label || standard.id || key,
|
|
185
|
+
title: standard.title || "",
|
|
186
|
+
status: standard.status || "",
|
|
187
|
+
revision: standard.revision || 0,
|
|
188
|
+
package: {
|
|
189
|
+
name: packageJson.name,
|
|
190
|
+
version: packageJson.version,
|
|
191
|
+
repository: packageJson.repository?.url || "",
|
|
192
|
+
},
|
|
193
|
+
schemaIds,
|
|
194
|
+
schemaPaths,
|
|
195
|
+
hasTrustTaxonomySchema: Boolean(schemaIds.trustTaxonomy && trustTaxonomyPath && trustTaxonomy),
|
|
196
|
+
trustTaxonomy,
|
|
197
|
+
concepts: { ...(standard.concepts || {}) },
|
|
198
|
+
metadataSchema: standards.metadataSchema || {},
|
|
199
|
+
source: standards.source || {},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
151
203
|
function normalizeHash(value, label, { required = false } = {}) {
|
|
152
204
|
const text = optionalString(value).replace(/^sha256:/, "").trim();
|
|
153
205
|
if (!text && !required) {
|
|
@@ -391,6 +443,7 @@ function normalizeStringObjectArray(value, label) {
|
|
|
391
443
|
throw new Error(`${label}[${index}] must be a string or object`);
|
|
392
444
|
}
|
|
393
445
|
return {
|
|
446
|
+
...entry,
|
|
394
447
|
id: optionalString(entry.id || entry.name || entry.surface || entry.path || entry.kind || `entry-${index}`),
|
|
395
448
|
kind: optionalString(entry.kind || entry.type),
|
|
396
449
|
reason: optionalString(entry.reason || entry.rationale || entry.note || entry.description),
|
|
@@ -400,6 +453,110 @@ function normalizeStringObjectArray(value, label) {
|
|
|
400
453
|
.filter((entry) => entry.id || entry.reason);
|
|
401
454
|
}
|
|
402
455
|
|
|
456
|
+
const KFD2_TAXONOMY_FIELDS = ["riskType", "trustImpact", "machineProvability", "agentAction"];
|
|
457
|
+
|
|
458
|
+
function kfdExtensionGuidance({ taxonomy = undefined, extensionRequests = [] } = {}) {
|
|
459
|
+
const requestPath = taxonomy?.["x-kfd"]?.extensionPolicy?.requestPath || {};
|
|
460
|
+
const kfd3Request = Array.isArray(extensionRequests)
|
|
461
|
+
? extensionRequests.find((entry) => entry?.requestPath?.kind === "github-issue")
|
|
462
|
+
: undefined;
|
|
463
|
+
return {
|
|
464
|
+
action: taxonomy?.["x-kfd"]?.extensionPolicy?.standardAction || "open-kfd-extension-issue",
|
|
465
|
+
repository: requestPath.repository || kfd3Request?.requestPath?.target || "https://github.com/kungfu-systems/kfd",
|
|
466
|
+
issueUrl: requestPath.issueUrl || kfd3Request?.requestPath?.template || "https://github.com/kungfu-systems/kfd/issues/new?title=KFD-2%20trust%20taxonomy%20extension%20request",
|
|
467
|
+
appliesTo: requestPath.appliesTo || KFD2_TAXONOMY_FIELDS,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function taxonomyAllowedValues(taxonomy = {}) {
|
|
472
|
+
const xKfd = taxonomy?.["x-kfd"]?.allowedValues || {};
|
|
473
|
+
const defs = taxonomy?.$defs || {};
|
|
474
|
+
return {
|
|
475
|
+
riskType: new Set(xKfd.riskType || defs.riskType?.enum || []),
|
|
476
|
+
trustImpact: new Set(xKfd.trustImpact || defs.trustImpact?.enum || []),
|
|
477
|
+
machineProvability: new Set(xKfd.machineProvability || defs.machineProvability?.enum || []),
|
|
478
|
+
agentAction: new Set(xKfd.agentAction || defs.agentAction?.enum || []),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function validateTaxonomyField(entry, field, { allowed, label, guidance }) {
|
|
483
|
+
const value = optionalString(entry[field]).trim();
|
|
484
|
+
if (!value) {
|
|
485
|
+
throw new Error(`${label}.${field} is required by KFD-2 trust taxonomy; do not invent a local private value, ${guidance.action} at ${guidance.issueUrl}`);
|
|
486
|
+
}
|
|
487
|
+
if (!allowed[field]?.has(value)) {
|
|
488
|
+
throw new Error(`${label}.${field}=${value} is not in KFD-2 trust taxonomy; unknown taxonomy values fail validation, ${guidance.action} at ${guidance.issueUrl}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function validateKfd2TrustTaxonomyEntry(entry, {
|
|
493
|
+
kind = "residualRisk",
|
|
494
|
+
label = kind,
|
|
495
|
+
metadata = resolveKfd2Metadata({ requireTrustTaxonomy: true }),
|
|
496
|
+
extensionRequests = [],
|
|
497
|
+
} = {}) {
|
|
498
|
+
const taxonomy = metadata.trustTaxonomy;
|
|
499
|
+
const taxonomyContract = taxonomy?.contract || taxonomy?.["x-kfd"]?.contract;
|
|
500
|
+
if (!taxonomy || taxonomyContract !== "kfd-2-trust-taxonomy") {
|
|
501
|
+
const guidance = kfdExtensionGuidance({ taxonomy, extensionRequests });
|
|
502
|
+
throw new Error(`${label} requires KFD-2 trust taxonomy from @kungfu-tech/kfd standards.json; ${guidance.action} at ${guidance.issueUrl}`);
|
|
503
|
+
}
|
|
504
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
505
|
+
throw new Error(`${label} must be an object validated by KFD-2 trust taxonomy`);
|
|
506
|
+
}
|
|
507
|
+
const guidance = kfdExtensionGuidance({ taxonomy, extensionRequests });
|
|
508
|
+
const defs = taxonomy.$defs || {};
|
|
509
|
+
const required = defs[kind]?.required || (kind === "residualRisk"
|
|
510
|
+
? ["id", "definedBy", "riskType", "trustImpact", "machineProvability", "agentAction", "reason", "owner"]
|
|
511
|
+
: ["id", "riskType", "trustImpact", "reason"]);
|
|
512
|
+
for (const field of required) {
|
|
513
|
+
if (!optionalString(entry[field]).trim()) {
|
|
514
|
+
throw new Error(`${label}.${field} is required by KFD-2 trust taxonomy; do not add a private local taxonomy value, ${guidance.action} at ${guidance.issueUrl}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const allowed = taxonomyAllowedValues(taxonomy);
|
|
518
|
+
for (const field of KFD2_TAXONOMY_FIELDS) {
|
|
519
|
+
if (field in entry || required.includes(field)) {
|
|
520
|
+
validateTaxonomyField(entry, field, { allowed, label, guidance });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (kind === "residualRisk") {
|
|
524
|
+
const expectedDefinedBy = `${metadata.schemaIds.trustTaxonomy}#/$defs/residualRisk`;
|
|
525
|
+
if (entry.definedBy !== expectedDefinedBy) {
|
|
526
|
+
throw new Error(`${label}.definedBy must be ${expectedDefinedBy}; ${guidance.action} at ${guidance.issueUrl}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
...entry,
|
|
531
|
+
taxonomy: {
|
|
532
|
+
standard: metadata.key,
|
|
533
|
+
schemaId: metadata.schemaIds.trustTaxonomy,
|
|
534
|
+
schemaPath: metadata.schemaPaths.trustTaxonomy,
|
|
535
|
+
contract: taxonomyContract,
|
|
536
|
+
package: metadata.package,
|
|
537
|
+
},
|
|
538
|
+
extensionRequest: entry.extensionRequest || kfdExtensionGuidance({ taxonomy, extensionRequests }),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function normalizeKfd2TaxonomyEntries(value, {
|
|
543
|
+
kind = "residualRisk",
|
|
544
|
+
label = kind,
|
|
545
|
+
extensionRequests = [],
|
|
546
|
+
} = {}) {
|
|
547
|
+
const entries = normalizeStringObjectArray(value, label);
|
|
548
|
+
if (entries.length === 0) {
|
|
549
|
+
return [];
|
|
550
|
+
}
|
|
551
|
+
const metadata = resolveKfd2Metadata({ requireTrustTaxonomy: true });
|
|
552
|
+
return entries.map((entry, index) => validateKfd2TrustTaxonomyEntry(entry, {
|
|
553
|
+
kind,
|
|
554
|
+
label: `${label}[${index}]`,
|
|
555
|
+
metadata,
|
|
556
|
+
extensionRequests,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
|
|
403
560
|
function normalizeKfd3AuditBoundary(value = {}, { closure = {}, label = "auditBoundary" } = {}) {
|
|
404
561
|
const boundary = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
405
562
|
const sourceClosure = closure && typeof closure === "object" && !Array.isArray(closure) ? closure : {};
|
|
@@ -444,9 +601,10 @@ function normalizeKfd3AuditBoundary(value = {}, { closure = {}, label = "auditBo
|
|
|
444
601
|
};
|
|
445
602
|
}
|
|
446
603
|
|
|
447
|
-
function normalizeKfd3ResidualRisk(value = [], { auditBoundary = {} } = {}) {
|
|
604
|
+
function normalizeKfd3ResidualRisk(value = [], { auditBoundary = {}, extensionRequests = [] } = {}) {
|
|
448
605
|
const explicitRisks = normalizeStringObjectArray(value, "residualRisk");
|
|
449
606
|
const boundaryRisks = (auditBoundary.nonExhaustivelyEnumerableSurfaces || []).map((entry) => ({
|
|
607
|
+
...entry,
|
|
450
608
|
id: entry.id,
|
|
451
609
|
kind: entry.kind || "non-exhaustive-surface",
|
|
452
610
|
reason: entry.reason || "Surface cannot be exhaustively enumerated by the release passport.",
|
|
@@ -456,7 +614,11 @@ function normalizeKfd3ResidualRisk(value = [], { auditBoundary = {} } = {}) {
|
|
|
456
614
|
for (const entry of [...explicitRisks, ...boundaryRisks]) {
|
|
457
615
|
byId.set(entry.id || entry.reason, entry);
|
|
458
616
|
}
|
|
459
|
-
return [...byId.values()]
|
|
617
|
+
return normalizeKfd2TaxonomyEntries([...byId.values()], {
|
|
618
|
+
kind: "residualRisk",
|
|
619
|
+
label: "residualRisk",
|
|
620
|
+
extensionRequests,
|
|
621
|
+
});
|
|
460
622
|
}
|
|
461
623
|
|
|
462
624
|
function normalizeKfd3Responsibility(value = {}, { registry = {}, artifactVerifyCommand = "" } = {}) {
|
|
@@ -502,6 +664,14 @@ export function normalizeKfd3CollaborationInterfacePrebuildWitness(witness, { me
|
|
|
502
664
|
}
|
|
503
665
|
const registry = normalizeRegistry(witness.registry || witness.sourceRegistry || witness.source_registry || witness.collaborationInterface?.sourceRegistry || witness.collaboration_interface?.sourceRegistry);
|
|
504
666
|
const closure = witness.collaborationInterface?.closure || witness.collaboration_interface?.closure || {};
|
|
667
|
+
const extensionRequests = normalizeStringObjectArray(
|
|
668
|
+
witness.extensionRequests ||
|
|
669
|
+
witness.extension_requests ||
|
|
670
|
+
witness.collaborationInterface?.extensionRequests ||
|
|
671
|
+
witness.collaboration_interface?.extensionRequests ||
|
|
672
|
+
[],
|
|
673
|
+
"extensionRequests",
|
|
674
|
+
);
|
|
505
675
|
const auditBoundary = normalizeKfd3AuditBoundary(witness.auditBoundary || witness.audit_boundary || closure, { closure });
|
|
506
676
|
const expectedArtifactVerification = witness.expectedArtifactVerification && typeof witness.expectedArtifactVerification === "object" && !Array.isArray(witness.expectedArtifactVerification)
|
|
507
677
|
? { ...witness.expectedArtifactVerification, command: optionalString(witness.expectedArtifactVerification.command) }
|
|
@@ -534,8 +704,9 @@ export function normalizeKfd3CollaborationInterfacePrebuildWitness(witness, { me
|
|
|
534
704
|
auditBoundary,
|
|
535
705
|
residualRisk: normalizeKfd3ResidualRisk(
|
|
536
706
|
witness.residualRisk || witness.residual_risk || witness.auditBoundary?.residualRisk || witness.audit_boundary?.residual_risk || [],
|
|
537
|
-
{ auditBoundary },
|
|
707
|
+
{ auditBoundary, extensionRequests },
|
|
538
708
|
),
|
|
709
|
+
extensionRequests,
|
|
539
710
|
responsibility: normalizeKfd3Responsibility(witness.responsibility || witness.owners, {
|
|
540
711
|
registry,
|
|
541
712
|
artifactVerifyCommand: expectedArtifactVerification.command,
|
|
@@ -559,6 +730,10 @@ export function normalizeKfd3CollaborationInterfaceArtifactWitness(witness, { me
|
|
|
559
730
|
throw new Error(`KFD-3 artifact witness standard must match ${metadata.key}`);
|
|
560
731
|
}
|
|
561
732
|
const exposedSurfaces = artifactSurfacesFromWitness(witness).map((surface, index) => normalizeKfd3Surface(surface, index, "exposedSurfaces"));
|
|
733
|
+
const extensionRequests = normalizeStringObjectArray(
|
|
734
|
+
witness.extensionRequests || witness.extension_requests || [],
|
|
735
|
+
"extensionRequests",
|
|
736
|
+
);
|
|
562
737
|
return {
|
|
563
738
|
schemaVersion: 1,
|
|
564
739
|
contract: KFD3_ARTIFACT_WITNESS_CONTRACT,
|
|
@@ -574,6 +749,12 @@ export function normalizeKfd3CollaborationInterfaceArtifactWitness(witness, { me
|
|
|
574
749
|
witness.collaboration_interface?.digest,
|
|
575
750
|
),
|
|
576
751
|
exposedSurfaces,
|
|
752
|
+
residualRisk: normalizeKfd2TaxonomyEntries(witness.residualRisk || witness.residual_risk || [], {
|
|
753
|
+
kind: "residualRisk",
|
|
754
|
+
label: "artifactWitness.residualRisk",
|
|
755
|
+
extensionRequests,
|
|
756
|
+
}),
|
|
757
|
+
extensionRequests,
|
|
577
758
|
verifier: witness.verifier && typeof witness.verifier === "object" && !Array.isArray(witness.verifier)
|
|
578
759
|
? { ...witness.verifier }
|
|
579
760
|
: {},
|
|
@@ -1004,7 +1185,11 @@ export function createKfd3CollaborationInterfaceReleaseGateEvidence({
|
|
|
1004
1185
|
reasons.push(`support-level-${supportLevel}`);
|
|
1005
1186
|
}
|
|
1006
1187
|
const status = reasons.length === 0 ? "passed" : supportLevel === "release" ? "failed" : "downgraded";
|
|
1007
|
-
const
|
|
1188
|
+
const residualRiskMap = new Map();
|
|
1189
|
+
for (const risk of [...(prebuildWitness.residualRisk || []), ...(artifactWitness?.residualRisk || [])]) {
|
|
1190
|
+
residualRiskMap.set(risk.id || risk.reason, risk);
|
|
1191
|
+
}
|
|
1192
|
+
const residualRisk = [...residualRiskMap.values()];
|
|
1008
1193
|
const releaseStatus = kfd3ReleaseStatus({ comparisonStatus: status, supportLevel, residualRisk });
|
|
1009
1194
|
const trustResult = kfd3TrustResult(status);
|
|
1010
1195
|
const declaredCapabilityVerification = {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
createKfd3CollaborationInterfaceReleaseGateEvidence,
|
|
11
11
|
resolveKfd1Metadata,
|
|
12
12
|
resolveKfd3Metadata,
|
|
13
|
+
validateKfd2TrustTaxonomyEntry,
|
|
13
14
|
validateKfd1ReleaseGateEvidence,
|
|
14
15
|
validateKfd3CollaborationInterfaceReleaseGateEvidence,
|
|
15
16
|
} from "./kfd-gate.js";
|
|
@@ -625,6 +626,13 @@ function arrayOrEmpty(value) {
|
|
|
625
626
|
return Array.isArray(value) ? value : [];
|
|
626
627
|
}
|
|
627
628
|
|
|
629
|
+
function arrayOrSingleton(value) {
|
|
630
|
+
if (Array.isArray(value)) {
|
|
631
|
+
return value;
|
|
632
|
+
}
|
|
633
|
+
return value && typeof value === "object" ? [value] : [];
|
|
634
|
+
}
|
|
635
|
+
|
|
628
636
|
function normalizeKfd2Claim(raw = {}, index = 0) {
|
|
629
637
|
const claim = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
630
638
|
const sourceBindings = arrayOrEmpty(claim.sourceBindings || claim.source_bindings || claim.sources || claim.declaredSources);
|
|
@@ -638,7 +646,15 @@ function normalizeKfd2Claim(raw = {}, index = 0) {
|
|
|
638
646
|
? claim.audit_boundary
|
|
639
647
|
: {};
|
|
640
648
|
const responsibility = claim.responsibility && typeof claim.responsibility === "object" && !Array.isArray(claim.responsibility) ? claim.responsibility : {};
|
|
641
|
-
const residualRisk =
|
|
649
|
+
const residualRisk = arrayOrSingleton(claim.residualRisk || claim.residual_risk).map((entry, riskIndex) => validateKfd2TrustTaxonomyEntry(entry, {
|
|
650
|
+
kind: "residualRisk",
|
|
651
|
+
label: `kfd-2.claims[${index}].residualRisk[${riskIndex}]`,
|
|
652
|
+
}));
|
|
653
|
+
const downgradeReasons = arrayOrSingleton(claim.downgradeReasons || claim.downgrade_reasons || claim.downgradeReason || claim.downgrade_reason)
|
|
654
|
+
.map((entry, reasonIndex) => validateKfd2TrustTaxonomyEntry(entry, {
|
|
655
|
+
kind: "downgradeReason",
|
|
656
|
+
label: `kfd-2.claims[${index}].downgradeReasons[${reasonIndex}]`,
|
|
657
|
+
}));
|
|
642
658
|
const trustProof = claim.trustProof && typeof claim.trustProof === "object" && !Array.isArray(claim.trustProof)
|
|
643
659
|
? claim.trustProof
|
|
644
660
|
: undefined;
|
|
@@ -668,6 +684,7 @@ function normalizeKfd2Claim(raw = {}, index = 0) {
|
|
|
668
684
|
auditBoundary,
|
|
669
685
|
responsibility,
|
|
670
686
|
residualRisk,
|
|
687
|
+
downgradeReasons,
|
|
671
688
|
...(trustProof ? { trustProof } : {}),
|
|
672
689
|
proseOnly,
|
|
673
690
|
missingBindings,
|
|
@@ -789,6 +806,7 @@ function createKfd2ReleaseTrustPassportAudit({ explicitClaims = [], kfd1Section
|
|
|
789
806
|
}
|
|
790
807
|
const failed = claims.filter((claim) => claim.public && claim.status === "failed");
|
|
791
808
|
const downgraded = claims.filter((claim) => claim.public && (claim.status === "downgraded" || claim.proseOnly));
|
|
809
|
+
const downgradeReasons = claims.flatMap((claim) => arrayOrEmpty(claim.downgradeReasons));
|
|
792
810
|
return {
|
|
793
811
|
schemaVersion: 1,
|
|
794
812
|
contract: KFD2_RELEASE_TRUST_PASSPORT_CONTRACT,
|
|
@@ -798,6 +816,7 @@ function createKfd2ReleaseTrustPassportAudit({ explicitClaims = [], kfd1Section
|
|
|
798
816
|
scope: "public release claims visible to humans or agents",
|
|
799
817
|
policy: "public claims must bind declared sources, machine-readable evidence, hashes, artifact coordinates, verification results, audit boundaries, responsibility state, and residual risk",
|
|
800
818
|
},
|
|
819
|
+
downgradeReasons,
|
|
801
820
|
claims,
|
|
802
821
|
summary: {
|
|
803
822
|
claimCount: claims.length,
|
|
@@ -1307,6 +1326,18 @@ function validateKfd2ReleaseTrustPassportAudit(section, issues) {
|
|
|
1307
1326
|
if (claims.length === 0) {
|
|
1308
1327
|
issues.push(issue("error", "kfd-2.claims.empty", "kfd-2 audit must enumerate at least one public release claim"));
|
|
1309
1328
|
}
|
|
1329
|
+
for (const [reasonIndex, reason] of arrayOrEmpty(section.downgradeReasons).entries()) {
|
|
1330
|
+
try {
|
|
1331
|
+
validateKfd2TrustTaxonomyEntry(reason, {
|
|
1332
|
+
kind: "downgradeReason",
|
|
1333
|
+
label: `kfd-2.downgradeReasons[${reasonIndex}]`,
|
|
1334
|
+
});
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
issues.push(issue("error", `kfd-2.downgradeReasons[${reasonIndex}]`, error.message, {
|
|
1337
|
+
id: reason?.id || "",
|
|
1338
|
+
}));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1310
1341
|
for (const [index, claim] of claims.entries()) {
|
|
1311
1342
|
if (!claim.id || !claim.claim) {
|
|
1312
1343
|
issues.push(issue("error", `kfd-2.claims[${index}].identity`, "public release claim must include id and statement"));
|
|
@@ -1326,6 +1357,30 @@ function validateKfd2ReleaseTrustPassportAudit(section, issues) {
|
|
|
1326
1357
|
proseOnly: Boolean(claim.proseOnly),
|
|
1327
1358
|
}));
|
|
1328
1359
|
}
|
|
1360
|
+
for (const [riskIndex, risk] of arrayOrSingleton(claim.residualRisk || claim.residual_risk).entries()) {
|
|
1361
|
+
try {
|
|
1362
|
+
validateKfd2TrustTaxonomyEntry(risk, {
|
|
1363
|
+
kind: "residualRisk",
|
|
1364
|
+
label: `kfd-2.claims[${index}].residualRisk[${riskIndex}]`,
|
|
1365
|
+
});
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
issues.push(issue("error", `kfd-2.claims[${index}].residualRisk[${riskIndex}]`, error.message, {
|
|
1368
|
+
id: risk?.id || claim.id || "",
|
|
1369
|
+
}));
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
for (const [reasonIndex, reason] of arrayOrSingleton(claim.downgradeReasons || claim.downgrade_reasons || claim.downgradeReason || claim.downgrade_reason).entries()) {
|
|
1373
|
+
try {
|
|
1374
|
+
validateKfd2TrustTaxonomyEntry(reason, {
|
|
1375
|
+
kind: "downgradeReason",
|
|
1376
|
+
label: `kfd-2.claims[${index}].downgradeReasons[${reasonIndex}]`,
|
|
1377
|
+
});
|
|
1378
|
+
} catch (error) {
|
|
1379
|
+
issues.push(issue("error", `kfd-2.claims[${index}].downgradeReasons[${reasonIndex}]`, error.message, {
|
|
1380
|
+
id: reason?.id || claim.id || "",
|
|
1381
|
+
}));
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1329
1384
|
if (String(claim.id || "").startsWith("kfd-3:")) {
|
|
1330
1385
|
const trustProof = claim.trustProof && typeof claim.trustProof === "object" && !Array.isArray(claim.trustProof)
|
|
1331
1386
|
? claim.trustProof
|
|
@@ -1360,6 +1415,19 @@ function validateKfd2ReleaseTrustPassportAudit(section, issues) {
|
|
|
1360
1415
|
issues.push(issue("error", `kfd-2.claims[${index}].trustProof.residualRisk`, "KFD-2 trust proof must preserve residual risk as an array", {
|
|
1361
1416
|
id: claim.id || "",
|
|
1362
1417
|
}));
|
|
1418
|
+
} else {
|
|
1419
|
+
for (const [riskIndex, risk] of trustProof.residualRisk.entries()) {
|
|
1420
|
+
try {
|
|
1421
|
+
validateKfd2TrustTaxonomyEntry(risk, {
|
|
1422
|
+
kind: "residualRisk",
|
|
1423
|
+
label: `kfd-2.claims[${index}].trustProof.residualRisk[${riskIndex}]`,
|
|
1424
|
+
});
|
|
1425
|
+
} catch (error) {
|
|
1426
|
+
issues.push(issue("error", `kfd-2.claims[${index}].trustProof.residualRisk[${riskIndex}]`, error.message, {
|
|
1427
|
+
id: risk?.id || claim.id || "",
|
|
1428
|
+
}));
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1363
1431
|
}
|
|
1364
1432
|
if (!trustProof.responsibility?.registryFactsOwner || !trustProof.responsibility?.artifactVerificationOwner || !trustProof.responsibility?.releasePassportProofOwner) {
|
|
1365
1433
|
issues.push(issue("error", `kfd-2.claims[${index}].trustProof.responsibility`, "KFD-2 trust proof must preserve KFD-3 responsibility state", {
|