@blamejs/exceptd-skills 0.16.28 → 0.16.30
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/CHANGELOG.md +28 -0
- package/README.md +1 -1
- package/bin/exceptd.js +251 -18
- package/data/_indexes/_meta.json +4 -3
- package/data/_indexes/jurisdiction-map.json +31 -158
- package/data/playbooks/crypto.json +6 -0
- package/lib/auto-discovery.js +8 -0
- package/lib/collectors/README.md +3 -2
- package/lib/collectors/library-author.js +26 -9
- package/lib/collectors/secrets.js +8 -1
- package/lib/cross-ref-api.js +96 -31
- package/lib/lint-skills.js +6 -1
- package/lib/playbook-runner.js +264 -52
- package/lib/prefetch.js +78 -6
- package/lib/refresh-external.js +106 -5
- package/lib/scoring.js +49 -5
- package/lib/validate-cve-catalog.js +14 -2
- package/lib/validate-indexes.js +5 -0
- package/lib/validate-playbooks.js +133 -38
- package/manifest.json +53 -53
- package/orchestrator/pipeline.js +16 -4
- package/package.json +1 -1
- package/sbom.cdx.json +73 -58
- package/scripts/build-indexes.js +12 -1
- package/scripts/check-sbom-currency.js +76 -14
- package/scripts/refresh-sbom.js +1 -1
- package/scripts/run-e2e-scenarios.js +41 -11
- package/scripts/sync-package-description.js +74 -0
- package/scripts/verify-shipped-tarball.js +18 -7
- package/sources/validators/cve-validator.js +16 -6
package/sbom.cdx.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:a7c95fa5-56fc-47e1-9190-3c7136d591ca",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
7
|
+
"timestamp": "2115-03-16T22:18:13.000Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "blamejs",
|
|
11
11
|
"name": "scripts/refresh-sbom.js",
|
|
12
|
-
"version": "0.16.
|
|
12
|
+
"version": "0.16.30"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.
|
|
16
|
+
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.30",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.16.
|
|
19
|
+
"version": "0.16.30",
|
|
20
20
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (439 CVEs / 177 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
|
|
21
21
|
"licenses": [
|
|
22
22
|
{
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.30",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
32
|
+
"content": "c1bd30e9ed811a98d591a42f6afbfbaa84fd44ef183e7897ad80551fc28b9736"
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
35
|
"externalReferences": [
|
|
36
36
|
{
|
|
37
37
|
"type": "distribution",
|
|
38
|
-
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.
|
|
38
|
+
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.30"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "vcs",
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"hashes": [
|
|
117
117
|
{
|
|
118
118
|
"alg": "SHA-256",
|
|
119
|
-
"content": "
|
|
119
|
+
"content": "673ca71c12d4d15633bb8aef96c9894e153d2ef878c51ace03de99e3ef59e924"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
123
|
+
"content": "ae11ee07cfeff030fa9cab9d3a668b160a8859d27fa15abad8c2fa9f63f39eaeb45806cd64cc45f23b919a74b682ca0122df10270b3bc10cdb3c56c470f0da5e"
|
|
124
124
|
}
|
|
125
125
|
]
|
|
126
126
|
},
|
|
@@ -176,11 +176,11 @@
|
|
|
176
176
|
"hashes": [
|
|
177
177
|
{
|
|
178
178
|
"alg": "SHA-256",
|
|
179
|
-
"content": "
|
|
179
|
+
"content": "e7b854e7db9a364a1b368b5084b4f0c2a8282f0459ce39800ac1d1dabdc06074"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"alg": "SHA3-512",
|
|
183
|
-
"content": "
|
|
183
|
+
"content": "8c0af6e6ca9c5753f726b940b5a0cf948abd61c552c3779d4c3b3ee6950657a4713815926c5cda3d743256b8f580865986de9992167ce1f476b3dd0bd5557ee1"
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
},
|
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
"hashes": [
|
|
282
282
|
{
|
|
283
283
|
"alg": "SHA-256",
|
|
284
|
-
"content": "
|
|
284
|
+
"content": "17553af5707a5bf163f76eb28c62bca472d7d9cb74c5d2fb5eb7bf18ab63bde8"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
288
|
+
"content": "05a98e18e6e13d94ee77b95bcb5e22288b5780c1279d89549e9683fce3661165c2e9578ac08ef0732543fb51f59e9588fe6a0141a498a5e91e9afa07766f21f0"
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
},
|
|
@@ -566,11 +566,11 @@
|
|
|
566
566
|
"hashes": [
|
|
567
567
|
{
|
|
568
568
|
"alg": "SHA-256",
|
|
569
|
-
"content": "
|
|
569
|
+
"content": "68e5e75851ba4023585726e3c95c8eed651e0e35c8519edf9399addedbbef25b"
|
|
570
570
|
},
|
|
571
571
|
{
|
|
572
572
|
"alg": "SHA3-512",
|
|
573
|
-
"content": "
|
|
573
|
+
"content": "11b1935f75c8b77b50f73cf64ac5e8aafeeddae172d6cdd9e036c6e6348ad3587dd0a173243f0db4c579dae2fef9bf5eeaeb742271588b644ad5edc814ef5493"
|
|
574
574
|
}
|
|
575
575
|
]
|
|
576
576
|
},
|
|
@@ -986,11 +986,11 @@
|
|
|
986
986
|
"hashes": [
|
|
987
987
|
{
|
|
988
988
|
"alg": "SHA-256",
|
|
989
|
-
"content": "
|
|
989
|
+
"content": "081583535b98f300966c656ba007858127506f4d2853a62f8f5084984d99a3ef"
|
|
990
990
|
},
|
|
991
991
|
{
|
|
992
992
|
"alg": "SHA3-512",
|
|
993
|
-
"content": "
|
|
993
|
+
"content": "1297d553949f1501518f9c8d4f2c133cb92a0ab49aacde2bd18b3f883809fb340036a960cc4ded99828a351a4756cba38fd37ba3688903049f17888af0f9c411"
|
|
994
994
|
}
|
|
995
995
|
]
|
|
996
996
|
},
|
|
@@ -1031,11 +1031,11 @@
|
|
|
1031
1031
|
"hashes": [
|
|
1032
1032
|
{
|
|
1033
1033
|
"alg": "SHA-256",
|
|
1034
|
-
"content": "
|
|
1034
|
+
"content": "f8b834f2b9615aef61f2ab07c9ff4c1242c9634a3636eec166fbdcdca7c616f0"
|
|
1035
1035
|
},
|
|
1036
1036
|
{
|
|
1037
1037
|
"alg": "SHA3-512",
|
|
1038
|
-
"content": "
|
|
1038
|
+
"content": "667b0cac8ce4d14ce648611de8696165fa64bdf6147d34144252c96edd4dfc6d01fce3be4825c6fabc00ba27a49a431da0a48e9cb4c69f50534ac7aabdc1053e"
|
|
1039
1039
|
}
|
|
1040
1040
|
]
|
|
1041
1041
|
},
|
|
@@ -1181,11 +1181,11 @@
|
|
|
1181
1181
|
"hashes": [
|
|
1182
1182
|
{
|
|
1183
1183
|
"alg": "SHA-256",
|
|
1184
|
-
"content": "
|
|
1184
|
+
"content": "d5962bed4fda430834283e9ec557161ba7e2c4f5f964f8fd45f4a1723a8cd622"
|
|
1185
1185
|
},
|
|
1186
1186
|
{
|
|
1187
1187
|
"alg": "SHA3-512",
|
|
1188
|
-
"content": "
|
|
1188
|
+
"content": "39006cdae4faf916cb1c05d729c29f5a7cb4c1535fedfc59a57a6ac5c38a449e195c05dc23aff1465cff0de720e2efa07a9e8dd5a2e573561b72d411da88e515"
|
|
1189
1189
|
}
|
|
1190
1190
|
]
|
|
1191
1191
|
},
|
|
@@ -1256,11 +1256,11 @@
|
|
|
1256
1256
|
"hashes": [
|
|
1257
1257
|
{
|
|
1258
1258
|
"alg": "SHA-256",
|
|
1259
|
-
"content": "
|
|
1259
|
+
"content": "0f2fadb970f21ee2c04573d97fd22857d26b39925d11ea1a9dff3f07b5c3d0ae"
|
|
1260
1260
|
},
|
|
1261
1261
|
{
|
|
1262
1262
|
"alg": "SHA3-512",
|
|
1263
|
-
"content": "
|
|
1263
|
+
"content": "8a2b59dc57a5dad4e53420d465999b16d9ca949b95846293ee1dc088709df9dcabc7994e47385c0f1471039556f4e2d867c6e7d70f59c51b345c32f52f0e30fa"
|
|
1264
1264
|
}
|
|
1265
1265
|
]
|
|
1266
1266
|
},
|
|
@@ -1271,11 +1271,11 @@
|
|
|
1271
1271
|
"hashes": [
|
|
1272
1272
|
{
|
|
1273
1273
|
"alg": "SHA-256",
|
|
1274
|
-
"content": "
|
|
1274
|
+
"content": "503037e4f50d6fc4ff6ebb972b3b4c3663003c5d9902df52aec0c3f3408f9e47"
|
|
1275
1275
|
},
|
|
1276
1276
|
{
|
|
1277
1277
|
"alg": "SHA3-512",
|
|
1278
|
-
"content": "
|
|
1278
|
+
"content": "c3dde0efa5762c8466eb45ebe93ba61f0f7c0772ec87731b9f7c7994b687ef8deacc5a952f9fd68b96c658b9fd21ee7418845fdbadd4df0228e8e6e22c46d9c7"
|
|
1279
1279
|
}
|
|
1280
1280
|
]
|
|
1281
1281
|
},
|
|
@@ -1451,11 +1451,11 @@
|
|
|
1451
1451
|
"hashes": [
|
|
1452
1452
|
{
|
|
1453
1453
|
"alg": "SHA-256",
|
|
1454
|
-
"content": "
|
|
1454
|
+
"content": "2a488a8dcbb25e0e46dd04e6c8294406f3f75833ff3b2e59fdc9ba3e589df5c8"
|
|
1455
1455
|
},
|
|
1456
1456
|
{
|
|
1457
1457
|
"alg": "SHA3-512",
|
|
1458
|
-
"content": "
|
|
1458
|
+
"content": "39ec71547e6c9c9823749ae0248f886af234358e6937da59001ae5cd3bb846a7134b0cbf15f5a086f80b2eec1d0a9ebee96d01568559930a5ac560235b19a246"
|
|
1459
1459
|
}
|
|
1460
1460
|
]
|
|
1461
1461
|
},
|
|
@@ -1466,11 +1466,11 @@
|
|
|
1466
1466
|
"hashes": [
|
|
1467
1467
|
{
|
|
1468
1468
|
"alg": "SHA-256",
|
|
1469
|
-
"content": "
|
|
1469
|
+
"content": "59179357f3ebc1cae07ca63f6e53441e1f5ce001491b0a71c03b00270403a7c7"
|
|
1470
1470
|
},
|
|
1471
1471
|
{
|
|
1472
1472
|
"alg": "SHA3-512",
|
|
1473
|
-
"content": "
|
|
1473
|
+
"content": "b2f8c4ebda7ada3125f8b6cb56fedf7b6072606130cba59c29679df22161162774d7a43e9c3a1b8860ada5320f18ee9d771cd20b48a324c4f2218afd42b0a03d"
|
|
1474
1474
|
}
|
|
1475
1475
|
]
|
|
1476
1476
|
},
|
|
@@ -1481,11 +1481,11 @@
|
|
|
1481
1481
|
"hashes": [
|
|
1482
1482
|
{
|
|
1483
1483
|
"alg": "SHA-256",
|
|
1484
|
-
"content": "
|
|
1484
|
+
"content": "f9c0e300bcce3371b036f7c578470ee80b6bcf33fc9e618e4377c527b889f0a0"
|
|
1485
1485
|
},
|
|
1486
1486
|
{
|
|
1487
1487
|
"alg": "SHA3-512",
|
|
1488
|
-
"content": "
|
|
1488
|
+
"content": "045c5a90c33b7bff92f1fbfb783e573e1819ab8a5980a2bd2c9347502b0b02e87176aa1ac1238c030018cb8d50c4bd1484c434c82e2b3882e2826b45b976f000"
|
|
1489
1489
|
}
|
|
1490
1490
|
]
|
|
1491
1491
|
},
|
|
@@ -1496,11 +1496,11 @@
|
|
|
1496
1496
|
"hashes": [
|
|
1497
1497
|
{
|
|
1498
1498
|
"alg": "SHA-256",
|
|
1499
|
-
"content": "
|
|
1499
|
+
"content": "ad595c63a137b7bb85ff581897cfc8bbdf7e7f46a247e40245a14f564b8a1c07"
|
|
1500
1500
|
},
|
|
1501
1501
|
{
|
|
1502
1502
|
"alg": "SHA3-512",
|
|
1503
|
-
"content": "
|
|
1503
|
+
"content": "8cfd603e5d873730e393d678380568d29725e05d37f1d6cf876e54ea186c0912ad30aa0f5135a23fa959fe141b40034f08742e5aac07b0303e4a728e192b8070"
|
|
1504
1504
|
}
|
|
1505
1505
|
]
|
|
1506
1506
|
},
|
|
@@ -1601,11 +1601,11 @@
|
|
|
1601
1601
|
"hashes": [
|
|
1602
1602
|
{
|
|
1603
1603
|
"alg": "SHA-256",
|
|
1604
|
-
"content": "
|
|
1604
|
+
"content": "83d6e9b8afc4c9158ec49a641d6838e06d4189dd0b3e7e8e96ab013a54d05d97"
|
|
1605
1605
|
},
|
|
1606
1606
|
{
|
|
1607
1607
|
"alg": "SHA3-512",
|
|
1608
|
-
"content": "
|
|
1608
|
+
"content": "fe6a305dca12f5ee5c8f51f13aed88307a7ab87ec8f800fe4cf12e7aefe5e2db6a2efc25fc8297f61072d0af4f010fec813211e5cb0778fa7f8ce14a4e5c34af"
|
|
1609
1609
|
}
|
|
1610
1610
|
]
|
|
1611
1611
|
},
|
|
@@ -1736,11 +1736,11 @@
|
|
|
1736
1736
|
"hashes": [
|
|
1737
1737
|
{
|
|
1738
1738
|
"alg": "SHA-256",
|
|
1739
|
-
"content": "
|
|
1739
|
+
"content": "f5ec0535d2e0712654a6dff2952e66b7de2044afb03ab6422ba0b1597779d782"
|
|
1740
1740
|
},
|
|
1741
1741
|
{
|
|
1742
1742
|
"alg": "SHA3-512",
|
|
1743
|
-
"content": "
|
|
1743
|
+
"content": "17a5d85d096aa1744202eb076c72ac8f9ba61a595795d45a06ff9f4628a21f654b38752ee04beb13dc6048945fa9f89f9b204e386503bb200e4a31d93206ab99"
|
|
1744
1744
|
}
|
|
1745
1745
|
]
|
|
1746
1746
|
},
|
|
@@ -1751,11 +1751,11 @@
|
|
|
1751
1751
|
"hashes": [
|
|
1752
1752
|
{
|
|
1753
1753
|
"alg": "SHA-256",
|
|
1754
|
-
"content": "
|
|
1754
|
+
"content": "b574dffe09c2881e4cae3a4676aa0396950263e755349c677e94343945f435a9"
|
|
1755
1755
|
},
|
|
1756
1756
|
{
|
|
1757
1757
|
"alg": "SHA3-512",
|
|
1758
|
-
"content": "
|
|
1758
|
+
"content": "8cdea5fbb52cf82fd543d1333375359708ca1b7ce172bbaf81fc5cd6374715dedc62b3a2249da685808c02cf817086b795e1510c175b5ea33c25a54e8bdcb4b2"
|
|
1759
1759
|
}
|
|
1760
1760
|
]
|
|
1761
1761
|
},
|
|
@@ -1781,11 +1781,11 @@
|
|
|
1781
1781
|
"hashes": [
|
|
1782
1782
|
{
|
|
1783
1783
|
"alg": "SHA-256",
|
|
1784
|
-
"content": "
|
|
1784
|
+
"content": "dd4c2bdee021c4a2c7f0b7c50dc287838e1be60d4e85bb7447ca4575bf0b93f1"
|
|
1785
1785
|
},
|
|
1786
1786
|
{
|
|
1787
1787
|
"alg": "SHA3-512",
|
|
1788
|
-
"content": "
|
|
1788
|
+
"content": "b52322b974b16541d67339a317a72e37c90b52b089776d55686e174de9075bbdb8543469533011446b1f04c46312bd1be438cacb1b3e3e6ec14d92900040bfcd"
|
|
1789
1789
|
}
|
|
1790
1790
|
]
|
|
1791
1791
|
},
|
|
@@ -1901,11 +1901,11 @@
|
|
|
1901
1901
|
"hashes": [
|
|
1902
1902
|
{
|
|
1903
1903
|
"alg": "SHA-256",
|
|
1904
|
-
"content": "
|
|
1904
|
+
"content": "70fea689545e1145b6e4e55621b07083d54e657372d1293406697cbcdd216fbb"
|
|
1905
1905
|
},
|
|
1906
1906
|
{
|
|
1907
1907
|
"alg": "SHA3-512",
|
|
1908
|
-
"content": "
|
|
1908
|
+
"content": "1bf4b7ec8dfa0661f5219a20572816d8bfc9b3c4bb83651f4ba07959063d28099ea519ca0401fcc1d38d5fb7658699453ba1ed71fa6dfdaca7fd615b056ffadc"
|
|
1909
1909
|
}
|
|
1910
1910
|
]
|
|
1911
1911
|
},
|
|
@@ -1976,11 +1976,11 @@
|
|
|
1976
1976
|
"hashes": [
|
|
1977
1977
|
{
|
|
1978
1978
|
"alg": "SHA-256",
|
|
1979
|
-
"content": "
|
|
1979
|
+
"content": "515a7c8b3ec6814d45c35a8243b30e71c900dfc4016adb5201b9bbecb047d1fd"
|
|
1980
1980
|
},
|
|
1981
1981
|
{
|
|
1982
1982
|
"alg": "SHA3-512",
|
|
1983
|
-
"content": "
|
|
1983
|
+
"content": "58ef4fcb1a6d5cb7f9f75046dfa63fc7c732440932f76acc31981a7edc0f5dbfacea8f5d30709aecbcb05331332f39921ae5e766a068403913c6d753b2136de3"
|
|
1984
1984
|
}
|
|
1985
1985
|
]
|
|
1986
1986
|
},
|
|
@@ -2096,11 +2096,11 @@
|
|
|
2096
2096
|
"hashes": [
|
|
2097
2097
|
{
|
|
2098
2098
|
"alg": "SHA-256",
|
|
2099
|
-
"content": "
|
|
2099
|
+
"content": "9f54c09e25593d82ba36bffcf5ee6b8137eab9e1f5408b7cf6cb681a69806d4c"
|
|
2100
2100
|
},
|
|
2101
2101
|
{
|
|
2102
2102
|
"alg": "SHA3-512",
|
|
2103
|
-
"content": "
|
|
2103
|
+
"content": "9711d20338cd19da03cb50d089aa42ce5c8f45513d92523a84572494337d88654a29bedc991009ba4522346d3b04506621974707a537c1e24ee1d985017d113f"
|
|
2104
2104
|
}
|
|
2105
2105
|
]
|
|
2106
2106
|
},
|
|
@@ -2396,11 +2396,11 @@
|
|
|
2396
2396
|
"hashes": [
|
|
2397
2397
|
{
|
|
2398
2398
|
"alg": "SHA-256",
|
|
2399
|
-
"content": "
|
|
2399
|
+
"content": "3fcc2abf934ee1b70674856ba937833830d45346b4f146d613e500b9a9b4d163"
|
|
2400
2400
|
},
|
|
2401
2401
|
{
|
|
2402
2402
|
"alg": "SHA3-512",
|
|
2403
|
-
"content": "
|
|
2403
|
+
"content": "1cc3b832434aa188fc7136feadb286c68d528e1e35406572bbaaf29a1b21be90ca59f3a8ea96d7173e7365e6c7b9248fdb45dcb6c56bbeec93ede95341ca5720"
|
|
2404
2404
|
}
|
|
2405
2405
|
]
|
|
2406
2406
|
},
|
|
@@ -2606,11 +2606,11 @@
|
|
|
2606
2606
|
"hashes": [
|
|
2607
2607
|
{
|
|
2608
2608
|
"alg": "SHA-256",
|
|
2609
|
-
"content": "
|
|
2609
|
+
"content": "bba34316450d9bb0e6aa60e75dfcb28bdc767487d13ca429dbc654772a18938b"
|
|
2610
2610
|
},
|
|
2611
2611
|
{
|
|
2612
2612
|
"alg": "SHA3-512",
|
|
2613
|
-
"content": "
|
|
2613
|
+
"content": "41e269280701ba73957c7d747f019568c23d5c5e6a19c6f7a5153c0211937a673df33e81b0f7efe286ebd36a50789bbbc45b8d145fb94f7800008021cb7f9530"
|
|
2614
2614
|
}
|
|
2615
2615
|
]
|
|
2616
2616
|
},
|
|
@@ -2651,11 +2651,11 @@
|
|
|
2651
2651
|
"hashes": [
|
|
2652
2652
|
{
|
|
2653
2653
|
"alg": "SHA-256",
|
|
2654
|
-
"content": "
|
|
2654
|
+
"content": "fbf31669b2d177ae643e480f4fad5ea60b7af8ab32617add0350757bd9dcce9e"
|
|
2655
2655
|
},
|
|
2656
2656
|
{
|
|
2657
2657
|
"alg": "SHA3-512",
|
|
2658
|
-
"content": "
|
|
2658
|
+
"content": "5e6b83c43ae77337b7b600d54ce6ecc168ee6fe7401aa154f0da58d13b1f21cf784221dda481895c7721595160d6a8b59093fdb9ea2dccb2eed1fa34f1aaea46"
|
|
2659
2659
|
}
|
|
2660
2660
|
]
|
|
2661
2661
|
},
|
|
@@ -2674,6 +2674,21 @@
|
|
|
2674
2674
|
}
|
|
2675
2675
|
]
|
|
2676
2676
|
},
|
|
2677
|
+
{
|
|
2678
|
+
"bom-ref": "file:scripts/sync-package-description.js",
|
|
2679
|
+
"type": "file",
|
|
2680
|
+
"name": "scripts/sync-package-description.js",
|
|
2681
|
+
"hashes": [
|
|
2682
|
+
{
|
|
2683
|
+
"alg": "SHA-256",
|
|
2684
|
+
"content": "4110a273512bf43b9246f5ab4c196008c9043778b4137dca8a459df7ca6066de"
|
|
2685
|
+
},
|
|
2686
|
+
{
|
|
2687
|
+
"alg": "SHA3-512",
|
|
2688
|
+
"content": "427e94caa4b48f2a4f0421cc9924e7c9b626032a7d78df684e0e63743331caf16da65a345f88f90c316aa6c0ae95d29924d69d1cb8d92e582bf4e376629428a0"
|
|
2689
|
+
}
|
|
2690
|
+
]
|
|
2691
|
+
},
|
|
2677
2692
|
{
|
|
2678
2693
|
"bom-ref": "file:scripts/validate-vendor-online.js",
|
|
2679
2694
|
"type": "file",
|
|
@@ -2696,11 +2711,11 @@
|
|
|
2696
2711
|
"hashes": [
|
|
2697
2712
|
{
|
|
2698
2713
|
"alg": "SHA-256",
|
|
2699
|
-
"content": "
|
|
2714
|
+
"content": "7541cd702c57e7b966b4b1bfffed748dba16f5bb25ff9176be64a50a92fc8e65"
|
|
2700
2715
|
},
|
|
2701
2716
|
{
|
|
2702
2717
|
"alg": "SHA3-512",
|
|
2703
|
-
"content": "
|
|
2718
|
+
"content": "15c7bb7f853a04a615d247ff47ac53c1b282cfc56875fd0248843b069d71ce04e3731bb3890b01644d58f0f42e37d362016e4f8176e4dd64ba837043c46f5805"
|
|
2704
2719
|
}
|
|
2705
2720
|
]
|
|
2706
2721
|
},
|
|
@@ -3491,11 +3506,11 @@
|
|
|
3491
3506
|
"hashes": [
|
|
3492
3507
|
{
|
|
3493
3508
|
"alg": "SHA-256",
|
|
3494
|
-
"content": "
|
|
3509
|
+
"content": "5410a1ca1ebea4c3d7ff14a9ea5d69dde5d59490f9bae518d1d3b0440a457238"
|
|
3495
3510
|
},
|
|
3496
3511
|
{
|
|
3497
3512
|
"alg": "SHA3-512",
|
|
3498
|
-
"content": "
|
|
3513
|
+
"content": "96872e1b74348e5be3d440e3e2265ab14ce35118a4226d7b2a698c36573374120bb663c8c852a67206169b715b7008944941a9a4ef890f7d1afa94e7d105be71"
|
|
3499
3514
|
}
|
|
3500
3515
|
]
|
|
3501
3516
|
},
|
package/scripts/build-indexes.js
CHANGED
|
@@ -266,6 +266,13 @@ const OUTPUTS = [
|
|
|
266
266
|
for (const s of ctx.skills) {
|
|
267
267
|
const body = ctx.skillBodies[s.name];
|
|
268
268
|
for (const code of codes) {
|
|
269
|
+
// Skip bare 2-letter ISO codes in free-text matching. `\bID\b`,
|
|
270
|
+
// `\bCA\b`, `\bNO\b` etc. collide with prose words ("the ID",
|
|
271
|
+
// "US-based") and control/countermeasure id grammar (`\bCA\b` matches
|
|
272
|
+
// inside `D3-CA`, `\bSA\b` inside `SA-12`), polluting coverage
|
|
273
|
+
// (Indonesia landed on 41/51 skills). These jurisdictions are mapped
|
|
274
|
+
// via the curated NAME_TO_CODE regulation-name table below instead.
|
|
275
|
+
if (code.length <= 2) continue;
|
|
269
276
|
const re = new RegExp("\\b" + code + "\\b");
|
|
270
277
|
if (re.test(body) && !out[code].skills.includes(s.name)) out[code].skills.push(s.name);
|
|
271
278
|
}
|
|
@@ -499,7 +506,7 @@ const OUTPUTS = [
|
|
|
499
506
|
{
|
|
500
507
|
name: "stale-content",
|
|
501
508
|
file: "stale-content.json",
|
|
502
|
-
deps: [isManifest, isAnySkillBody, isAnyCatalog],
|
|
509
|
+
deps: [isManifest, isAnySkillBody, isAnyCatalog, (p) => p === "README.md"],
|
|
503
510
|
build: (ctx) => {
|
|
504
511
|
const { buildStaleContent } = require("./builders/stale-content");
|
|
505
512
|
return buildStaleContent({ root: ctx.root, manifest: ctx.manifest, skills: ctx.skills, catalogFiles: ctx.catalogFiles });
|
|
@@ -518,6 +525,10 @@ function loadPriorMeta() {
|
|
|
518
525
|
function liveSourceSet(ctx) {
|
|
519
526
|
const out = new Set();
|
|
520
527
|
out.add("manifest.json");
|
|
528
|
+
// README.md is consumed by the stale-content builder (badge-count drift), so
|
|
529
|
+
// it must be a hashed source — otherwise a README edit is invisible to
|
|
530
|
+
// --changed and the validate-indexes freshness gate.
|
|
531
|
+
if (fs.existsSync(ABS("README.md"))) out.add("README.md");
|
|
521
532
|
for (const c of ctx.catalogFiles) out.add(c);
|
|
522
533
|
for (const s of ctx.skills) out.add(s.path);
|
|
523
534
|
return out;
|
|
@@ -136,19 +136,42 @@ function checkSbomCurrency(root) {
|
|
|
136
136
|
);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
// The "N catalogs" and "N jurisdictions" free-text counts in the same
|
|
140
|
+
// description string were never validated — only the per-catalog entry tokens
|
|
141
|
+
// and the skill count were. Pin them to the live values so a stale
|
|
142
|
+
// description (e.g. after an auto-refresh changed a count) fails the gate.
|
|
143
|
+
const catalogMatch = description.match(/(\d+)\s+catalogs?\b/i);
|
|
144
|
+
if (catalogMatch && Number(catalogMatch[1]) !== liveCatalogs) {
|
|
145
|
+
errors.push(
|
|
146
|
+
`SBOM description catalog count is ${Number(catalogMatch[1])} but live data/ has ${liveCatalogs} catalogs — description is stale; update package.json.description and \`npm run refresh-sbom\``
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
const liveJurisdictions = (() => {
|
|
150
|
+
try {
|
|
151
|
+
const gf = JSON.parse(fs.readFileSync(path.join(dataDir, "global-frameworks.json"), "utf8"));
|
|
152
|
+
// Non-underscore top-level keys — the canonical jurisdiction count the
|
|
153
|
+
// README badge and catalog-summaries use.
|
|
154
|
+
return Object.keys(gf).filter((k) => !k.startsWith("_")).length;
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
})();
|
|
159
|
+
const jurisdictionMatch = description.match(/(\d+)\s+jurisdictions?\b/i);
|
|
160
|
+
if (liveJurisdictions !== null && jurisdictionMatch && Number(jurisdictionMatch[1]) !== liveJurisdictions) {
|
|
161
|
+
errors.push(
|
|
162
|
+
`SBOM description jurisdiction count is ${Number(jurisdictionMatch[1])} but live global-frameworks.json has ${liveJurisdictions} — description is stale; update package.json.description and \`npm run refresh-sbom\``
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Component-level cross-check (defense-in-depth). In normal operation
|
|
167
|
+
// refresh-sbom emits NO per-skill "skill:" components — skill drift is caught
|
|
168
|
+
// by the file:skills/<name>/skill.md and file:manifest.json content hashes in
|
|
169
|
+
// the file: component pass below (a bumped or renamed skill changes those
|
|
170
|
+
// bytes). This branch is therefore not exercised by a clean SBOM, but it is
|
|
171
|
+
// retained as a tamper guard: a forged or buggy SBOM that injected a skill
|
|
172
|
+
// component with a stale version (or a skill name no longer in the manifest)
|
|
173
|
+
// is still caught here. Vendor components are validated against
|
|
174
|
+
// vendor/blamejs/_PROVENANCE.json.
|
|
152
175
|
const components = Array.isArray(sbom.components) ? sbom.components : [];
|
|
153
176
|
const skillByName = new Map(
|
|
154
177
|
(manifest.skills || []).map((s) => [s.name, s])
|
|
@@ -281,6 +304,45 @@ function checkSbomCurrency(root) {
|
|
|
281
304
|
fileComponentsChecked++;
|
|
282
305
|
}
|
|
283
306
|
|
|
307
|
+
// Completeness + bundle-digest integrity. The per-file pass above verifies
|
|
308
|
+
// every RECORDED file: component, but never checked that every SHIPPED file
|
|
309
|
+
// (the package.json.files expansion) actually HAS a component — a
|
|
310
|
+
// newly-shipped file would ship unhashed and silent. And the aggregate
|
|
311
|
+
// bundle digest in metadata.component.hashes[] was never recomputed. Reuse
|
|
312
|
+
// refresh-sbom's exact allowlist expansion + digest so the gate can't drift
|
|
313
|
+
// from the generator.
|
|
314
|
+
try {
|
|
315
|
+
const { expandAllowlist, bundleDigest } = require("./refresh-sbom");
|
|
316
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
317
|
+
const expected = expandAllowlist(pkg.files || []);
|
|
318
|
+
const fileComps = components.filter(
|
|
319
|
+
(c) => typeof c["bom-ref"] === "string" && c["bom-ref"].startsWith("file:")
|
|
320
|
+
);
|
|
321
|
+
const fileCompNames = new Set(fileComps.map((c) => c.name));
|
|
322
|
+
for (const rel of expected) {
|
|
323
|
+
if (!fileCompNames.has(rel)) {
|
|
324
|
+
errors.push(
|
|
325
|
+
`Shipped file "${rel}" (package.json.files) has no file: component in the SBOM — run \`npm run refresh-sbom\``
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Recompute the aggregate bundle digest from the file: components' recorded
|
|
330
|
+
// SHA-256 hashes and compare to metadata.component.hashes[] (the per-file
|
|
331
|
+
// pass already tied each recorded hash to live bytes).
|
|
332
|
+
const compHashes = (sbom.metadata && sbom.metadata.component && sbom.metadata.component.hashes) || [];
|
|
333
|
+
const recorded = (compHashes.find((h) => h && h.alg === "SHA-256") || {}).content;
|
|
334
|
+
if (recorded && fileComps.length) {
|
|
335
|
+
const recomputed = bundleDigest(fileComps);
|
|
336
|
+
if (recomputed !== recorded) {
|
|
337
|
+
errors.push(
|
|
338
|
+
`SBOM bundle digest mismatch: metadata.component.hashes SHA-256 ${String(recorded).slice(0, 12)}… != recomputed ${recomputed.slice(0, 12)}… from file: components — run \`npm run refresh-sbom\``
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} catch (e) {
|
|
343
|
+
errors.push(`SBOM completeness/bundle-digest check failed: ${e.message}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
284
346
|
return {
|
|
285
347
|
ok: errors.length === 0,
|
|
286
348
|
errors,
|
|
@@ -310,6 +372,6 @@ function main() {
|
|
|
310
372
|
);
|
|
311
373
|
}
|
|
312
374
|
|
|
313
|
-
module.exports = { checkSbomCurrency, resolveRoot };
|
|
375
|
+
module.exports = { checkSbomCurrency, resolveRoot, DESCRIPTION_ENTRY_TOKENS, catalogEntryCount };
|
|
314
376
|
|
|
315
377
|
if (require.main === module) main();
|
package/scripts/refresh-sbom.js
CHANGED
|
@@ -55,6 +55,27 @@ function getJsonPath(obj, dotted) {
|
|
|
55
55
|
return dotted.split(".").reduce((acc, key) => acc?.[key], obj);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// Evaluate the negative stderr guard against raw stderr text. Lives here, NOT
|
|
59
|
+
// inside diffExpect, because the ban must hold regardless of whether stdout
|
|
60
|
+
// parsed as JSON — a scenario whose stdout is a human banner (no JSON body,
|
|
61
|
+
// only an expect_exit assertion) must still enforce a forbidden-token ban on
|
|
62
|
+
// stderr. evaluateScenario calls this unconditionally.
|
|
63
|
+
function stderrBanFailures(expect, stderr) {
|
|
64
|
+
const failures = [];
|
|
65
|
+
if (expect.stderr_must_not_match) {
|
|
66
|
+
for (const regex of expect.stderr_must_not_match) {
|
|
67
|
+
if (new RegExp(regex).test(stderr || "")) {
|
|
68
|
+
failures.push(`stderr_must_not_match /${regex}/: stderr contains it`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return failures;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Diff a parsed JSON body against the positive expect matchers. The negative
|
|
76
|
+
// stderr guard is NOT evaluated here (see stderrBanFailures); this function
|
|
77
|
+
// only inspects the JSON body so it cannot be silently skipped when stdout
|
|
78
|
+
// fails to parse.
|
|
58
79
|
function diffExpect(jsonBody, expect, ctx) {
|
|
59
80
|
const failures = [];
|
|
60
81
|
if (expect.json_path_equals) {
|
|
@@ -89,23 +110,26 @@ function diffExpect(jsonBody, expect, ctx) {
|
|
|
89
110
|
}
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
|
-
if (expect.stderr_must_not_match) {
|
|
93
|
-
for (const regex of expect.stderr_must_not_match) {
|
|
94
|
-
if (new RegExp(regex).test(ctx.stderr)) {
|
|
95
|
-
failures.push(`stderr_must_not_match /${regex}/: stderr contains it`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
113
|
return failures;
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
function tryParseJson(s) {
|
|
103
117
|
if (!s) return null;
|
|
104
|
-
try {
|
|
105
|
-
|
|
118
|
+
try {
|
|
119
|
+
const v = JSON.parse(s.trim());
|
|
120
|
+
if (v && typeof v === "object") return v;
|
|
121
|
+
} catch { /* ignore */ }
|
|
122
|
+
// Some verbs may emit trailing logs; pick the LAST complete JSON object or
|
|
123
|
+
// array on stdout. A verb envelope is always an object/array, so bare
|
|
124
|
+
// scalars (a trailing JSON-parseable "done"/42/true log line) are skipped —
|
|
125
|
+
// binding assertions against a trailing scalar would silently test the
|
|
126
|
+
// wrong value.
|
|
106
127
|
const lines = s.trim().split("\n");
|
|
107
128
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
108
|
-
try {
|
|
129
|
+
try {
|
|
130
|
+
const v = JSON.parse(lines[i]);
|
|
131
|
+
if (v && typeof v === "object") return v;
|
|
132
|
+
} catch { /* keep looking */ }
|
|
109
133
|
}
|
|
110
134
|
return null;
|
|
111
135
|
}
|
|
@@ -148,6 +172,12 @@ function evaluateScenario(scenario, expect, res) {
|
|
|
148
172
|
failures.push(`stdout did not parse as JSON; first 200 chars: ${stdout.slice(0, 200)}`);
|
|
149
173
|
}
|
|
150
174
|
if (body) failures.push(...diffExpect(body, expect, { stdout, stderr, status }));
|
|
175
|
+
|
|
176
|
+
// The forbidden-token ban on stderr runs unconditionally — it does not
|
|
177
|
+
// depend on stdout parsing as JSON. A scenario with only an expect_exit
|
|
178
|
+
// assertion (human-banner stdout) must still fail if stderr carries a banned
|
|
179
|
+
// token.
|
|
180
|
+
failures.push(...stderrBanFailures(expect, stderr));
|
|
151
181
|
return failures;
|
|
152
182
|
}
|
|
153
183
|
|
|
@@ -266,6 +296,6 @@ function main() {
|
|
|
266
296
|
process.exit(failed.length === 0 ? 0 : 1);
|
|
267
297
|
}
|
|
268
298
|
|
|
269
|
-
module.exports = { evaluateScenario, diffExpect, runScenario };
|
|
299
|
+
module.exports = { evaluateScenario, diffExpect, tryParseJson, stderrBanFailures, runScenario, SCENARIO_DIR };
|
|
270
300
|
|
|
271
301
|
if (require.main === module) main();
|