@blamejs/exceptd-skills 0.16.9 → 0.16.11
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/AGENTS.md +2 -1
- package/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/bin/exceptd.js +2 -0
- package/data/_indexes/_meta.json +17 -16
- package/data/_indexes/activity-feed.json +9 -2
- package/data/_indexes/chains.json +1186 -56
- package/data/_indexes/currency.json +10 -1
- package/data/_indexes/frequency.json +89 -51
- package/data/_indexes/handoff-dag.json +5 -1
- package/data/_indexes/jurisdiction-map.json +4 -2
- package/data/_indexes/section-offsets.json +85 -0
- package/data/_indexes/stale-content.json +10 -3
- package/data/_indexes/summary-cards.json +40 -0
- package/data/_indexes/token-budget.json +53 -3
- package/data/_indexes/trigger-table.json +54 -0
- package/data/_indexes/xref.json +29 -6
- package/data/cwe-catalog.json +12 -3
- package/data/playbooks/cred-stores.json +24 -7
- package/data/playbooks/framework.json +17 -5
- package/data/playbooks/identity-sso-compromise.json +21 -4
- package/data/playbooks/vc-wallet-trust.json +725 -0
- package/lib/cve-curation.js +2 -4
- package/lib/flag-suggest.js +1 -1
- package/lib/lint-skills.js +1 -0
- package/lib/playbook-runner.js +2 -2
- package/lib/scoring.js +3 -3
- package/lib/validate-playbooks.js +0 -2
- package/manifest-snapshot.json +57 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +103 -44
- package/package.json +2 -2
- package/sbom.cdx.json +80 -50
- package/scripts/check-codebase-patterns-currency.js +1 -2
- package/scripts/check-codebase-patterns.js +109 -1
- package/scripts/release.js +2 -2
- package/skills/vc-wallet-trust/skill.md +84 -0
package/sbom.cdx.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:29d1b932-e4db-4aac-b562-a8b3727c1da6",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
7
|
+
"timestamp": "2048-03-26T11:44:50.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.11"
|
|
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.11",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.16.
|
|
20
|
-
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation.
|
|
19
|
+
"version": "0.16.11",
|
|
20
|
+
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 43 skills, 11 catalogs (439 CVEs / 174 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
|
{
|
|
23
23
|
"license": {
|
|
@@ -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.11",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
32
|
+
"content": "70d9f0f41bcba23f227253df9e54fd9f7712bc3b14f7ab15414a19fe96727339"
|
|
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.11"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "vcs",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"name": "exceptd:skill:count",
|
|
57
|
-
"value": "
|
|
57
|
+
"value": "43"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
"name": "exceptd:integrity:method",
|
|
@@ -86,11 +86,11 @@
|
|
|
86
86
|
"hashes": [
|
|
87
87
|
{
|
|
88
88
|
"alg": "SHA-256",
|
|
89
|
-
"content": "
|
|
89
|
+
"content": "fb74fd144663792dc340deb3a714fc36fca41d5c8c5ac144308763c84aa89458"
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"alg": "SHA3-512",
|
|
93
|
-
"content": "
|
|
93
|
+
"content": "ee990d784984d065bf2a29a074a9d23ef2f8687c03214e6150a492076ec8f09d79ee09a11127bb7fd59da8653dc4860678d5767dafcd48031fb2b0cb1adbe768"
|
|
94
94
|
}
|
|
95
95
|
]
|
|
96
96
|
},
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"hashes": [
|
|
117
117
|
{
|
|
118
118
|
"alg": "SHA-256",
|
|
119
|
-
"content": "
|
|
119
|
+
"content": "96f0a921259e60d698b848dad1f62f9556d30aa993b1bb4354393de8ca384258"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
123
|
+
"content": "83a9e972e8f6d67063be8b7df88f5668acca4115195897469a9f1a358eb17756132ded3b4c775fb2608c9ab5a82c30bbb207e55e1d4f1779dee1225d788f3fb2"
|
|
124
124
|
}
|
|
125
125
|
]
|
|
126
126
|
},
|
|
@@ -176,11 +176,11 @@
|
|
|
176
176
|
"hashes": [
|
|
177
177
|
{
|
|
178
178
|
"alg": "SHA-256",
|
|
179
|
-
"content": "
|
|
179
|
+
"content": "ffa417f75487e002c2b7ea829c24b2ae3d4b9861feb9b796960498cd11039501"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"alg": "SHA3-512",
|
|
183
|
-
"content": "
|
|
183
|
+
"content": "3ddf859b5a55ac7fbff422672c8edaf33fc8b96a75d8d4fa89dafa074f9f43c30a60104d0255c9564da33d7f98bc5c6f7cc6c9ea3fdc67fa1d702b6b302aaa72"
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
},
|
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
"hashes": [
|
|
282
282
|
{
|
|
283
283
|
"alg": "SHA-256",
|
|
284
|
-
"content": "
|
|
284
|
+
"content": "81fbed0c2e511d190486fab7b73f80ae3f49f2fa7b1d9737e36108dbaaf15a15"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
288
|
+
"content": "1fce92382805e2d8f8ff1698bcfc150c2a46f862befe705694b7fc67dc68ecc9a703288b95c636e7031f992fb1e4923ebf418285771f84efb783efe0b0948758"
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
},
|
|
@@ -341,11 +341,11 @@
|
|
|
341
341
|
"hashes": [
|
|
342
342
|
{
|
|
343
343
|
"alg": "SHA-256",
|
|
344
|
-
"content": "
|
|
344
|
+
"content": "6fa292e00da31186d66d58cd11ee960c56d1fa7f1f6113c3b66625732a79f412"
|
|
345
345
|
},
|
|
346
346
|
{
|
|
347
347
|
"alg": "SHA3-512",
|
|
348
|
-
"content": "
|
|
348
|
+
"content": "6ab606c53a4b568f7bb21e5f48c49359c8b23285096e9608ff682f28c8b2321c1e2857271e7698dc162dc6685a2e1286187758596e8f49f3b9d84f28f958117b"
|
|
349
349
|
}
|
|
350
350
|
]
|
|
351
351
|
},
|
|
@@ -521,11 +521,11 @@
|
|
|
521
521
|
"hashes": [
|
|
522
522
|
{
|
|
523
523
|
"alg": "SHA-256",
|
|
524
|
-
"content": "
|
|
524
|
+
"content": "ece18e05860fdd763645ca246d076ed2fe7e61d738412c6a69a2ce651cd6c1aa"
|
|
525
525
|
},
|
|
526
526
|
{
|
|
527
527
|
"alg": "SHA3-512",
|
|
528
|
-
"content": "
|
|
528
|
+
"content": "f0927574bfc2f0233a617fb93c4ad2cce5287d9a0876ae49f339f646782141f51fd1b452e7e4ec7fd4d6a7bd2d3f5b84012b4051d34a3cd76dea1016cec7886e"
|
|
529
529
|
}
|
|
530
530
|
]
|
|
531
531
|
},
|
|
@@ -566,11 +566,11 @@
|
|
|
566
566
|
"hashes": [
|
|
567
567
|
{
|
|
568
568
|
"alg": "SHA-256",
|
|
569
|
-
"content": "
|
|
569
|
+
"content": "bdd51b48ddc83601a05d4d5e81a96a37095741914bc9db7905f4835a2b3e5805"
|
|
570
570
|
},
|
|
571
571
|
{
|
|
572
572
|
"alg": "SHA3-512",
|
|
573
|
-
"content": "
|
|
573
|
+
"content": "0db71ad4349b31179818fbff41608c828845c3f26ab4545772e9ee304e186e732bf89b454d7a3559e891c17e93396602db343577ff09e9fd524b2d21577a9c17"
|
|
574
574
|
}
|
|
575
575
|
]
|
|
576
576
|
},
|
|
@@ -596,11 +596,11 @@
|
|
|
596
596
|
"hashes": [
|
|
597
597
|
{
|
|
598
598
|
"alg": "SHA-256",
|
|
599
|
-
"content": "
|
|
599
|
+
"content": "93603ece19e3d299ba7130f3b69c826646172cdbea0ddf91f4dddb9efcbde53c"
|
|
600
600
|
},
|
|
601
601
|
{
|
|
602
602
|
"alg": "SHA3-512",
|
|
603
|
-
"content": "
|
|
603
|
+
"content": "cdc1cd7166e5558588a2173ae6551c73c265c1a693993caa786a8f33921d879c12d456a2f8e2f795af7596cec1ad0b15f94d0cede1d6b197322f8c670a99f506"
|
|
604
604
|
}
|
|
605
605
|
]
|
|
606
606
|
},
|
|
@@ -769,6 +769,21 @@
|
|
|
769
769
|
}
|
|
770
770
|
]
|
|
771
771
|
},
|
|
772
|
+
{
|
|
773
|
+
"bom-ref": "file:data/playbooks/vc-wallet-trust.json",
|
|
774
|
+
"type": "file",
|
|
775
|
+
"name": "data/playbooks/vc-wallet-trust.json",
|
|
776
|
+
"hashes": [
|
|
777
|
+
{
|
|
778
|
+
"alg": "SHA-256",
|
|
779
|
+
"content": "67663610d7fe7fd711dc6dc3a5f6963654263b5f979009faf44e5c60e1135431"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
"alg": "SHA3-512",
|
|
783
|
+
"content": "af609c3248471f5a75cb70f79ffc14042ab8456d78aba70dea4fab7e2ac7a17f42a9997f309db796a372a0fb70a2b6cd6798a13c6c51ced89c5f182c4f1fd0d7"
|
|
784
|
+
}
|
|
785
|
+
]
|
|
786
|
+
},
|
|
772
787
|
{
|
|
773
788
|
"bom-ref": "file:data/playbooks/webhook-callback-abuse.json",
|
|
774
789
|
"type": "file",
|
|
@@ -1166,11 +1181,11 @@
|
|
|
1166
1181
|
"hashes": [
|
|
1167
1182
|
{
|
|
1168
1183
|
"alg": "SHA-256",
|
|
1169
|
-
"content": "
|
|
1184
|
+
"content": "069a323205aa0ee280661b9bd1b85bb4d35ea547f7f5dfdd24ea545d14ef492f"
|
|
1170
1185
|
},
|
|
1171
1186
|
{
|
|
1172
1187
|
"alg": "SHA3-512",
|
|
1173
|
-
"content": "
|
|
1188
|
+
"content": "ff9b24c9197e80129933c1f2b89e820ebdbd401ef50b9e255723de04c959f81b0abef396bca24f0e8236b9048ff7fd516219a12f753d8297120d7ebe1af1c9de"
|
|
1174
1189
|
}
|
|
1175
1190
|
]
|
|
1176
1191
|
},
|
|
@@ -1226,11 +1241,11 @@
|
|
|
1226
1241
|
"hashes": [
|
|
1227
1242
|
{
|
|
1228
1243
|
"alg": "SHA-256",
|
|
1229
|
-
"content": "
|
|
1244
|
+
"content": "eb1a3820bba3203967c6057f12cd594e18f3044d966e1c7c1ba17757d902559f"
|
|
1230
1245
|
},
|
|
1231
1246
|
{
|
|
1232
1247
|
"alg": "SHA3-512",
|
|
1233
|
-
"content": "
|
|
1248
|
+
"content": "1a0acc79ebea7cb9d80cde43503c14f14342ccffda834752c2f29215aacfba46a9d120e78c544cd389e6023ccaa519b829dc82eb4f4de6ed1d4ca2ce2ee06e97"
|
|
1234
1249
|
}
|
|
1235
1250
|
]
|
|
1236
1251
|
},
|
|
@@ -1301,11 +1316,11 @@
|
|
|
1301
1316
|
"hashes": [
|
|
1302
1317
|
{
|
|
1303
1318
|
"alg": "SHA-256",
|
|
1304
|
-
"content": "
|
|
1319
|
+
"content": "0ce41dee14acf7998866f5adb63502b614b5501acf8f88c37cd277c96b91a87c"
|
|
1305
1320
|
},
|
|
1306
1321
|
{
|
|
1307
1322
|
"alg": "SHA3-512",
|
|
1308
|
-
"content": "
|
|
1323
|
+
"content": "58f6d75ff97f64633937bef489fdfefd6cf331115109cab30b2064cf3aebfaaddde6c973fa71b31bb44f06ecb485751ee9137e11f528a61b329b4699d53a4041"
|
|
1309
1324
|
}
|
|
1310
1325
|
]
|
|
1311
1326
|
},
|
|
@@ -1316,11 +1331,11 @@
|
|
|
1316
1331
|
"hashes": [
|
|
1317
1332
|
{
|
|
1318
1333
|
"alg": "SHA-256",
|
|
1319
|
-
"content": "
|
|
1334
|
+
"content": "68bc62828f28047b5f09da9e5fa3c2cc3ed98d965c1a43dbf51b9fa7e45ec1a1"
|
|
1320
1335
|
},
|
|
1321
1336
|
{
|
|
1322
1337
|
"alg": "SHA3-512",
|
|
1323
|
-
"content": "
|
|
1338
|
+
"content": "c05800e93846fb755ded51be74047d84d032eeff9f5a0b818b1abc16f76c7032181ded4d5cf054d0387c3d7f2d71095f55a7a42c7f4c2764ec0115300ce75c85"
|
|
1324
1339
|
}
|
|
1325
1340
|
]
|
|
1326
1341
|
},
|
|
@@ -1451,11 +1466,11 @@
|
|
|
1451
1466
|
"hashes": [
|
|
1452
1467
|
{
|
|
1453
1468
|
"alg": "SHA-256",
|
|
1454
|
-
"content": "
|
|
1469
|
+
"content": "70fda3a2033d461f3ef1ac7e4bf259c9fe78090f1a1f5c09eb72dbf927e59955"
|
|
1455
1470
|
},
|
|
1456
1471
|
{
|
|
1457
1472
|
"alg": "SHA3-512",
|
|
1458
|
-
"content": "
|
|
1473
|
+
"content": "8547b087e1881cf58a55fd0aab186441f508ad226be0ab4aa3efd13a0e864af776f29576e25df9b4324ec7a4712bb3476faac5ed63a226a6ff71d225c297c969"
|
|
1459
1474
|
}
|
|
1460
1475
|
]
|
|
1461
1476
|
},
|
|
@@ -1631,11 +1646,11 @@
|
|
|
1631
1646
|
"hashes": [
|
|
1632
1647
|
{
|
|
1633
1648
|
"alg": "SHA-256",
|
|
1634
|
-
"content": "
|
|
1649
|
+
"content": "fe42cfd0758cb2709dc7dad91c4e47b495d6ebf984b00ace2422de34e744d35f"
|
|
1635
1650
|
},
|
|
1636
1651
|
{
|
|
1637
1652
|
"alg": "SHA3-512",
|
|
1638
|
-
"content": "
|
|
1653
|
+
"content": "a06a61b0f278a5973c0e4d6398d1688a574dec3442ca9823d0b4c5f37d1cb1cab47e8575580051b56e34829f8e22195ded1f4f57bdc3925ca32edae3e4c8839f"
|
|
1639
1654
|
}
|
|
1640
1655
|
]
|
|
1641
1656
|
},
|
|
@@ -1721,11 +1736,11 @@
|
|
|
1721
1736
|
"hashes": [
|
|
1722
1737
|
{
|
|
1723
1738
|
"alg": "SHA-256",
|
|
1724
|
-
"content": "
|
|
1739
|
+
"content": "3d9a53fa10f7c9842903252192f1f838bec422b99ed9562be9fd7aef88dacf20"
|
|
1725
1740
|
},
|
|
1726
1741
|
{
|
|
1727
1742
|
"alg": "SHA3-512",
|
|
1728
|
-
"content": "
|
|
1743
|
+
"content": "62cef0d1915cd4f3a63f952c2fa42c7e0279c15828ae83e5cdd8b6490767400fa7cdfbf5ecedca6fe16859ccfd72ff7acffec7209ae806a4bf6748c63d25bfc7"
|
|
1729
1744
|
}
|
|
1730
1745
|
]
|
|
1731
1746
|
},
|
|
@@ -1736,11 +1751,11 @@
|
|
|
1736
1751
|
"hashes": [
|
|
1737
1752
|
{
|
|
1738
1753
|
"alg": "SHA-256",
|
|
1739
|
-
"content": "
|
|
1754
|
+
"content": "a774939dc7902765bf4be36cd308c34b66a3bb105f4404fa06992b79d5ac3dee"
|
|
1740
1755
|
},
|
|
1741
1756
|
{
|
|
1742
1757
|
"alg": "SHA3-512",
|
|
1743
|
-
"content": "
|
|
1758
|
+
"content": "dd9a282b83be1929330b58def5c377eec93fcb0fb23401c484d81a639c0e5c0df0ff6b151aaf73b720345e88c45f68f47da83ef0114f3520f6b5040f3b64f27b"
|
|
1744
1759
|
}
|
|
1745
1760
|
]
|
|
1746
1761
|
},
|
|
@@ -1751,11 +1766,11 @@
|
|
|
1751
1766
|
"hashes": [
|
|
1752
1767
|
{
|
|
1753
1768
|
"alg": "SHA-256",
|
|
1754
|
-
"content": "
|
|
1769
|
+
"content": "cdd4fb5f74a72b662c5db992bb3de8d484c9c87951a40762f2b5198453747dba"
|
|
1755
1770
|
},
|
|
1756
1771
|
{
|
|
1757
1772
|
"alg": "SHA3-512",
|
|
1758
|
-
"content": "
|
|
1773
|
+
"content": "678e0257b4a71322194c65cc2258c0c76d0f2ec83152ef1bd3a3e453aab360da8550a4430b67eb5f8b03d8314cd283be47bd779cd22087b7855dd90129916168"
|
|
1759
1774
|
}
|
|
1760
1775
|
]
|
|
1761
1776
|
},
|
|
@@ -2201,11 +2216,11 @@
|
|
|
2201
2216
|
"hashes": [
|
|
2202
2217
|
{
|
|
2203
2218
|
"alg": "SHA-256",
|
|
2204
|
-
"content": "
|
|
2219
|
+
"content": "7ef5ed5d9a6dd68b2d43419d7fa2d0408eb8b57b69588e51db136aa0f29e82a6"
|
|
2205
2220
|
},
|
|
2206
2221
|
{
|
|
2207
2222
|
"alg": "SHA3-512",
|
|
2208
|
-
"content": "
|
|
2223
|
+
"content": "b96dad01e1385bc17bb1fa54d2f0d86558063856c12401fca1d96a01869de984b87cf5c54ded025a5f93762c109385ef6aed5552abff36070e281810ae063db8"
|
|
2209
2224
|
}
|
|
2210
2225
|
]
|
|
2211
2226
|
},
|
|
@@ -2216,11 +2231,11 @@
|
|
|
2216
2231
|
"hashes": [
|
|
2217
2232
|
{
|
|
2218
2233
|
"alg": "SHA-256",
|
|
2219
|
-
"content": "
|
|
2234
|
+
"content": "bd449efbfde0263c17aa1d2cf4ff57f39f8221e78a7f8eb62392b522acc7f3b8"
|
|
2220
2235
|
},
|
|
2221
2236
|
{
|
|
2222
2237
|
"alg": "SHA3-512",
|
|
2223
|
-
"content": "
|
|
2238
|
+
"content": "1a7ecda566d16f89d15d721ca491962344fe9f011c27126f48cde5fb887405d5be5a28dac1892add97fd7316ab2204aa69020ede2bc1c43db4d988b45c1f1dea"
|
|
2224
2239
|
}
|
|
2225
2240
|
]
|
|
2226
2241
|
},
|
|
@@ -2486,11 +2501,11 @@
|
|
|
2486
2501
|
"hashes": [
|
|
2487
2502
|
{
|
|
2488
2503
|
"alg": "SHA-256",
|
|
2489
|
-
"content": "
|
|
2504
|
+
"content": "1e140b2a035043d72cfb1a46e57751bbb2d4b3d39ecc73b48c28625b260e17f3"
|
|
2490
2505
|
},
|
|
2491
2506
|
{
|
|
2492
2507
|
"alg": "SHA3-512",
|
|
2493
|
-
"content": "
|
|
2508
|
+
"content": "f027c768c24cd0c5a4aff871afe82482a7ee655a56a69ab0d95662609a95fb28a31a3b4aa1108f879bdbf59ce097a6536e931c128105a7d93436e6eed59bfd82"
|
|
2494
2509
|
}
|
|
2495
2510
|
]
|
|
2496
2511
|
},
|
|
@@ -3154,6 +3169,21 @@
|
|
|
3154
3169
|
}
|
|
3155
3170
|
]
|
|
3156
3171
|
},
|
|
3172
|
+
{
|
|
3173
|
+
"bom-ref": "file:skills/vc-wallet-trust/skill.md",
|
|
3174
|
+
"type": "file",
|
|
3175
|
+
"name": "skills/vc-wallet-trust/skill.md",
|
|
3176
|
+
"hashes": [
|
|
3177
|
+
{
|
|
3178
|
+
"alg": "SHA-256",
|
|
3179
|
+
"content": "802dada75d934c9ab322246f650eb2eac9e2f72c1f094b0cebcfa605b56108a5"
|
|
3180
|
+
},
|
|
3181
|
+
{
|
|
3182
|
+
"alg": "SHA3-512",
|
|
3183
|
+
"content": "bce19482404831b9706509aace15cf36c6a6d10028a26b7d232b4f359a7d7ade3e3fbeac015ed32af4e5ab4410ec4f5c12f1635bd1f2b8687f9ed3d8c5fdd587"
|
|
3184
|
+
}
|
|
3185
|
+
]
|
|
3186
|
+
},
|
|
3157
3187
|
{
|
|
3158
3188
|
"bom-ref": "file:skills/webapp-security/skill.md",
|
|
3159
3189
|
"type": "file",
|
|
@@ -30,7 +30,7 @@ const ROOT = path.resolve(__dirname, "..");
|
|
|
30
30
|
// Every key here was classified (adopted / already-owned / helper-dependent /
|
|
31
31
|
// out-of-scope). A class appearing upstream but absent here is NEW and wants
|
|
32
32
|
// triage. Refresh this list (and re-triage the delta) when this check fires.
|
|
33
|
-
const UPSTREAM_TRIAGED = Object.freeze([
|
|
33
|
+
const UPSTREAM_TRIAGED = Object.freeze([ // keep-sorted
|
|
34
34
|
"ai-disclosure-on-request-without-requested-gate",
|
|
35
35
|
"archive-gz-without-safedecompress",
|
|
36
36
|
"archive-wrap-partial-recipient",
|
|
@@ -59,7 +59,6 @@ const UPSTREAM_TRIAGED = Object.freeze([
|
|
|
59
59
|
"math-random-noncrypto",
|
|
60
60
|
"nfinity",
|
|
61
61
|
"no-number-money-arithmetic",
|
|
62
|
-
"numeric-opt-no-bounds-check",
|
|
63
62
|
"primitive-unreachable",
|
|
64
63
|
"process-exit",
|
|
65
64
|
"raw-byte-literal",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* VALID_ALLOW_CLASSES, or is missing the `— <reason>` tail. A typo'd
|
|
20
20
|
* marker suppresses nothing, so the underlying violation would ship
|
|
21
21
|
* unflagged — this meta-guard keeps the marker mechanism trustworthy.
|
|
22
|
+
* - unsorted-marked-array : a flat string array tagged `// keep-sorted` that
|
|
23
|
+
* drifted out of alphabetical order. Opt-in — only marked arrays are
|
|
24
|
+
* checked, so a one-time allowlist sort becomes a standing guarantee.
|
|
25
|
+
* - misaligned-marked-run : a `// keep-aligned` const/weight table whose
|
|
26
|
+
* `=`/`:` assignment columns are not all equal. Opt-in, same shape.
|
|
22
27
|
*
|
|
23
28
|
* Exceptions live at the violation site, not in this file:
|
|
24
29
|
* - file-level, in the first 50 lines: // codebase-patterns:allow-file <class> — <reason>
|
|
@@ -42,6 +47,8 @@ const VALID_ALLOW_CLASSES = Object.freeze({
|
|
|
42
47
|
"process-exit-after-stdout-write": true,
|
|
43
48
|
"dynamic-regex": true,
|
|
44
49
|
"bidi-codepoint-literal": true,
|
|
50
|
+
"unsorted-marked-array": true,
|
|
51
|
+
"misaligned-marked-run": true,
|
|
45
52
|
});
|
|
46
53
|
|
|
47
54
|
const EXCLUDE_DIRS = new Set([
|
|
@@ -272,6 +279,91 @@ function detectOrphanAllowClass(files) {
|
|
|
272
279
|
return hits;
|
|
273
280
|
}
|
|
274
281
|
|
|
282
|
+
// ---- opt-in readability detectors (preventative) -------------------------
|
|
283
|
+
// These fire ONLY on sites that explicitly opt in via a marker, so unmarked
|
|
284
|
+
// code is never flagged. They turn a one-time cleanup (sorting an allowlist,
|
|
285
|
+
// aligning a const table) into a standing guarantee: mark the cleaned site and
|
|
286
|
+
// the gate keeps it clean.
|
|
287
|
+
|
|
288
|
+
// `// keep-sorted` marks a flat string-literal array that must stay
|
|
289
|
+
// alphabetically sorted (e.g. an allowlist). Only arrays whose opening line
|
|
290
|
+
// carries the marker are checked; arrays containing object/nested elements are
|
|
291
|
+
// skipped (not a flat string list).
|
|
292
|
+
function scanUnsortedMarkedArray(rel, lines) {
|
|
293
|
+
const hits = [];
|
|
294
|
+
for (let i = 0; i < lines.length; i++) {
|
|
295
|
+
if (!/\/\/\s*keep-sorted\b/.test(lines[i])) continue;
|
|
296
|
+
const openIdx = lines[i].indexOf("[");
|
|
297
|
+
if (openIdx === -1) continue;
|
|
298
|
+
let depth = 0, started = false, body = "";
|
|
299
|
+
for (let j = i; j < lines.length; j++) {
|
|
300
|
+
const seg = (j === i) ? lines[j].slice(openIdx) : lines[j];
|
|
301
|
+
for (const ch of seg) {
|
|
302
|
+
if (ch === "[") { depth++; started = true; }
|
|
303
|
+
else if (ch === "]") { depth--; }
|
|
304
|
+
}
|
|
305
|
+
body += " " + seg;
|
|
306
|
+
if (started && depth <= 0) break;
|
|
307
|
+
}
|
|
308
|
+
if (/[{]/.test(body)) continue; // object/nested elements — not a flat string array
|
|
309
|
+
const strs = [];
|
|
310
|
+
const re = /(['"])((?:\\.|(?!\1).)*)\1/g;
|
|
311
|
+
let m;
|
|
312
|
+
while ((m = re.exec(body)) !== null) strs.push(m[2]);
|
|
313
|
+
if (strs.length < 2) continue;
|
|
314
|
+
const sorted = [...strs].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
315
|
+
if (strs.join("") !== sorted.join("")) {
|
|
316
|
+
const k = strs.findIndex((s, idx) => idx > 0 && strs[idx - 1] > s);
|
|
317
|
+
hits.push({ file: rel, line: i + 1, content: lines[i].trim(), why: `marked // keep-sorted but "${strs[k]}" is out of alphabetical order` });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return hits;
|
|
321
|
+
}
|
|
322
|
+
function detectUnsortedMarkedArray(files) {
|
|
323
|
+
const hits = [];
|
|
324
|
+
for (const rel of (files || filesUnder(["bin/exceptd.js", "lib", "orchestrator", "scripts"]))) {
|
|
325
|
+
if (rel === "scripts/check-codebase-patterns.js") continue; // holds the detector + its own marker prose
|
|
326
|
+
hits.push(...scanUnsortedMarkedArray(rel, readLines(rel)));
|
|
327
|
+
}
|
|
328
|
+
return hits;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// `// keep-aligned` marks a contiguous run of `IDENT = value` / `IDENT: value`
|
|
332
|
+
// lines (a const/weight table) whose assignment columns must all line up. The
|
|
333
|
+
// run is the lines immediately after the marker, until a blank or non-assignment
|
|
334
|
+
// line. Opt-in, so only deliberately-aligned tables are enforced.
|
|
335
|
+
function scanMisalignedMarkedRun(rel, lines) {
|
|
336
|
+
const hits = [];
|
|
337
|
+
for (let i = 0; i < lines.length; i++) {
|
|
338
|
+
if (!/\/\/\s*keep-aligned\b/.test(lines[i])) continue;
|
|
339
|
+
const run = [];
|
|
340
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
341
|
+
if (/^\s*$/.test(lines[j])) break;
|
|
342
|
+
const code = stripLineComment(lines[j]).replace(/\s+$/, "");
|
|
343
|
+
const m = code.match(/^(\s*[A-Za-z_$][\w$.'"-]*\s*)([:=])\s/);
|
|
344
|
+
if (!m) break;
|
|
345
|
+
run.push({ lineNo: j + 1, col: m[1].length, op: m[2], content: lines[j].trim() });
|
|
346
|
+
}
|
|
347
|
+
if (run.length < 2) continue;
|
|
348
|
+
const op = run[0].op;
|
|
349
|
+
const cols = run.filter((r) => r.op === op).map((r) => r.col);
|
|
350
|
+
const target = Math.max(...cols);
|
|
351
|
+
const bad = run.find((r) => r.op === op && r.col !== target);
|
|
352
|
+
if (bad) {
|
|
353
|
+
hits.push({ file: rel, line: bad.lineNo, content: bad.content, why: `marked // keep-aligned but the '${op}' columns are not all equal` });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return hits;
|
|
357
|
+
}
|
|
358
|
+
function detectMisalignedMarkedRun(files) {
|
|
359
|
+
const hits = [];
|
|
360
|
+
for (const rel of (files || filesUnder(["bin/exceptd.js", "lib", "orchestrator", "scripts"]))) {
|
|
361
|
+
if (rel === "scripts/check-codebase-patterns.js") continue;
|
|
362
|
+
hits.push(...scanMisalignedMarkedRun(rel, readLines(rel)));
|
|
363
|
+
}
|
|
364
|
+
return hits;
|
|
365
|
+
}
|
|
366
|
+
|
|
275
367
|
const CLASSES = [
|
|
276
368
|
{
|
|
277
369
|
id: "process-exit-after-stdout-write",
|
|
@@ -282,9 +374,21 @@ const CLASSES = [
|
|
|
282
374
|
{
|
|
283
375
|
id: "dynamic-regex",
|
|
284
376
|
run: detectDynamicRegex,
|
|
285
|
-
warnOnly:
|
|
377
|
+
warnOnly: false,
|
|
286
378
|
hint: "RegExp from operator input is a ReDoS sink — anchor + length-cap, or `// allow:dynamic-regex — <reason>` when the pattern is a trusted bundled schema",
|
|
287
379
|
},
|
|
380
|
+
{
|
|
381
|
+
id: "unsorted-marked-array",
|
|
382
|
+
run: detectUnsortedMarkedArray,
|
|
383
|
+
warnOnly: false,
|
|
384
|
+
hint: "a flat string array tagged `// keep-sorted` drifted out of alphabetical order — re-sort it, or drop the marker if the order is intentional",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
id: "misaligned-marked-run",
|
|
388
|
+
run: detectMisalignedMarkedRun,
|
|
389
|
+
warnOnly: false,
|
|
390
|
+
hint: "a `// keep-aligned` const/weight table has uneven assignment columns — realign the `=`/`:` columns, or drop the marker",
|
|
391
|
+
},
|
|
288
392
|
{
|
|
289
393
|
id: "bidi-codepoint-literal",
|
|
290
394
|
run: detectBidiCodepointLiteral,
|
|
@@ -331,6 +435,10 @@ module.exports = {
|
|
|
331
435
|
detectDynamicRegex,
|
|
332
436
|
detectBidiCodepointLiteral,
|
|
333
437
|
detectOrphanAllowClass,
|
|
438
|
+
detectUnsortedMarkedArray,
|
|
439
|
+
detectMisalignedMarkedRun,
|
|
440
|
+
scanUnsortedMarkedArray,
|
|
441
|
+
scanMisalignedMarkedRun,
|
|
334
442
|
filesUnder,
|
|
335
443
|
};
|
|
336
444
|
|
package/scripts/release.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Usage:
|
|
13
13
|
* node scripts/release.js prepare [--minor] # bump + sign + indexes + snapshot + sbom + baseline
|
|
14
|
-
* node scripts/release.js gates # npm test +
|
|
14
|
+
* node scripts/release.js gates # npm test + 20-gate predeploy
|
|
15
15
|
* node scripts/release.js commit # release branch + signed commit
|
|
16
16
|
* node scripts/release.js push # push branch + open PR
|
|
17
17
|
* node scripts/release.js watch # CI watch + flag unresolved review threads
|
|
@@ -576,7 +576,7 @@ function cmdHelp() {
|
|
|
576
576
|
console.log("");
|
|
577
577
|
console.log("Usage:");
|
|
578
578
|
console.log(" node scripts/release.js prepare [--minor] # bump + sign + indexes + snapshot + sbom + baseline");
|
|
579
|
-
console.log(" node scripts/release.js gates # npm test +
|
|
579
|
+
console.log(" node scripts/release.js gates # npm test + 20-gate predeploy");
|
|
580
580
|
console.log(" node scripts/release.js commit # release branch + signed commit");
|
|
581
581
|
console.log(" node scripts/release.js push # push branch + open PR");
|
|
582
582
|
console.log(" node scripts/release.js watch # CI watch + flag unresolved review threads");
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vc-wallet-trust
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: Verifiable-credential / digital-wallet verifier trust for mid-2026 — SD-JWT-VC, OID4VCI/OID4VP, mdoc (ISO 18013-5), DID resolution, OAuth Token Status List revocation, OpenID Federation trust anchors, and the EUDI wallet (eIDAS 2.0) acceptance path
|
|
5
|
+
triggers:
|
|
6
|
+
- verifiable credential
|
|
7
|
+
- digital wallet
|
|
8
|
+
- sd-jwt-vc
|
|
9
|
+
- oid4vp
|
|
10
|
+
- oid4vci
|
|
11
|
+
- mdoc
|
|
12
|
+
- mdl
|
|
13
|
+
- iso 18013-5
|
|
14
|
+
- eudi wallet
|
|
15
|
+
- eidas 2.0
|
|
16
|
+
- did:web
|
|
17
|
+
- status list
|
|
18
|
+
- credential revocation
|
|
19
|
+
- openid federation
|
|
20
|
+
- trust anchor
|
|
21
|
+
- credential verifier
|
|
22
|
+
- presentation exchange
|
|
23
|
+
- dcql
|
|
24
|
+
discovery_mode: standalone
|
|
25
|
+
data_deps:
|
|
26
|
+
- cve-catalog.json
|
|
27
|
+
- atlas-ttps.json
|
|
28
|
+
- attack-techniques.json
|
|
29
|
+
- framework-control-gaps.json
|
|
30
|
+
- cwe-catalog.json
|
|
31
|
+
- rfc-references.json
|
|
32
|
+
atlas_refs: []
|
|
33
|
+
attack_refs:
|
|
34
|
+
- T1556
|
|
35
|
+
- T1606
|
|
36
|
+
- T1550
|
|
37
|
+
framework_gaps:
|
|
38
|
+
- NIST-800-63B-rev4
|
|
39
|
+
- NIST-800-53-IA-5-Federated
|
|
40
|
+
- ISO-27001-2022-A.5.16-Federated
|
|
41
|
+
- NIS2-Art-21-Federated-Identity
|
|
42
|
+
- UK-CAF-B2
|
|
43
|
+
cwe_refs:
|
|
44
|
+
- CWE-347
|
|
45
|
+
- CWE-290
|
|
46
|
+
- CWE-863
|
|
47
|
+
- CWE-200
|
|
48
|
+
- CWE-672
|
|
49
|
+
last_threat_review: "2026-06-02"
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Verifiable-Credential / Digital-Wallet Verifier Trust
|
|
53
|
+
|
|
54
|
+
## Threat Context (mid-2026)
|
|
55
|
+
|
|
56
|
+
A credential verifier is a trust boundary: every verifiable credential it accepts grants whatever the credential asserts — age, residency, employment, professional licence, payment authority. With the EU Digital Identity Wallet (eIDAS 2.0) rolling out and ISO 18013-5 mobile driving licences in production, verifiers across payments, age-gating, and onboarding now accept SD-JWT-VC, OID4VP, and mdoc presentations from wallets they do not control. The dominant abuse is not breaking the cryptography but exploiting a missing trust check: an issuer key the verifier never pinned to an anchor, a revocation status it never read, a presentation it never bound to a fresh challenge, or a device signature it never verified. Each lets an attacker present a forged, revoked, or replayed credential that the verifier treats as authentic.
|
|
57
|
+
|
|
58
|
+
## Framework Lag Declaration
|
|
59
|
+
|
|
60
|
+
Organisational identity controls were written for service-to-service and human-credential authentication and are silent on the verifiable-credential acceptance path. NIST 800-53 IA-9 (service identification) and IA-5 (authenticator management) do not require credential issuer trust-anchor pinning or presentation replay-binding. ISO 27001:2022 A.5.16 governs the lifecycle of internal identities, not the trust model for externally-issued credentials a verifier accepts. NIS2 Art.21 names supply-chain trust of entities but not the cryptographic anchor model for the credentials those entities present. A clean identity-control audit is therefore NON-EVIDENCE for verifier trust posture; the controls predate the wallet ecosystem and do not exercise it.
|
|
61
|
+
|
|
62
|
+
## TTP Mapping
|
|
63
|
+
|
|
64
|
+
The verifier-trust failure modes map to MITRE ATT&CK: **T1606 (Forge Web Credentials)** for accepting a credential from an unanchored or algorithm-substituted issuer key; **T1556 (Modify Authentication Process)** for trusting an unpinned did:web document, an unverified key attestation, or an unanchored federation chain; and **T1550 (Use Alternate Authentication Material)** for replaying a presentation that lacks nonce/audience binding, replaying issuer-signed mdoc data without device-auth, or presenting a revoked credential whose status was never checked. The weakness classes are CWE-347 (improper signature verification), CWE-290 (authentication bypass by spoofing), CWE-672 (operation on a resource after expiration/revocation), CWE-863 (incorrect authorization), and CWE-200 (over-disclosure of personal claims).
|
|
65
|
+
|
|
66
|
+
## Exploit Availability Matrix
|
|
67
|
+
|
|
68
|
+
These are configuration and code-posture gaps, not single-CVE exploits, so weaponisation cost is low and reusable. Forging an unanchored issuer requires only standing up an OID4VCI issuer or serving a crafted did:web document — commodity tooling. Replaying an unbound presentation requires capturing one valid presentation and resubmitting it. Presenting a revoked credential requires nothing beyond holding a credential whose authorisation was withdrawn. Algorithm-substitution requires the verifier library to accept an unexpected alg. None require a published CVE; the exploit is the absence of the check. Real-world priority is driven by reachability (internet-facing verifier) and the value of the entitlement the credential gates, not by a CVSS score.
|
|
69
|
+
|
|
70
|
+
## Analysis Procedure
|
|
71
|
+
|
|
72
|
+
1. Inventory every service that ACCEPTS credentials (issuer-only services are out of scope for the verifier checks). 2. For each accepted format (SD-JWT-VC, OID4VP, mdoc, DID-identified), read the verifier accept-path and answer: is the issuer key validated against a pinned trust anchor or issuer allowlist? 3. Is the revocation / status-list resolved and enforced fail-closed? 4. Are presentations bound to a fresh verifier-issued nonce and audience (key-binding required, device-auth verified for mdoc)? 5. Is an explicit signature-algorithm allowlist enforced with "none" and unexpected symmetric algorithms refused? 6. Are disclosed claims filtered to the requested query (no over-disclosure)? Run the `vc-wallet-trust` playbook to execute these as detect indicators with false-positive checks, then score by reachability and entitlement value.
|
|
73
|
+
|
|
74
|
+
## Output Format
|
|
75
|
+
|
|
76
|
+
Report per accepted credential format, listing each trust check as enforced / missing / inconclusive (visibility gap). For every missing check, state the credential types and downstream entitlements it gates, whether the verifier is internet-facing, and the resulting blast radius. Distinguish a production-reachable gap from a test-only resolver. Provide the prioritised remediation (pin issuer anchors, enforce revocation fail-closed, bind presentations to nonce+audience, verify mdoc device-auth, enforce an algorithm allowlist, filter to requested claims) and the negative validation tests that prove each fix (forged-issuer rejected, revoked rejected, replayed rejected) plus the positive test that the legitimate path still accepts.
|
|
77
|
+
|
|
78
|
+
## Compliance Theater Check
|
|
79
|
+
|
|
80
|
+
The recurring theater is "we accept a certified wallet, so acceptance is trustworthy" and "the signature verified, so the credential is trusted." Wallet certification covers the wallet, not the verifier; a valid signature only proves some key signed the credential, not that the issuer is authentic. The distinguishing test: ask for the verifier's trust-anchor / issuer-allowlist configuration, the revocation check on the accept path, and the presentation nonce/audience binding. If acceptance succeeds against an unpinned issuer key, a never-read status list, or an unbound presentation, the assurance is paper. "Our credentials support a status list" is theater unless the verifier accept-path actually fetches and enforces it.
|
|
81
|
+
|
|
82
|
+
## Defensive Countermeasure Mapping
|
|
83
|
+
|
|
84
|
+
Map findings to MITRE D3FEND: trust-anchor pinning and issuer-allowlist validation realise Credential Hardening and Certificate Pinning (countering T1606/T1556); presentation nonce/audience binding and mdoc device-auth verification realise Authentication Event Thresholding and Message Authentication (countering T1550 replay); fail-closed revocation enforcement realises Credential Revoking. Pair the verifier checks with issuer key-rotation monitoring and a fast anchor-revocation path so a compromised-but-trusted issuer can be removed quickly. The residual risk after pinning is a trusted issuer's own key compromise, which trust-anchor pinning does not address — accept it at the CISO level with compensating issuer-key monitoring.
|