blitzstrike 2.4.111 → 2.4.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to Blitz Strike are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [2.4.112] — 2026-09-17
9
+
10
+ ### Added — nuclei-templates CVE corpus (watchlist 35 → 4,100+ fingerprints)
11
+
12
+ - New `intelligence/cve-corpus.json` (4,113 CVEs) derived from the canonical
13
+ `projectdiscovery/nuclei-templates` `http/cves/**` tree via
14
+ `scripts/sync-cve-corpus.py` (re-runnable after `nuclei -update-templates`).
15
+ Each entry carries product name + severity + EPSS + CWE + a word-boundary
16
+ `match` regex built from product-name tokens + fofa/shodan query keywords
17
+ (NOT matcher payload words, which are too generic).
18
+ - `watchlistCveMatches` / `live_recon` now match against the hand-curated
19
+ watchlist (version-precise) AND the corpus (long-tail), ranked by EPSS then
20
+ severity, capped at 20, with the hand-curated entry overriding the corpus for
21
+ the same CVE. Generic infra tokens (php/apache/mysql/powered/…) are excluded
22
+ so a plain PHP/HTML target yields ZERO false matches — regression-tested
23
+ (plain php → 0; vite/gradio/ollama/n8n → their specific CVEs).
24
+
8
25
  ## [2.4.111] — 2026-09-17
9
26
 
10
27
  ### Added — CVE watchlist expanded 18 → 35 (2025-2026 fingerprints)
package/dist/index.js CHANGED
@@ -36376,6 +36376,27 @@ function loadWatchlist() {
36376
36376
  const data = JSON.parse(raw);
36377
36377
  return { cwe_frequency: data.cwe_frequency ?? [], cve_watchlist: data.cve_watchlist ?? [] };
36378
36378
  }
36379
+ function loadCorpus() {
36380
+ try {
36381
+ const raw = readFileSync11(join12(ROOT4, "intelligence", "cve-corpus.json"), "utf8");
36382
+ const data = JSON.parse(raw);
36383
+ return (data.cves ?? []).map((c) => ({
36384
+ cve: String(c.cve ?? ""),
36385
+ name: String(c.name ?? ""),
36386
+ product: String(c.product ?? ""),
36387
+ class: "",
36388
+ epss: typeof c.epss === "number" ? c.epss : parseFloat(String(c.epss ?? "0")) || 0,
36389
+ reports: 0,
36390
+ fingerprint: String(c.product ?? ""),
36391
+ match: typeof c.match === "string" ? c.match : undefined,
36392
+ fixed_in: null,
36393
+ version_key: null,
36394
+ severity: typeof c.severity === "string" ? c.severity : undefined
36395
+ }));
36396
+ } catch {
36397
+ return [];
36398
+ }
36399
+ }
36379
36400
  function fmtPayout(min, max) {
36380
36401
  if (min == null || max == null)
36381
36402
  return "n/a";
@@ -36417,31 +36438,42 @@ function semverLt(a, b) {
36417
36438
  }
36418
36439
  return false;
36419
36440
  }
36420
- function watchlistCveMatches(techs, headers = {}, body = "", versions = []) {
36441
+ function watchlistCveMatches(techs, headers = {}, body = "", versions = [], max = 20) {
36421
36442
  const { cve_watchlist } = loadWatchlist();
36443
+ const corpus = loadCorpus();
36422
36444
  const haystack = `${techs.join(" ")} ${Object.keys(headers).join(" ")} ${Object.values(headers).join(" ")} ${body}`.toLowerCase();
36423
- return cve_watchlist.filter((c) => {
36445
+ const curated = new Map(cve_watchlist.map((c) => [c.cve, c]));
36446
+ const merged = [...corpus.filter((c) => !curated.has(c.cve)), ...cve_watchlist];
36447
+ const matched = merged.filter((c) => {
36424
36448
  const m = c.match;
36425
36449
  if (!m)
36426
36450
  return false;
36427
- let matched = false;
36451
+ let ok = false;
36428
36452
  try {
36429
- matched = new RegExp(m, "i").test(haystack);
36453
+ ok = new RegExp(m, "i").test(haystack);
36430
36454
  } catch {
36431
- matched = false;
36455
+ ok = false;
36432
36456
  }
36433
- if (!matched)
36457
+ if (!ok)
36434
36458
  return false;
36435
36459
  const fixedIn = c.fixed_in;
36436
36460
  const vkey = c.version_key;
36437
36461
  if (fixedIn && vkey) {
36438
36462
  const v = versions.find((x) => x.product.toLowerCase().includes(vkey.toLowerCase()));
36439
- if (v?.version && !semverLt(v.version, fixedIn)) {
36463
+ if (v?.version && !semverLt(v.version, fixedIn))
36440
36464
  return false;
36441
- }
36442
36465
  }
36443
36466
  return true;
36444
36467
  });
36468
+ const sevRank = { critical: 0, high: 1, medium: 2, low: 3 };
36469
+ matched.sort((a, b) => {
36470
+ const ea = a.epss ?? 0;
36471
+ const eb = b.epss ?? 0;
36472
+ if (ea !== eb)
36473
+ return eb - ea;
36474
+ return (sevRank[a.severity ?? ""] ?? 9) - (sevRank[b.severity ?? ""] ?? 9);
36475
+ });
36476
+ return matched.slice(0, max);
36445
36477
  }
36446
36478
 
36447
36479
  // src/surface-map.ts
@@ -59801,7 +59833,7 @@ function loadDir(dir) {
59801
59833
  } catch {}
59802
59834
  return entries;
59803
59835
  }
59804
- function loadCorpus() {
59836
+ function loadCorpus2() {
59805
59837
  return [...loadDir(CORPUS), ...loadDir(CAPTURED_CORPUS_DIR)];
59806
59838
  }
59807
59839
  function captureFalsePositive(input) {
@@ -59858,7 +59890,7 @@ function detect(entry) {
59858
59890
  return { detected: false };
59859
59891
  }
59860
59892
  function runBenchmark() {
59861
- const entries = loadCorpus();
59893
+ const entries = loadCorpus2();
59862
59894
  const results = entries.map((entry) => {
59863
59895
  const { detected, sinkKind } = detect(entry);
59864
59896
  const outcome = entry.vulnerable ? detected ? "tp" : "fn" : detected ? "fp" : "tn";