@blamejs/exceptd-skills 0.14.0 → 0.14.2
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 +3 -1
- package/CHANGELOG.md +16 -0
- package/README.md +31 -0
- package/bin/exceptd.js +31 -1
- package/data/_indexes/_meta.json +3 -3
- package/data/_indexes/activity-feed.json +8 -8
- package/data/_indexes/catalog-summaries.json +2 -2
- package/data/_indexes/frequency.json +1413 -1
- package/data/playbooks/citation-hygiene.json +1 -1
- package/data/rfc-references.json +55757 -146
- package/lib/citation-resolve.js +226 -0
- package/lib/collectors/citation-hygiene.js +81 -1
- package/lib/cve-cli.js +51 -0
- package/lib/flag-suggest.js +1 -1
- package/lib/rfc-cli.js +68 -0
- package/lib/schemas/cve-catalog.schema.json +13 -0
- package/lib/source-ghsa.js +3 -0
- package/lib/source-osv.js +4 -0
- package/lib/validate-package.js +7 -2
- package/manifest.json +44 -44
- package/package.json +2 -2
- package/sbom.cdx.json +84 -39
- package/scripts/refresh-upstream-catalogs.js +12 -2
- package/sources/validators/cve-validator.js +46 -1
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:0af1688b-ffe0-4b0b-b177-9598a354bd03",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
7
|
+
"timestamp": "2031-10-26T22:04:59.000Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "blamejs",
|
|
11
11
|
"name": "scripts/refresh-sbom.js",
|
|
12
|
-
"version": "0.14.
|
|
12
|
+
"version": "0.14.2"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.14.
|
|
16
|
+
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.14.2",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.14.
|
|
20
|
-
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (406 CVEs / 171 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND /
|
|
19
|
+
"version": "0.14.2",
|
|
20
|
+
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (406 CVEs / 171 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.14.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.14.2",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
32
|
+
"content": "d1c4e08bc2fcade623d92d3a5d9398cf2a804e50aea76b0efd31f45c09184016"
|
|
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.14.
|
|
38
|
+
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.14.2"
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"type": "vcs",
|
|
@@ -86,11 +86,11 @@
|
|
|
86
86
|
"hashes": [
|
|
87
87
|
{
|
|
88
88
|
"alg": "SHA-256",
|
|
89
|
-
"content": "
|
|
89
|
+
"content": "6327718cdd949622fbd4af0846fab9a0a5a8f501e173f014534fe8ff4ba22110"
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"alg": "SHA3-512",
|
|
93
|
-
"content": "
|
|
93
|
+
"content": "146c237d4f022c4a9819ad11d0328b592bc234d38dbcb7e5cf78149fe5497ed81769e68520e91a9600767200119594ed3a7234add92d1c12a087bd9b1fa7a116"
|
|
94
94
|
}
|
|
95
95
|
]
|
|
96
96
|
},
|
|
@@ -116,11 +116,11 @@
|
|
|
116
116
|
"hashes": [
|
|
117
117
|
{
|
|
118
118
|
"alg": "SHA-256",
|
|
119
|
-
"content": "
|
|
119
|
+
"content": "8ad69ce7f1057bfa600caa1cca31bd4332a75b6c403ab3087c923dec6c4f756c"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
123
|
+
"content": "b4389da9ddc26c2730e2187de177c9bf3d6f4d9cffb838ac32f4b9a10205dd37099dd171de68ec9df160049b347bc08e96bba5ec9d665ac28bd626d8afdaa1cd"
|
|
124
124
|
}
|
|
125
125
|
]
|
|
126
126
|
},
|
|
@@ -176,11 +176,11 @@
|
|
|
176
176
|
"hashes": [
|
|
177
177
|
{
|
|
178
178
|
"alg": "SHA-256",
|
|
179
|
-
"content": "
|
|
179
|
+
"content": "ae39088caf4e044bf0449b97c99acfe76ad30b8a9d8e008b438f1cbda0dc16d5"
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"alg": "SHA3-512",
|
|
183
|
-
"content": "
|
|
183
|
+
"content": "21a01dee170f40b3b5d77360fb209c89b60a8decbbf4c0a871d72176fed03cfb7b45698830741802a903deee84c601ab25a47db6d600679e259368a4ce4c171d"
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
},
|
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
"hashes": [
|
|
282
282
|
{
|
|
283
283
|
"alg": "SHA-256",
|
|
284
|
-
"content": "
|
|
284
|
+
"content": "5c10d15f2560ef31ffce7904f7d3ad5cd6f0377b3353971f5f3f2834d9090dd3"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
288
|
+
"content": "1db6e57551d5c411dd0e3ce937556930e2549adc2c145c8548bc651963901f8d4c7c5825f7dce8dce115d9bbe5d6bd7fc61ef563532beb288770a0cd28c0501b"
|
|
289
289
|
}
|
|
290
290
|
]
|
|
291
291
|
},
|
|
@@ -476,11 +476,11 @@
|
|
|
476
476
|
"hashes": [
|
|
477
477
|
{
|
|
478
478
|
"alg": "SHA-256",
|
|
479
|
-
"content": "
|
|
479
|
+
"content": "69b25f892979583d886409379562b9a1a97f27f1dc0094480155b8086fec6798"
|
|
480
480
|
},
|
|
481
481
|
{
|
|
482
482
|
"alg": "SHA3-512",
|
|
483
|
-
"content": "
|
|
483
|
+
"content": "62a1b38da1c1865a18d1fe585e8a57cf0f346a3999e91df9dd1a4e52e942ec892b16dcf5af8f726e921f3297044ed54f627cf8549f6b78c6e71520e68f1d93aa"
|
|
484
484
|
}
|
|
485
485
|
]
|
|
486
486
|
},
|
|
@@ -791,11 +791,11 @@
|
|
|
791
791
|
"hashes": [
|
|
792
792
|
{
|
|
793
793
|
"alg": "SHA-256",
|
|
794
|
-
"content": "
|
|
794
|
+
"content": "b21d03b948c41bc8a854e2f057948ecf844bd8c105848aeb141d1eadf8192c31"
|
|
795
795
|
},
|
|
796
796
|
{
|
|
797
797
|
"alg": "SHA3-512",
|
|
798
|
-
"content": "
|
|
798
|
+
"content": "19e242bf80e0d2984e0e807c3c4b236afc59561cc63a29efcc1f34488adc71dd0ba21eb8b6162eae83b87ca872d2d5073c1efc7aff05761b16b76631acfaaba4"
|
|
799
799
|
}
|
|
800
800
|
]
|
|
801
801
|
},
|
|
@@ -874,6 +874,21 @@
|
|
|
874
874
|
}
|
|
875
875
|
]
|
|
876
876
|
},
|
|
877
|
+
{
|
|
878
|
+
"bom-ref": "file:lib/citation-resolve.js",
|
|
879
|
+
"type": "file",
|
|
880
|
+
"name": "lib/citation-resolve.js",
|
|
881
|
+
"hashes": [
|
|
882
|
+
{
|
|
883
|
+
"alg": "SHA-256",
|
|
884
|
+
"content": "cbfe304f39dfcc2a889171ff92c17b0c8590ccf69f2883cf630499ef4e89d04f"
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"alg": "SHA3-512",
|
|
888
|
+
"content": "92057e0bfc5e24cdac6251fd35aaab415afe3ea9a5a31623016779d0e7ad3d56fc607dea815c6cd7fd21ccbcc9294ecfe8817486c6611611b0ca4f9113d4178c"
|
|
889
|
+
}
|
|
890
|
+
]
|
|
891
|
+
},
|
|
877
892
|
{
|
|
878
893
|
"bom-ref": "file:lib/collectors/README.md",
|
|
879
894
|
"type": "file",
|
|
@@ -926,11 +941,11 @@
|
|
|
926
941
|
"hashes": [
|
|
927
942
|
{
|
|
928
943
|
"alg": "SHA-256",
|
|
929
|
-
"content": "
|
|
944
|
+
"content": "5576bf3ca7f4f2c69f22c93303d808c3f302bf53b62d46cbb7ec23f5f72f9f8b"
|
|
930
945
|
},
|
|
931
946
|
{
|
|
932
947
|
"alg": "SHA3-512",
|
|
933
|
-
"content": "
|
|
948
|
+
"content": "6f2f8db0387761724feb916761459b73c9ab58064de5d5b30e3e04141601bd36e8903fc1e351501abc48501dfac9ecc6583d89afdfd4df27c9394db1a9b9eda4"
|
|
934
949
|
}
|
|
935
950
|
]
|
|
936
951
|
},
|
|
@@ -1129,6 +1144,21 @@
|
|
|
1129
1144
|
}
|
|
1130
1145
|
]
|
|
1131
1146
|
},
|
|
1147
|
+
{
|
|
1148
|
+
"bom-ref": "file:lib/cve-cli.js",
|
|
1149
|
+
"type": "file",
|
|
1150
|
+
"name": "lib/cve-cli.js",
|
|
1151
|
+
"hashes": [
|
|
1152
|
+
{
|
|
1153
|
+
"alg": "SHA-256",
|
|
1154
|
+
"content": "b1012dac78542a9c7fad7945ee1c15671d710a39d054ab9a6ef0a8f78c9282f2"
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
"alg": "SHA3-512",
|
|
1158
|
+
"content": "6b2892048c6338da5ea68a490bd861d71e7ff31affb2fdbe13b48202d7b71b6b9ea453692f4cdca6c756725b747f4310fc174e21cf2700fda32022ab57955e20"
|
|
1159
|
+
}
|
|
1160
|
+
]
|
|
1161
|
+
},
|
|
1132
1162
|
{
|
|
1133
1163
|
"bom-ref": "file:lib/cve-curation.js",
|
|
1134
1164
|
"type": "file",
|
|
@@ -1196,11 +1226,11 @@
|
|
|
1196
1226
|
"hashes": [
|
|
1197
1227
|
{
|
|
1198
1228
|
"alg": "SHA-256",
|
|
1199
|
-
"content": "
|
|
1229
|
+
"content": "65257992ec15d25efa25667bd605166159b363719be86ddf4e99f53ce41c54e6"
|
|
1200
1230
|
},
|
|
1201
1231
|
{
|
|
1202
1232
|
"alg": "SHA3-512",
|
|
1203
|
-
"content": "
|
|
1233
|
+
"content": "31ad733cf5ed8177131f4a73235dd0991946b7eacfe7de3ddcb8717065804f076e66e3a7103c4304c52d33ff1799037893599a4924ef6f56e7c27a30ea615e8d"
|
|
1204
1234
|
}
|
|
1205
1235
|
]
|
|
1206
1236
|
},
|
|
@@ -1339,6 +1369,21 @@
|
|
|
1339
1369
|
}
|
|
1340
1370
|
]
|
|
1341
1371
|
},
|
|
1372
|
+
{
|
|
1373
|
+
"bom-ref": "file:lib/rfc-cli.js",
|
|
1374
|
+
"type": "file",
|
|
1375
|
+
"name": "lib/rfc-cli.js",
|
|
1376
|
+
"hashes": [
|
|
1377
|
+
{
|
|
1378
|
+
"alg": "SHA-256",
|
|
1379
|
+
"content": "53800bf89522a15e0f8f6f888c5f04410f6c17a2c6537738dc74d5e173a9d43c"
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
"alg": "SHA3-512",
|
|
1383
|
+
"content": "170bbfd54710e1834f1d6e64d6c22c15f6e884bd874053c42c7d1dbbb42effa937572ba8d6617af7c0f698776e13b9a350ad020ebe06bac6a3f9cb972a30ec6c"
|
|
1384
|
+
}
|
|
1385
|
+
]
|
|
1386
|
+
},
|
|
1342
1387
|
{
|
|
1343
1388
|
"bom-ref": "file:lib/schemas/cve-catalog.schema.json",
|
|
1344
1389
|
"type": "file",
|
|
@@ -1346,11 +1391,11 @@
|
|
|
1346
1391
|
"hashes": [
|
|
1347
1392
|
{
|
|
1348
1393
|
"alg": "SHA-256",
|
|
1349
|
-
"content": "
|
|
1394
|
+
"content": "8ed8c965f9f1725313333abbc032b0d85a03653cc979781948cc770b88c11659"
|
|
1350
1395
|
},
|
|
1351
1396
|
{
|
|
1352
1397
|
"alg": "SHA3-512",
|
|
1353
|
-
"content": "
|
|
1398
|
+
"content": "91b4583a35b86beecc84f6c217b5b3bc5a432f4b6c56667c9d83aad774b44fedc00d42186394084828abe7978f1bc645dc213a8aeccfe714f5b6dcdb3273b1fa"
|
|
1354
1399
|
}
|
|
1355
1400
|
]
|
|
1356
1401
|
},
|
|
@@ -1451,11 +1496,11 @@
|
|
|
1451
1496
|
"hashes": [
|
|
1452
1497
|
{
|
|
1453
1498
|
"alg": "SHA-256",
|
|
1454
|
-
"content": "
|
|
1499
|
+
"content": "06dc58abb5ed96d7e85c625e5fca1a49ccb23451771104de8ea1fbb097a484e1"
|
|
1455
1500
|
},
|
|
1456
1501
|
{
|
|
1457
1502
|
"alg": "SHA3-512",
|
|
1458
|
-
"content": "
|
|
1503
|
+
"content": "900372a6681d99d2dba8ce3bc1a575f0a5225f6ae34808fe96cf6b7b988a728b82bc1a2c798a7225915009d55115276f58e8b877be23b3a773e8d3548846e0ab"
|
|
1459
1504
|
}
|
|
1460
1505
|
]
|
|
1461
1506
|
},
|
|
@@ -1466,11 +1511,11 @@
|
|
|
1466
1511
|
"hashes": [
|
|
1467
1512
|
{
|
|
1468
1513
|
"alg": "SHA-256",
|
|
1469
|
-
"content": "
|
|
1514
|
+
"content": "73a41241f2b425aebada9ea641590cb8d596390f93f9e87d07c35f3cb1b1421c"
|
|
1470
1515
|
},
|
|
1471
1516
|
{
|
|
1472
1517
|
"alg": "SHA3-512",
|
|
1473
|
-
"content": "
|
|
1518
|
+
"content": "53728b018b92ab98300b76b097884c2984a2c7b56f4bf059c5be7b84c37eb8084fd90673588aba7f2f02b14b0e9959010026aa4684d3d6660e705f41308439cc"
|
|
1474
1519
|
}
|
|
1475
1520
|
]
|
|
1476
1521
|
},
|
|
@@ -1571,11 +1616,11 @@
|
|
|
1571
1616
|
"hashes": [
|
|
1572
1617
|
{
|
|
1573
1618
|
"alg": "SHA-256",
|
|
1574
|
-
"content": "
|
|
1619
|
+
"content": "fa34eff1166720bc4f2f72bb6b1cd2d98c1e803fd7e5b192631ae0f2fcba48dd"
|
|
1575
1620
|
},
|
|
1576
1621
|
{
|
|
1577
1622
|
"alg": "SHA3-512",
|
|
1578
|
-
"content": "
|
|
1623
|
+
"content": "062707a2721ff3b9f38a4220d377eae456ac28ca6765fd10ede310959fd62ec078649fea9fd21f826f041b84353cd4549c5a3f90004c7949a70b9aa128910707"
|
|
1579
1624
|
}
|
|
1580
1625
|
]
|
|
1581
1626
|
},
|
|
@@ -1706,11 +1751,11 @@
|
|
|
1706
1751
|
"hashes": [
|
|
1707
1752
|
{
|
|
1708
1753
|
"alg": "SHA-256",
|
|
1709
|
-
"content": "
|
|
1754
|
+
"content": "e3c6dc62608d025f8f60ae232bdd54da7433eb8c1d29bdef81deee51122dae4c"
|
|
1710
1755
|
},
|
|
1711
1756
|
{
|
|
1712
1757
|
"alg": "SHA3-512",
|
|
1713
|
-
"content": "
|
|
1758
|
+
"content": "e526912ad23c013ab847d002800b354a74e2f89663702b34a9b82eb18ea99b1fd725cacef689543f2e5eee305df757420bb802978cb108f5c58d5156cec6482f"
|
|
1714
1759
|
}
|
|
1715
1760
|
]
|
|
1716
1761
|
},
|
|
@@ -2381,11 +2426,11 @@
|
|
|
2381
2426
|
"hashes": [
|
|
2382
2427
|
{
|
|
2383
2428
|
"alg": "SHA-256",
|
|
2384
|
-
"content": "
|
|
2429
|
+
"content": "f0186de029d872b8523eb8a47e1d1162c5eddc6ed45e62e010a0b4a82ec67c26"
|
|
2385
2430
|
},
|
|
2386
2431
|
{
|
|
2387
2432
|
"alg": "SHA3-512",
|
|
2388
|
-
"content": "
|
|
2433
|
+
"content": "37d62e5a201c83be7d5882e8f1a968593f81b6b1cb2fb6c811107a19c2faa1890a4e19dd54f8233a866434bd15a1f9c43baeb2458676c9a1d3fef2cd85fc3e80"
|
|
2389
2434
|
}
|
|
2390
2435
|
]
|
|
2391
2436
|
},
|
|
@@ -3101,11 +3146,11 @@
|
|
|
3101
3146
|
"hashes": [
|
|
3102
3147
|
{
|
|
3103
3148
|
"alg": "SHA-256",
|
|
3104
|
-
"content": "
|
|
3149
|
+
"content": "eadeff28bc343c7f7c270a6bc1a7593449cd1d238e49e4e3f74817d07b91de9a"
|
|
3105
3150
|
},
|
|
3106
3151
|
{
|
|
3107
3152
|
"alg": "SHA3-512",
|
|
3108
|
-
"content": "
|
|
3153
|
+
"content": "920540ecd16f41814cf345ebfe505093efb1501c58aa81cd3dfb58237ac2d25264b6c33331d0989d22944df2edbcfa1d656bb177ab5af98d928f3838e954a5ae"
|
|
3109
3154
|
}
|
|
3110
3155
|
]
|
|
3111
3156
|
},
|
|
@@ -249,10 +249,18 @@ async function refreshRfc({ dry = false } = {}) {
|
|
|
249
249
|
if (touched) { cur.last_verified = TODAY; backfilledCount++; }
|
|
250
250
|
}
|
|
251
251
|
// Second pass: add new "current" entries that weren't in the catalog.
|
|
252
|
-
|
|
252
|
+
// Add new rows from the FULL index, not just the current series. Obsoleted
|
|
253
|
+
// and historic RFCs were previously excluded, so "is RFC N still current?"
|
|
254
|
+
// had no offline answer and forced a datatracker lookup. They are added here
|
|
255
|
+
// marked `_obsoleted` (with obsoleted_by populated) so the resolver can say
|
|
256
|
+
// "Historic, superseded by RFC X" offline. UNKNOWN-status index rows
|
|
257
|
+
// (placeholders / not-issued numbers) are still skipped.
|
|
258
|
+
for (const e of backfillable) {
|
|
253
259
|
const id = `RFC-${e.num}`;
|
|
254
260
|
// Existing rows handled in the first-pass backfill above.
|
|
255
261
|
if (existing.has(id)) continue;
|
|
262
|
+
if (e.status === "UNKNOWN") continue;
|
|
263
|
+
const obsoleted = !!e.obsoleted || e.status === "HISTORIC";
|
|
256
264
|
cat[id] = {
|
|
257
265
|
number: e.num,
|
|
258
266
|
title: e.title,
|
|
@@ -269,6 +277,7 @@ async function refreshRfc({ dry = false } = {}) {
|
|
|
269
277
|
obsoletes: e.obsoletes,
|
|
270
278
|
updates: e.updates,
|
|
271
279
|
updated_by: e.updatedBy,
|
|
280
|
+
obsoleted_by: e.obsoletedBy,
|
|
272
281
|
is_also: e.isAlso,
|
|
273
282
|
errata_count: e.hasErrata ? null : 0,
|
|
274
283
|
tracker: `https://www.rfc-editor.org/info/rfc${e.num}`,
|
|
@@ -278,7 +287,8 @@ async function refreshRfc({ dry = false } = {}) {
|
|
|
278
287
|
skills_referencing: [],
|
|
279
288
|
last_verified: TODAY,
|
|
280
289
|
_auto_imported: true,
|
|
281
|
-
_intake_method: "ietf-rfc-index"
|
|
290
|
+
_intake_method: "ietf-rfc-index",
|
|
291
|
+
...(obsoleted ? { _obsoleted: true } : {}),
|
|
282
292
|
};
|
|
283
293
|
existing.add(id);
|
|
284
294
|
added++;
|
|
@@ -112,6 +112,27 @@ function extractNvdCvss(nvdJson) {
|
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// NVD carries the authoritative assignment lifecycle in `vulnStatus`
|
|
116
|
+
// ("Rejected", "Disputed", "Analyzed", "Awaiting Analysis", "Modified", …)
|
|
117
|
+
// and dispute/quality flags in `cveTags[].tags` (e.g. "disputed",
|
|
118
|
+
// "unsupported-when-assigned"). Both live on the same vuln object the CVSS
|
|
119
|
+
// extractor already reads — surfacing them is what lets exceptd answer
|
|
120
|
+
// "is this CVE rejected/disputed?" instead of leaving an agent to look it up.
|
|
121
|
+
function extractNvdStatus(nvdJson) {
|
|
122
|
+
const vuln = nvdJson?.vulnerabilities?.[0]?.cve;
|
|
123
|
+
if (!vuln) return { vulnStatus: null, tags: [] };
|
|
124
|
+
const tags = [];
|
|
125
|
+
for (const ct of (Array.isArray(vuln.cveTags) ? vuln.cveTags : [])) {
|
|
126
|
+
for (const t of (Array.isArray(ct?.tags) ? ct.tags : [])) {
|
|
127
|
+
if (typeof t === 'string') tags.push(t);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
vulnStatus: typeof vuln.vulnStatus === 'string' ? vuln.vulnStatus : null,
|
|
132
|
+
tags,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
115
136
|
function pushDiscrepancy(list, field, local, fetched, severity = 'warning') {
|
|
116
137
|
list.push({ field, local, fetched, severity });
|
|
117
138
|
}
|
|
@@ -153,6 +174,9 @@ async function validateCve(cveId, localEntry) {
|
|
|
153
174
|
in_kev: null,
|
|
154
175
|
kev_date: null,
|
|
155
176
|
epss: null,
|
|
177
|
+
nvd_vuln_status: null, // NVD's authoritative status string, e.g. "Rejected"
|
|
178
|
+
cve_tags: [], // NVD cveTags, e.g. ["disputed"]
|
|
179
|
+
description: null, // NVD English description — the product/scope claim a citation must match
|
|
156
180
|
sources: { nvd: null, kev: null, epss: null },
|
|
157
181
|
};
|
|
158
182
|
const discrepancies = [];
|
|
@@ -175,6 +199,12 @@ async function validateCve(cveId, localEntry) {
|
|
|
175
199
|
fetched.sources.nvd = { reachable: true, found: false };
|
|
176
200
|
} else {
|
|
177
201
|
cveFoundInNvd = true;
|
|
202
|
+
const st = extractNvdStatus(nvdResult.json);
|
|
203
|
+
fetched.nvd_vuln_status = st.vulnStatus;
|
|
204
|
+
fetched.cve_tags = st.tags;
|
|
205
|
+
const vuln = nvdResult.json?.vulnerabilities?.[0]?.cve;
|
|
206
|
+
const enDesc = (Array.isArray(vuln?.descriptions) ? vuln.descriptions : []).find(d => d?.lang === "en");
|
|
207
|
+
fetched.description = (enDesc && typeof enDesc.value === "string") ? enDesc.value : null;
|
|
178
208
|
const cvss = extractNvdCvss(nvdResult.json);
|
|
179
209
|
if (cvss) {
|
|
180
210
|
fetched.cvss_score = cvss.score;
|
|
@@ -224,6 +254,21 @@ async function validateCve(cveId, localEntry) {
|
|
|
224
254
|
return { cve_id: cveId, status: 'missing', discrepancies, fetched, local, drift };
|
|
225
255
|
}
|
|
226
256
|
|
|
257
|
+
// --- Rejected / disputed short-circuit ---
|
|
258
|
+
// A MITRE/NVD-REJECTED record still returns totalResults:1, so without this
|
|
259
|
+
// a rejected CVE would fall through and be CVSS-drift-checked as if valid.
|
|
260
|
+
// Surface it as its own status — this is the signal that catches a cited
|
|
261
|
+
// CVE that was withdrawn after assignment (the class an agent otherwise has
|
|
262
|
+
// to confirm by hand against NVD).
|
|
263
|
+
if (cveFoundInNvd && /^rejected$/i.test(fetched.nvd_vuln_status || '')) {
|
|
264
|
+
return { cve_id: cveId, status: 'rejected', discrepancies, fetched, local, drift };
|
|
265
|
+
}
|
|
266
|
+
const disputed = (fetched.cve_tags || []).some(t => /disputed/i.test(t)) ||
|
|
267
|
+
/disputed/i.test(fetched.nvd_vuln_status || '');
|
|
268
|
+
if (cveFoundInNvd && disputed) {
|
|
269
|
+
pushDiscrepancy(discrepancies, 'cve_status', local?.status ?? null, 'disputed', 'high');
|
|
270
|
+
}
|
|
271
|
+
|
|
227
272
|
// --- Compare CVSS (only if NVD reachable & has data) ---
|
|
228
273
|
if (cveFoundInNvd && fetched.cvss_score !== null && local.cvss_score !== null) {
|
|
229
274
|
if (Math.abs(fetched.cvss_score - local.cvss_score) > 0.05) {
|
|
@@ -274,4 +319,4 @@ async function validateCve(cveId, localEntry) {
|
|
|
274
319
|
return { cve_id: cveId, status, discrepancies, fetched, local, drift };
|
|
275
320
|
}
|
|
276
321
|
|
|
277
|
-
module.exports = { validateCve, getKevCache, resetKevCache };
|
|
322
|
+
module.exports = { validateCve, getKevCache, resetKevCache, extractNvdStatus };
|