@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/sbom.cdx.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:e3ca9b8d-0dcb-458e-9513-53419884794d",
4
+ "serialNumber": "urn:uuid:0af1688b-ffe0-4b0b-b177-9598a354bd03",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2147-02-08T17:02:05.000Z",
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.0"
12
+ "version": "0.14.2"
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.14.0",
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.0",
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 / 7476 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
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.0",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.14.2",
29
29
  "hashes": [
30
30
  {
31
31
  "alg": "SHA-256",
32
- "content": "fadc220cc3f43c70787648fe3eeb50937544d6b05a78ecfa01044c58274f7f8f"
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.0"
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": "f8127c93b31da00f68c6a16ca376667621ac9ae98c815c51c594850080f79e93"
89
+ "content": "6327718cdd949622fbd4af0846fab9a0a5a8f501e173f014534fe8ff4ba22110"
90
90
  },
91
91
  {
92
92
  "alg": "SHA3-512",
93
- "content": "2c891b4c8b1acaf7162559b8be555e24059f94389d522c6fa47ad9d9f65bdcec2ca8f5d92125d94fc04140d9c42fb08728446e83e869d7cef111e50cfed6081d"
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": "273a8c5d2376e0be3a9b02544054606ffd34a4f280f8acb45645cf1ffccfbf6c"
119
+ "content": "8ad69ce7f1057bfa600caa1cca31bd4332a75b6c403ab3087c923dec6c4f756c"
120
120
  },
121
121
  {
122
122
  "alg": "SHA3-512",
123
- "content": "f063967f3a70ab0d8e002711a662138d782f23f62382d60405e67b80e5d0aedebc9848fef823858862336fdee735b373e513451007f285a281657be9a831e2ff"
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": "558e6bdf1bd8a2544ec284364bd3a3624d76f283adfc44239ebfb7112b3f0616"
179
+ "content": "ae39088caf4e044bf0449b97c99acfe76ad30b8a9d8e008b438f1cbda0dc16d5"
180
180
  },
181
181
  {
182
182
  "alg": "SHA3-512",
183
- "content": "998b0057136a850b704ada958051b4a1c103a4d8906e368c9798ec3939316975c00ba76522793a3a96a605860a56e6e7980de15714453f51f205b0719f654a40"
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": "8035064e2c044d92b4bc08206e36519cf7e9041b39dbd09b2807ea33c8852b95"
284
+ "content": "5c10d15f2560ef31ffce7904f7d3ad5cd6f0377b3353971f5f3f2834d9090dd3"
285
285
  },
286
286
  {
287
287
  "alg": "SHA3-512",
288
- "content": "2e0b5f43d9882e3c0f2c759e0aea8c192071cdd7387bd939abb6a366c2d7d44342743539e11bf195059de4f9b012f4f558fcb3e17e23ca48e6ea02358b99228d"
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": "d639f607ee338698f96684c7e432d6f3037031c4817f8077ea2497e79a099dac"
479
+ "content": "69b25f892979583d886409379562b9a1a97f27f1dc0094480155b8086fec6798"
480
480
  },
481
481
  {
482
482
  "alg": "SHA3-512",
483
- "content": "ef7b5111aa07d3386f5fa7d71c6434d06667c707c6a0143097de80825ab81db7cc1fb03b3439bbfcbefb2d5c553ed7ec317cf23b02cb20490f5f6cf26598d9ac"
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": "66ef2e1f444a2cf0c2700a754f0a66030bb8a91d9e68394b9537ea1fe8b904fe"
794
+ "content": "b21d03b948c41bc8a854e2f057948ecf844bd8c105848aeb141d1eadf8192c31"
795
795
  },
796
796
  {
797
797
  "alg": "SHA3-512",
798
- "content": "6b072c7219f10628e5cb001fa49f18b49e2a2911637d510121489816ade358a265bd396b1b23d317f26d6cb8c4ea9113cbcc38ad4549e8c226da980715858807"
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": "8968e632e4e77d7e13bce63579cebae448f2fe5aacb11984203891771bcc75f7"
944
+ "content": "5576bf3ca7f4f2c69f22c93303d808c3f302bf53b62d46cbb7ec23f5f72f9f8b"
930
945
  },
931
946
  {
932
947
  "alg": "SHA3-512",
933
- "content": "575dd30aab3a6568d9b4f351df493b64bf740edf6c99e3db0003685f6851e286ac7c6b2bb3d7d94ad05a562d690c2c7a951c7176fbe586a518ecad65fe6f1e91"
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": "37998843b3fce6b56fd7f18124302ee21335e43174aa445e307c578b0cfcf8ea"
1229
+ "content": "65257992ec15d25efa25667bd605166159b363719be86ddf4e99f53ce41c54e6"
1200
1230
  },
1201
1231
  {
1202
1232
  "alg": "SHA3-512",
1203
- "content": "6953bae9370604540ecefb1434d622524e57083ec8afba76cf431f8b41f5a48ee948208667bc2677fcebb301440df74029fa3e075f366ecb915567de25079282"
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": "bb1ad0a64b9e05c39d4bc3bd31615938f77510fd61a0c48b735386043a9ace22"
1394
+ "content": "8ed8c965f9f1725313333abbc032b0d85a03653cc979781948cc770b88c11659"
1350
1395
  },
1351
1396
  {
1352
1397
  "alg": "SHA3-512",
1353
- "content": "78a5e6c3b66098f70f5b8801ec50f35d2b238f96c79111ced74ba210d1779bc703f342c2b17f76a0b43c2b385d0f6ae6475c9e51e44ac019f903570339285b03"
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": "b2d732410344770094eaa11d0918d434e6123caafa468099fff025940ee91531"
1499
+ "content": "06dc58abb5ed96d7e85c625e5fca1a49ccb23451771104de8ea1fbb097a484e1"
1455
1500
  },
1456
1501
  {
1457
1502
  "alg": "SHA3-512",
1458
- "content": "4f3f3f4e3fa302a60a8cd8e6a5ee43c7c615f8ada41da92ce219e280a4d0e370903c80cc621d707232f8fe541c9d70943ff3028c24d0dbba01a05003f806b268"
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": "1e26aaae86262153a0ffca56cde65a8ddab6cb1c9061ca318fe542855c1dc0d8"
1514
+ "content": "73a41241f2b425aebada9ea641590cb8d596390f93f9e87d07c35f3cb1b1421c"
1470
1515
  },
1471
1516
  {
1472
1517
  "alg": "SHA3-512",
1473
- "content": "a27f70e507c9eef93d749ea999a52fc5202490893c4ad04d24188c908a7e1e3773c1964430db93ef69f2013b59054e9987352a2f5493dcb6c648cf2ae6dd5a84"
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": "5de9a1f65caca9bbc38fb669bc571de4f9d0855b6cd316044792411d1f30399c"
1619
+ "content": "fa34eff1166720bc4f2f72bb6b1cd2d98c1e803fd7e5b192631ae0f2fcba48dd"
1575
1620
  },
1576
1621
  {
1577
1622
  "alg": "SHA3-512",
1578
- "content": "30446655bdc888b3313153f565856f8e0dd70916d0c48971565e9ffead975efe779fa08c583e44637a0a359f17b05a8dbd5fca67654edf49f9ef65cc6106f514"
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": "b6388ed9b3b0e87f6f65d050c2aef4fb3121e2135892e59313ba05b6fa5090e7"
1754
+ "content": "e3c6dc62608d025f8f60ae232bdd54da7433eb8c1d29bdef81deee51122dae4c"
1710
1755
  },
1711
1756
  {
1712
1757
  "alg": "SHA3-512",
1713
- "content": "11b015db4bf77151a7236bc4c6651cb8ab65f519cb0252df9fbdfd0e2a5d413bf5e09f7d712016f23dde92076ea952fd4d53a091d4feee71ce72b68079de3558"
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": "4c2d6ba4c75dea4c92409908db8ffcd357b7208f765c532a3ac976a8cf785acf"
2429
+ "content": "f0186de029d872b8523eb8a47e1d1162c5eddc6ed45e62e010a0b4a82ec67c26"
2385
2430
  },
2386
2431
  {
2387
2432
  "alg": "SHA3-512",
2388
- "content": "29d02c2280fde260d95d215ef68ca4f668906c31d240a174195141b7568bd17a203af50c25d8f0a861d76247ee13984d0843ccab5b5e75f52b6ad77e69f06521"
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": "85543a2f2305c95d4d881769885b12c45107ea61e60f6bd2fd26c6a5bd068bf7"
3149
+ "content": "eadeff28bc343c7f7c270a6bc1a7593449cd1d238e49e4e3f74817d07b91de9a"
3105
3150
  },
3106
3151
  {
3107
3152
  "alg": "SHA3-512",
3108
- "content": "726a0c0ef9da2fa7dd9b5de8bed5c12926fa1f7bd9b3ccee0a275014b3f2f056db82d41b8c3e53f65fa82def8f9a7d9a90fa9985ceb3446a1b6c253a4db53d4c"
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
- for (const e of entries) {
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 };