@blamejs/exceptd-skills 0.16.22 → 0.16.24

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.
Files changed (62) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/CHANGELOG.md +42 -0
  3. package/CONTEXT.md +9 -9
  4. package/README.md +3 -3
  5. package/agents/report-generator.md +2 -2
  6. package/agents/skill-updater.md +1 -1
  7. package/agents/source-validator.md +3 -4
  8. package/agents/threat-researcher.md +1 -1
  9. package/bin/exceptd.js +91 -32
  10. package/data/_indexes/_meta.json +10 -10
  11. package/data/_indexes/activity-feed.json +12 -12
  12. package/data/_indexes/chains.json +70435 -4026
  13. package/data/_indexes/frequency.json +492 -163
  14. package/data/_indexes/section-offsets.json +51 -51
  15. package/data/_indexes/summary-cards.json +272 -106
  16. package/data/_indexes/token-budget.json +10 -10
  17. package/data/_indexes/trigger-table.json +15 -6
  18. package/data/_indexes/xref.json +218 -26
  19. package/data/cve-catalog.json +10 -10
  20. package/data/cwe-catalog.json +1 -0
  21. package/lib/auto-discovery.js +39 -1
  22. package/lib/collectors/ai-api.js +112 -7
  23. package/lib/collectors/citation-hygiene.js +27 -0
  24. package/lib/collectors/crypto-codebase.js +25 -0
  25. package/lib/collectors/kernel.js +32 -2
  26. package/lib/collectors/library-author.js +30 -0
  27. package/lib/collectors/runtime.js +38 -3
  28. package/lib/collectors/sbom.js +21 -2
  29. package/lib/collectors/scan-excludes.js +4 -1
  30. package/lib/collectors/secrets.js +125 -0
  31. package/lib/cve-cli.js +9 -1
  32. package/lib/cve-curation.js +8 -1
  33. package/lib/cve-regression-watcher.js +5 -2
  34. package/lib/exit-codes.js +2 -0
  35. package/lib/flag-suggest.js +1 -1
  36. package/lib/lint-skills.js +70 -0
  37. package/lib/playbook-runner.js +75 -14
  38. package/lib/prefetch.js +24 -1
  39. package/lib/refresh-external.js +32 -3
  40. package/lib/rfc-cli.js +8 -1
  41. package/lib/scoring.js +36 -8
  42. package/lib/validate-cve-catalog.js +36 -14
  43. package/lib/validate-package.js +8 -0
  44. package/lib/validate-playbooks.js +42 -0
  45. package/lib/verify.js +4 -3
  46. package/manifest-snapshot.json +4 -2
  47. package/manifest-snapshot.sha256 +1 -1
  48. package/manifest.json +57 -54
  49. package/orchestrator/README.md +1 -1
  50. package/orchestrator/index.js +65 -7
  51. package/orchestrator/scanner.js +53 -5
  52. package/package.json +1 -1
  53. package/sbom.cdx.json +110 -110
  54. package/scripts/build-indexes.js +42 -8
  55. package/scripts/builders/cwe-chains.js +1 -0
  56. package/scripts/builders/section-offsets.js +10 -2
  57. package/scripts/builders/token-budget.js +3 -3
  58. package/scripts/check-changelog-extract.js +38 -1
  59. package/scripts/check-sbom-currency.js +72 -0
  60. package/scripts/check-version-tags.js +5 -0
  61. package/scripts/release.js +22 -15
  62. package/skills/exploit-scoring/skill.md +8 -8
@@ -3,8 +3,8 @@
3
3
  "schema_version": "1.0.0",
4
4
  "tokenizer_note": "Character-density approximation: 1 token ≈ 4 chars. This is the canonical rule-of-thumb for OpenAI tokenizers on English+technical text. Claude's tokenizer is typically more efficient on prose; treat this as an upper-bound budget for both. Consumers with stricter precision needs should re-tokenize with their own tokenizer.",
5
5
  "approx_chars_per_token": 4,
6
- "total_chars": 1743803,
7
- "total_approx_tokens": 435954,
6
+ "total_chars": 1743806,
7
+ "total_approx_tokens": 435955,
8
8
  "skill_count": 51
9
9
  },
10
10
  "skills": {
@@ -300,10 +300,10 @@
300
300
  },
301
301
  "exploit-scoring": {
302
302
  "path": "skills/exploit-scoring/skill.md",
303
- "bytes": 30227,
304
- "chars": 30043,
303
+ "bytes": 30230,
304
+ "chars": 30046,
305
305
  "lines": 391,
306
- "approx_tokens": 7511,
306
+ "approx_tokens": 7512,
307
307
  "approx_chars_per_token": 4,
308
308
  "sections": {
309
309
  "frontmatter-scope": {
@@ -332,8 +332,8 @@
332
332
  "approx_tokens": 435
333
333
  },
334
334
  "rwep-formula": {
335
- "bytes": 2866,
336
- "chars": 2856,
335
+ "bytes": 2867,
336
+ "chars": 2857,
337
337
  "approx_tokens": 714
338
338
  },
339
339
  "pre-calculated-rwep-scores": {
@@ -347,9 +347,9 @@
347
347
  "approx_tokens": 373
348
348
  },
349
349
  "analysis-procedure": {
350
- "bytes": 1691,
351
- "chars": 1665,
352
- "approx_tokens": 416
350
+ "bytes": 1693,
351
+ "chars": 1667,
352
+ "approx_tokens": 417
353
353
  },
354
354
  "output-format": {
355
355
  "bytes": 1645,
@@ -11,12 +11,18 @@
11
11
  "dirty frag": [
12
12
  "kernel-lpe-triage"
13
13
  ],
14
+ "fragnesia": [
15
+ "kernel-lpe-triage"
16
+ ],
14
17
  "cve-2026-31431": [
15
18
  "kernel-lpe-triage"
16
19
  ],
17
20
  "cve-2026-43284": [
18
21
  "kernel-lpe-triage"
19
22
  ],
23
+ "cve-2026-46300": [
24
+ "kernel-lpe-triage"
25
+ ],
20
26
  "linux root": [
21
27
  "kernel-lpe-triage"
22
28
  ],
@@ -1459,9 +1465,18 @@
1459
1465
  "age assurance": [
1460
1466
  "age-gates-child-safety"
1461
1467
  ],
1468
+ "child safety": [
1469
+ "age-gates-child-safety"
1470
+ ],
1462
1471
  "child online safety": [
1463
1472
  "age-gates-child-safety"
1464
1473
  ],
1474
+ "children's online safety": [
1475
+ "age-gates-child-safety"
1476
+ ],
1477
+ "youth safety": [
1478
+ "age-gates-child-safety"
1479
+ ],
1465
1480
  "coppa": [
1466
1481
  "age-gates-child-safety"
1467
1482
  ],
@@ -1492,18 +1507,12 @@
1492
1507
  "csam": [
1493
1508
  "age-gates-child-safety"
1494
1509
  ],
1495
- "child safety": [
1496
- "age-gates-child-safety"
1497
- ],
1498
1510
  "ofcom": [
1499
1511
  "age-gates-child-safety"
1500
1512
  ],
1501
1513
  "esafety": [
1502
1514
  "age-gates-child-safety"
1503
1515
  ],
1504
- "children's online safety": [
1505
- "age-gates-child-safety"
1506
- ],
1507
1516
  "cloud iam compromise": [
1508
1517
  "cloud-iam-incident"
1509
1518
  ],
@@ -79,6 +79,7 @@
79
79
  "webapp-security"
80
80
  ],
81
81
  "CWE-918": [
82
+ "ai-c2-detection",
82
83
  "api-security",
83
84
  "attack-surface-pentest",
84
85
  "log-injection-telemetry",
@@ -292,73 +293,142 @@
292
293
  "kernel-lpe-triage"
293
294
  ],
294
295
  "D3-EAL": [
296
+ "ai-attack-surface",
295
297
  "attack-surface-pentest",
298
+ "cloud-security",
299
+ "container-runtime-security",
296
300
  "defensive-countermeasure-mapping",
297
301
  "dlp-gap-analysis",
298
302
  "fuzz-testing-strategy",
299
303
  "kernel-lpe-triage",
300
304
  "mcp-agent-trust",
301
- "supply-chain-integrity"
305
+ "mlops-security",
306
+ "sector-energy",
307
+ "sector-federal-government",
308
+ "supply-chain-integrity",
309
+ "webapp-security"
310
+ ],
311
+ "D3-PA": [
312
+ "defensive-countermeasure-mapping",
313
+ "kernel-lpe-triage"
302
314
  ],
303
315
  "D3-PHRA": [
304
316
  "defensive-countermeasure-mapping",
305
317
  "kernel-lpe-triage"
306
318
  ],
307
319
  "D3-PSEP": [
320
+ "container-runtime-security",
308
321
  "defensive-countermeasure-mapping",
309
322
  "fuzz-testing-strategy",
323
+ "kernel-lpe-triage",
324
+ "sector-energy"
325
+ ],
326
+ "D3-SCP": [
327
+ "defensive-countermeasure-mapping",
310
328
  "kernel-lpe-triage"
311
329
  ],
312
330
  "D3-IOPR": [
331
+ "age-gates-child-safety",
313
332
  "ai-attack-surface",
314
333
  "ai-c2-detection",
334
+ "ai-risk-management",
335
+ "api-security",
315
336
  "cloud-iam-incident",
337
+ "cloud-security",
338
+ "container-runtime-security",
316
339
  "defensive-countermeasure-mapping",
317
340
  "dlp-gap-analysis",
341
+ "email-security-anti-phishing",
318
342
  "fuzz-testing-strategy",
319
343
  "idp-incident-response",
344
+ "incident-response-playbook",
345
+ "mlops-security",
320
346
  "rag-pipeline-security",
321
347
  "ransomware-response",
322
- "sector-telecom"
348
+ "sector-financial",
349
+ "sector-healthcare",
350
+ "sector-telecom",
351
+ "webapp-security"
323
352
  ],
324
353
  "D3-NTA": [
325
354
  "ai-attack-surface",
326
355
  "ai-c2-detection",
356
+ "api-security",
327
357
  "attack-surface-pentest",
328
358
  "cloud-iam-incident",
359
+ "cloud-security",
329
360
  "defensive-countermeasure-mapping",
330
361
  "dlp-gap-analysis",
362
+ "email-security-anti-phishing",
331
363
  "idp-incident-response",
364
+ "incident-response-playbook",
332
365
  "rag-pipeline-security",
333
366
  "ransomware-response",
334
- "sector-telecom"
367
+ "sector-energy",
368
+ "sector-financial",
369
+ "sector-telecom",
370
+ "webapp-security"
335
371
  ],
336
- "D3-CBAN": [
337
- "cloud-iam-incident",
372
+ "D3-FAPA": [
373
+ "ai-attack-surface",
338
374
  "defensive-countermeasure-mapping",
339
- "idp-incident-response",
340
- "mcp-agent-trust",
341
- "supply-chain-integrity"
375
+ "rag-pipeline-security"
342
376
  ],
343
377
  "D3-CSPP": [
378
+ "age-gates-child-safety",
379
+ "ai-attack-surface",
344
380
  "ai-c2-detection",
381
+ "api-security",
345
382
  "attack-surface-pentest",
346
383
  "defensive-countermeasure-mapping",
347
384
  "dlp-gap-analysis",
385
+ "email-security-anti-phishing",
386
+ "identity-assurance",
387
+ "incident-response-playbook",
348
388
  "mcp-agent-trust",
349
389
  "rag-pipeline-security",
350
- "ransomware-response"
390
+ "ransomware-response",
391
+ "sector-healthcare",
392
+ "webapp-security"
393
+ ],
394
+ "D3-CAA": [
395
+ "cloud-iam-incident",
396
+ "mcp-agent-trust"
397
+ ],
398
+ "D3-CBAN": [
399
+ "api-security",
400
+ "cloud-iam-incident",
401
+ "cloud-security",
402
+ "defensive-countermeasure-mapping",
403
+ "idp-incident-response",
404
+ "mcp-agent-trust",
405
+ "sector-federal-government",
406
+ "sector-financial",
407
+ "supply-chain-integrity"
351
408
  ],
352
409
  "D3-EHB": [
410
+ "container-runtime-security",
353
411
  "defensive-countermeasure-mapping",
354
412
  "mcp-agent-trust",
413
+ "mlops-security",
414
+ "sector-federal-government",
355
415
  "supply-chain-integrity"
356
416
  ],
357
417
  "D3-MFA": [
418
+ "age-gates-child-safety",
419
+ "api-security",
358
420
  "cloud-iam-incident",
359
421
  "defensive-countermeasure-mapping",
422
+ "email-security-anti-phishing",
423
+ "identity-assurance",
360
424
  "idp-incident-response",
361
- "mcp-agent-trust"
425
+ "mcp-agent-trust",
426
+ "sector-financial",
427
+ "sector-healthcare",
428
+ "webapp-security"
429
+ ],
430
+ "D3-FCR": [
431
+ "rag-pipeline-security"
362
432
  ],
363
433
  "D3-CA": [
364
434
  "ai-c2-detection",
@@ -370,13 +440,18 @@
370
440
  ],
371
441
  "D3-NI": [
372
442
  "ai-c2-detection",
443
+ "container-runtime-security",
373
444
  "defensive-countermeasure-mapping",
445
+ "sector-energy",
374
446
  "sector-telecom"
375
447
  ],
376
448
  "D3-NTPM": [
377
449
  "ai-c2-detection",
450
+ "cloud-security",
451
+ "container-runtime-security",
378
452
  "defensive-countermeasure-mapping",
379
453
  "dlp-gap-analysis",
454
+ "sector-energy",
380
455
  "sector-telecom"
381
456
  ],
382
457
  "D3-FE": [
@@ -387,21 +462,10 @@
387
462
  "defensive-countermeasure-mapping",
388
463
  "pqc-first"
389
464
  ],
390
- "D3-FAPA": [
391
- "defensive-countermeasure-mapping"
392
- ],
393
- "D3-PA": [
394
- "defensive-countermeasure-mapping"
395
- ],
396
465
  "D3-RPA": [
397
466
  "defensive-countermeasure-mapping",
467
+ "incident-response-playbook",
398
468
  "ransomware-response"
399
- ],
400
- "D3-SCP": [
401
- "defensive-countermeasure-mapping"
402
- ],
403
- "D3-CAA": [
404
- "cloud-iam-incident"
405
469
  ]
406
470
  },
407
471
  "framework_gaps": {
@@ -414,9 +478,12 @@
414
478
  "privacy-consent-ops"
415
479
  ],
416
480
  "ISO-27001-2022-A.8.8": [
481
+ "attack-surface-pentest",
417
482
  "coordinated-vuln-disclosure",
418
483
  "kernel-lpe-triage",
419
- "mail-server-hardening"
484
+ "mail-server-hardening",
485
+ "ot-ics-security",
486
+ "sector-energy"
420
487
  ],
421
488
  "PCI-DSS-4.0-6.3.3": [
422
489
  "kernel-lpe-triage",
@@ -437,6 +504,17 @@
437
504
  "exploit-scoring",
438
505
  "kernel-lpe-triage"
439
506
  ],
507
+ "UK-CAF-D1": [
508
+ "coordinated-vuln-disclosure",
509
+ "incident-response-playbook",
510
+ "kernel-lpe-triage",
511
+ "sector-energy",
512
+ "sector-healthcare"
513
+ ],
514
+ "AU-Essential-8-Patch": [
515
+ "coordinated-vuln-disclosure",
516
+ "kernel-lpe-triage"
517
+ ],
440
518
  "ALL-AI-PIPELINE-INTEGRITY": [
441
519
  "ai-attack-surface",
442
520
  "compliance-theater"
@@ -486,6 +564,42 @@
486
564
  "identity-assurance",
487
565
  "sector-financial"
488
566
  ],
567
+ "EU-AI-Act-Art-15": [
568
+ "ai-attack-surface",
569
+ "ai-risk-management",
570
+ "mcp-agent-trust",
571
+ "mlops-security",
572
+ "rag-pipeline-security"
573
+ ],
574
+ "UK-CAF-A1": [
575
+ "ai-attack-surface",
576
+ "ai-risk-management",
577
+ "attack-surface-pentest",
578
+ "mcp-agent-trust",
579
+ "mlops-security",
580
+ "sector-federal-government",
581
+ "sector-financial",
582
+ "supply-chain-integrity",
583
+ "threat-modeling-methodology"
584
+ ],
585
+ "AU-Essential-8-App-Hardening": [
586
+ "ai-attack-surface",
587
+ "ai-c2-detection",
588
+ "ai-risk-management",
589
+ "api-security",
590
+ "attack-surface-pentest",
591
+ "container-runtime-security",
592
+ "dlp-gap-analysis",
593
+ "email-security-anti-phishing",
594
+ "mcp-agent-trust",
595
+ "mlops-security",
596
+ "ot-ics-security",
597
+ "rag-pipeline-security",
598
+ "sector-federal-government",
599
+ "supply-chain-integrity",
600
+ "threat-modeling-methodology",
601
+ "webapp-security"
602
+ ],
489
603
  "ALL-MCP-TOOL-TRUST": [
490
604
  "mcp-agent-trust"
491
605
  ],
@@ -544,6 +658,17 @@
544
658
  "mlops-security",
545
659
  "rag-pipeline-security"
546
660
  ],
661
+ "UK-CAF-B2": [
662
+ "age-gates-child-safety",
663
+ "api-security",
664
+ "cloud-security",
665
+ "container-runtime-security",
666
+ "identity-assurance",
667
+ "ot-ics-security",
668
+ "rag-pipeline-security",
669
+ "vc-wallet-trust",
670
+ "webapp-security"
671
+ ],
547
672
  "NIST-800-53-SC-7": [
548
673
  "ai-c2-detection",
549
674
  "dlp-gap-analysis"
@@ -561,6 +686,29 @@
561
686
  "email-security-anti-phishing",
562
687
  "incident-response-playbook"
563
688
  ],
689
+ "NIS2-Art21-incident-handling": [
690
+ "age-gates-child-safety",
691
+ "ai-c2-detection",
692
+ "api-security",
693
+ "cloud-security",
694
+ "container-runtime-security",
695
+ "coordinated-vuln-disclosure",
696
+ "dlp-gap-analysis",
697
+ "email-security-anti-phishing",
698
+ "identity-assurance",
699
+ "incident-response-playbook",
700
+ "sector-federal-government",
701
+ "sector-financial",
702
+ "sector-healthcare",
703
+ "supply-chain-integrity",
704
+ "threat-modeling-methodology",
705
+ "webapp-security"
706
+ ],
707
+ "UK-CAF-C1": [
708
+ "ai-c2-detection",
709
+ "dlp-gap-analysis",
710
+ "email-security-anti-phishing"
711
+ ],
564
712
  "NIST-800-53-SC-28": [
565
713
  "dlp-gap-analysis",
566
714
  "pqc-first"
@@ -627,6 +775,12 @@
627
775
  "identity-assurance",
628
776
  "sector-financial"
629
777
  ],
778
+ "AU-Essential-8-MFA": [
779
+ "age-gates-child-safety",
780
+ "cloud-security",
781
+ "identity-assurance",
782
+ "sector-financial"
783
+ ],
630
784
  "NIST-800-82r3": [
631
785
  "ot-ics-security",
632
786
  "sector-energy"
@@ -639,6 +793,11 @@
639
793
  "ot-ics-security",
640
794
  "sector-energy"
641
795
  ],
796
+ "AU-Essential-8-Backup": [
797
+ "incident-response-playbook",
798
+ "sector-energy",
799
+ "sector-healthcare"
800
+ ],
642
801
  "FCC-CPNI-4.1": [
643
802
  "sector-telecom"
644
803
  ],
@@ -740,9 +899,6 @@
740
899
  "OFAC-Sanctions-Threat-Actor-Negotiation": [
741
900
  "idp-incident-response"
742
901
  ],
743
- "UK-CAF-B2": [
744
- "vc-wallet-trust"
745
- ],
746
902
  "NIS2-Art21-network-security": [
747
903
  "audit-log-integrity",
748
904
  "decompression-dos",
@@ -1174,9 +1330,45 @@
1174
1330
  "RFC-9106": [
1175
1331
  "pqc-first"
1176
1332
  ],
1333
+ "ISO-29147": [
1334
+ "coordinated-vuln-disclosure"
1335
+ ],
1336
+ "ISO-30111": [
1337
+ "coordinated-vuln-disclosure"
1338
+ ],
1339
+ "RFC-9116": [
1340
+ "coordinated-vuln-disclosure"
1341
+ ],
1342
+ "CSAF-2.0": [
1343
+ "coordinated-vuln-disclosure"
1344
+ ],
1177
1345
  "RFC-9622": [
1178
1346
  "sector-telecom"
1179
1347
  ],
1348
+ "RFC-6545": [
1349
+ "incident-response-playbook"
1350
+ ],
1351
+ "RFC-6546": [
1352
+ "incident-response-playbook"
1353
+ ],
1354
+ "RFC-7970": [
1355
+ "incident-response-playbook"
1356
+ ],
1357
+ "RFC-7489": [
1358
+ "email-security-anti-phishing"
1359
+ ],
1360
+ "RFC-6376": [
1361
+ "email-security-anti-phishing"
1362
+ ],
1363
+ "RFC-7208": [
1364
+ "email-security-anti-phishing"
1365
+ ],
1366
+ "RFC-8616": [
1367
+ "email-security-anti-phishing"
1368
+ ],
1369
+ "RFC-8461": [
1370
+ "email-security-anti-phishing"
1371
+ ],
1180
1372
  "RFC-8693": [
1181
1373
  "cloud-iam-incident"
1182
1374
  ],
@@ -3010,12 +3010,12 @@
3010
3010
  "attack_refs": [
3011
3011
  "T1592"
3012
3012
  ],
3013
- "rwep_score": 30,
3013
+ "rwep_score": 35,
3014
3014
  "rwep_factors": {
3015
3015
  "cisa_kev": 0,
3016
3016
  "poc_available": 20,
3017
3017
  "ai_factor": 0,
3018
- "active_exploitation": 0,
3018
+ "active_exploitation": 5,
3019
3019
  "blast_radius": 25,
3020
3020
  "patch_available": -15,
3021
3021
  "live_patch_available": 0,
@@ -4983,7 +4983,7 @@
4983
4983
  "attack_refs": [
4984
4984
  "T1068"
4985
4985
  ],
4986
- "rwep_score": 30,
4986
+ "rwep_score": 35,
4987
4987
  "rwep_factors": {
4988
4988
  "cisa_kev": 0,
4989
4989
  "poc_available": 20,
@@ -4992,9 +4992,9 @@
4992
4992
  "blast_radius": 25,
4993
4993
  "patch_available": -15,
4994
4994
  "live_patch_available": 0,
4995
- "reboot_required": 0
4995
+ "reboot_required": 5
4996
4996
  },
4997
- "rwep_notes": "RWEP 30 today (T+3). Score will jump to 50 (+25 KEV) on CISA KEV listing — expected within weeks once exploitation observed. Reboot-required nature adds operator friction not captured in RWEP — practical exposure window is longer than the math suggests because reboot scheduling lags kernel-package availability. blast_radius 25 reflects every Linux host running setuid ssh-keysign or chage (every default OpenSSH + shadow-utils install). Live-patch credit deferred until KernelCare ships.",
4997
+ "rwep_notes": "RWEP 35 today (T+3). Score will jump to 60 (+25 KEV) on CISA KEV listing — expected within weeks once exploitation observed. Reboot-required nature adds operator friction not captured in RWEP — practical exposure window is longer than the math suggests because reboot scheduling lags kernel-package availability. blast_radius 25 reflects every Linux host running setuid ssh-keysign or chage (every default OpenSSH + shadow-utils install). Live-patch credit deferred until KernelCare ships.",
4998
4998
  "cwe_refs": [
4999
4999
  "CWE-672",
5000
5000
  "CWE-362"
@@ -7615,11 +7615,11 @@
7615
7615
  "cisa_kev": 0,
7616
7616
  "poc_available": 20,
7617
7617
  "ai_factor": 0,
7618
- "active_exploitation": 5,
7618
+ "active_exploitation": 0,
7619
7619
  "blast_radius": 25,
7620
7620
  "patch_available": -15,
7621
7621
  "live_patch_available": 0,
7622
- "reboot_required": 0
7622
+ "reboot_required": 5
7623
7623
  },
7624
7624
  "cwe_refs": [
7625
7625
  "CWE-362",
@@ -21079,7 +21079,7 @@
21079
21079
  "attack_refs": [
21080
21080
  "T1203"
21081
21081
  ],
21082
- "rwep_score": 70,
21082
+ "rwep_score": 65,
21083
21083
  "rwep_factors": {
21084
21084
  "cisa_kev": 25,
21085
21085
  "poc_available": 20,
@@ -21088,9 +21088,9 @@
21088
21088
  "blast_radius": 15,
21089
21089
  "patch_available": -15,
21090
21090
  "live_patch_available": 0,
21091
- "reboot_required": 5
21091
+ "reboot_required": 0
21092
21092
  },
21093
- "rwep_notes": "P1 — KEV-listed legacy client-side RCE; blast_radius=15 (legacy-constrained). Draft (KEV-gap-fill).",
21093
+ "rwep_notes": "P1 — KEV-listed legacy client-side RCE; blast_radius=15 (legacy-constrained). Client-application patch (APSB09-15) does not require a reboot. Draft (KEV-gap-fill).",
21094
21094
  "epss_score": null,
21095
21095
  "epss_date": "2026-05-25",
21096
21096
  "epss_note": "EPSS not pulled for this KEV-gap-fill draft.",
@@ -1913,6 +1913,7 @@
1913
1913
  "CAPEC-664"
1914
1914
  ],
1915
1915
  "skills_referencing": [
1916
+ "ai-c2-detection",
1916
1917
  "api-security",
1917
1918
  "attack-surface-pentest",
1918
1919
  "log-injection-telemetry",
@@ -29,6 +29,7 @@
29
29
 
30
30
  const fs = require("fs");
31
31
  const path = require("path");
32
+ const crypto = require("crypto");
32
33
  const { scoreCustom, RWEP_WEIGHTS, ACTIVE_EXPLOITATION_LADDER } = require("./scoring");
33
34
 
34
35
  // Stored rwep_factors must reproduce the stored rwep_score.
@@ -138,12 +139,49 @@ const RFC_STATUS_MAP = {
138
139
  unkn: "Unknown",
139
140
  };
140
141
 
142
+ // Read a per-source cache payload and verify its integrity against the
143
+ // signed _index.json before returning it. Values from these files flow into
144
+ // auto-imported catalog drafts (CVSS / CWE / EPSS), so an unverified read
145
+ // would let an attacker who dropped a forged payload between prefetch and
146
+ // refresh inject false mechanical fields. Discovery cannot throw (it returns
147
+ // drafts, not errors), so the fail-closed action is to return null — the
148
+ // payload is treated as absent and the draft keeps its null mechanical
149
+ // fields, the same fallback used when no cache file exists.
150
+ //
151
+ // Integrity policy, mirroring the hardened drift reader in
152
+ // refresh-external.js but never throwing:
153
+ // - _index.json present, has a per-entry sha256, payload matches -> return.
154
+ // - _index.json present, has a per-entry sha256, payload MISMATCHES ->
155
+ // return null. This is the core defense: a freshly-DISCOVERED CVE's
156
+ // NVD/EPSS sidecar dropped into a cache that already carries a valid
157
+ // signed index has no matching entry, and a tampered legit entry no
158
+ // longer hashes — both refuse to populate.
159
+ // - _index.json present but no entry for this source/id -> return null
160
+ // (the inject-alongside-a-signed-cache vector).
161
+ // - _index.json absent entirely -> unverified read. The --from-cache
162
+ // signature gate (loadCtx) already refuses an unsigned cache before
163
+ // consume unless --force-stale, so an index-less cache only reaches here
164
+ // under that explicit operator override or a direct library caller.
141
165
  function readCachedJson(cacheDir, source, id) {
142
166
  if (!cacheDir) return null;
143
167
  const safe = String(id).replace(/[^A-Za-z0-9._-]/g, "_");
144
168
  const p = path.join(cacheDir, source, `${safe}.json`);
145
169
  if (!fs.existsSync(p)) return null;
146
- try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
170
+ let parsed;
171
+ try { parsed = JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
172
+
173
+ const indexPath = path.join(cacheDir, "_index.json");
174
+ if (!fs.existsSync(indexPath)) return parsed;
175
+ let idx;
176
+ try { idx = JSON.parse(fs.readFileSync(indexPath, "utf8")); } catch { return null; }
177
+ const meta = idx && idx.entries && idx.entries[`${source}/${id}`];
178
+ if (!meta || typeof meta.sha256 !== "string") return null;
179
+ // The sha256 recorded at prefetch time is computed over JSON.stringify of
180
+ // the parsed payload (unindented), so round-trip + re-stringify to compute
181
+ // the comparable hash.
182
+ const actual = crypto.createHash("sha256").update(JSON.stringify(parsed)).digest("hex");
183
+ if (actual !== meta.sha256) return null;
184
+ return parsed;
147
185
  }
148
186
 
149
187
  function extractNvdMetrics(payload) {