@blamejs/exceptd-skills 0.21.3 → 0.21.5

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_meta": {
3
3
  "schema_version": "1.1.0",
4
- "last_updated": "2026-09-24",
4
+ "last_updated": "2026-09-27",
5
5
  "last_threat_review": "2026-05-17",
6
6
  "purpose": "Zero-day learning loop output. Each entry maps a CVE to: attack vector, defense chain analysis, framework coverage, new control requirements generated, and exposure scoring. v1.1.0 (2026-05-15): every entry now carries ai_discovered_zeroday boolean + ai_discovery_source enum + ai_discovery_date + ai_assist_factor ladder, per AGENTS.md Hard Rule #7.",
7
7
  "note": "Never delete entries. Closed gaps are marked status: closed. History is data.",
@@ -17,7 +17,7 @@
17
17
  "rebuild_after_days": 365,
18
18
  "note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
19
19
  },
20
- "entry_count": 1752
20
+ "entry_count": 1754
21
21
  },
22
22
  "CVE-2026-31431": {
23
23
  "name": "Copy Fail",
@@ -145297,5 +145297,165 @@
145297
145297
  ]
145298
145298
  }
145299
145299
  ]
145300
+ },
145301
+ "CVE-2021-20021": {
145302
+ "name": "SonicWall Email Security Improper Privilege Management Vulnerability",
145303
+ "lesson_date": "2026-09-26",
145304
+ "ai_discovered_zeroday": false,
145305
+ "ai_discovery_source": "human_researcher",
145306
+ "ai_discovery_date": null,
145307
+ "ai_assist_factor": "none",
145308
+ "attack_vector": {
145309
+ "description": "An unauthenticated attacker sends a GET or POST to the /createou endpoint with an XML document, which Email Security processes without checking authentication and which creates a role.ouadmin administrator account; the attacker then logs in as that account and administers the product.",
145310
+ "privileges_required": "none; the /createou endpoint accepts an unauthenticated network request",
145311
+ "complexity": "low",
145312
+ "ai_factor": "None. Credited to human researchers at Mandiant; no AI tooling is claimed."
145313
+ },
145314
+ "defense_chain": {
145315
+ "prevention": {
145316
+ "what_would_have_worked": "Installing Hotfix 10.0.9.6103 (Windows software) or 10.0.9.6105 (hardware and virtual appliance), which requires authentication on the administrative endpoint, then 10.0.9.6173 or 10.0.9.6177 to close the exploited chain, and not exposing the Email Security administration interface to the internet.",
145317
+ "was_this_required": true,
145318
+ "framework_requiring_it": "AU-ISM-1546",
145319
+ "adequacy": "Hotfix 10.0.9.6103 or 10.0.9.6105 closes this flaw, but the exploited chain with CVE-2021-20022 and CVE-2021-20023 stays open until 10.0.9.6173 or 10.0.9.6177 is installed. The operator could not add the missing check to the vendor's endpoint and had to wait for the vendor build, and the end-of-life 7.0.0 through 9.2.2 releases receive no fix."
145320
+ },
145321
+ "detection": {
145322
+ "what_would_have_worked": "Alerting on unauthenticated requests to /createou and on the creation of an administrator account that no operator initiated, and reviewing multi_accounts.xml and the perhost, perldap and perou account files for accounts no operator created.",
145323
+ "was_this_required": false,
145324
+ "framework_requiring_it": "NIST-800-53-AC-3",
145325
+ "adequacy": "The request and account-creation signals are reliable, but they fire after the attacker already holds an administrator account."
145326
+ },
145327
+ "response": {
145328
+ "what_would_have_worked": "Removing unexpected administrator accounts, rotating credentials, checking for web shells planted through the chained flaws, and installing Hotfix 10.0.9.6103 or 10.0.9.6105 and then 10.0.9.6173 or 10.0.9.6177, which closes the chain.",
145329
+ "was_this_required": false,
145330
+ "framework_requiring_it": "NIS2-Art21-network-security",
145331
+ "adequacy": "Patching closes the endpoint, but because the flaw led an exploit chain to code execution, the instance must be examined and, where compromised, rebuilt."
145332
+ }
145333
+ },
145334
+ "framework_coverage": {
145335
+ "NIST-800-53-AC-3": {
145336
+ "covered": true,
145337
+ "adequate": false,
145338
+ "gap": "The vendor's /createou form created an administrator account without prior authentication, and the organization could not apply its own access enforcement inside that form."
145339
+ },
145340
+ "NIS2-Art21-network-security": {
145341
+ "covered": true,
145342
+ "adequate": false,
145343
+ "gap": "For entities within Implementing Regulation 2024/2690, points 6.7.2 and 6.8.2 would have kept the administration interface off the internet; for other entities, Article 21(2)(e) sets no timeframe and names no interim measure for the exploitation that began in March 2021, before the April 9, 2021 hotfix."
145344
+ },
145345
+ "UK-CAF-B2": {
145346
+ "covered": true,
145347
+ "adequate": false,
145348
+ "gap": "The request that created the administrator account was never individually authenticated, and a B2 privileged-account review finds the account only after it has been used."
145349
+ },
145350
+ "AU-ISM-1546": {
145351
+ "covered": true,
145352
+ "adequate": false,
145353
+ "gap": "The /createou form accepted unauthenticated requests on the latest 10.0.9 build then available, so the configured authentication did not cover it until the April 9, 2021 hotfix."
145354
+ },
145355
+ "ISO-27001-2022-A.5.15": {
145356
+ "covered": true,
145357
+ "adequate": false,
145358
+ "gap": "Access rules for the administration panel were in place, yet an unauthenticated request created a role.ouadmin account outside them."
145359
+ }
145360
+ },
145361
+ "new_control_requirements": [
145362
+ {
145363
+ "id": "NEW-CTRL-134",
145364
+ "name": "DEVICE-MANAGEMENT-GATEWAY-CONFIG-ENDPOINT-AUTH-INPUT-NEUTRALIZATION",
145365
+ "description": "A management or configuration endpoint on a network-edge appliance must authenticate the caller and neutralize supplied input before it can invoke a privileged internal function, on every request path rather than on the primary one.",
145366
+ "evidence": "SonicWall Email Security builds before 10.0.9.6103 (Windows software and Hosted Email Security) and 10.0.9.6105 (appliances) exposed an administrative endpoint, /createou, that created a role.ouadmin account without verifying prior authentication (SonicWall advisory SNWLID-2021-0007), which Mandiant observed exploited in the wild; authenticating that configuration endpoint on every request path before it acts closes the bypass.",
145367
+ "gap_closes": [
145368
+ "NIST-800-53-AC-3",
145369
+ "UK-CAF-B2"
145370
+ ]
145371
+ },
145372
+ {
145373
+ "id": "NEW-CTRL-032",
145374
+ "name": "PERIMETER-COMPROMISE-REBUILD-NOT-PATCH",
145375
+ "description": "The incident-response default for a network-reachable appliance hit by an actively exploited pre-authentication flaw must be to export its configuration, rebuild it and rotate every credential it held, not to patch it in place.",
145376
+ "evidence": "Mandiant found CVE-2021-20021 used in March 2021 to create an administrator account on an internet-accessible SonicWall Email Security installation on Windows Server 2012, chained to a web shell that ran with NT AUTHORITY\\SYSTEM rights, so an exposed instance must be treated as compromised and rebuilt with credentials rotated rather than only upgraded to Hotfix 10.0.9.6103 or 10.0.9.6105, or to 10.0.9.6173 or 10.0.9.6177, which closes the chain.",
145377
+ "gap_closes": [
145378
+ "NIS2-Art21-network-security",
145379
+ "UK-CAF-B2"
145380
+ ]
145381
+ }
145382
+ ]
145383
+ },
145384
+ "CVE-2021-20016": {
145385
+ "name": "SonicWall SSLVPN SMA100 SQL Injection Vulnerability",
145386
+ "lesson_date": "2026-09-26",
145387
+ "ai_discovered_zeroday": false,
145388
+ "ai_discovery_source": "human_researcher",
145389
+ "ai_discovery_date": null,
145390
+ "ai_assist_factor": "none",
145391
+ "attack_vector": {
145392
+ "description": "A remote, unauthenticated attacker submits a crafted SQL query to the SMA 100 series SSL VPN web interface and reads usernames, passwords and session information from the appliance's database, which can then be used to log in to the appliance.",
145393
+ "privileges_required": "none; the request is unauthenticated and is sent directly to the internet-facing SSL VPN interface",
145394
+ "complexity": "low; a single crafted request returns credential and session data",
145395
+ "ai_factor": "None. Reported by the NCC Group; no AI tooling is claimed."
145396
+ },
145397
+ "defense_chain": {
145398
+ "prevention": {
145399
+ "what_would_have_worked": "Enabling the built-in WAF and MFA immediately, then upgrading SMA 100 10.x devices to 10.2.0.6-32sv, which includes the fix first shipped in 10.2.0.5-29sv and which SonicWall directs devices already on 10.2.0.5-29sv to install; blocking the appliance at the firewall or shutting it down while no fix was available.",
145400
+ "was_this_required": false,
145401
+ "framework_requiring_it": "AU-Essential-8-Patch",
145402
+ "adequacy": "SonicWall's WAF neutralized the flaw in testing but was explicitly a stopgap, not a replacement for the firmware; MFA limits reuse of stolen credentials but does not stop the SQL injection itself."
145403
+ },
145404
+ "detection": {
145405
+ "what_would_have_worked": "Watching for unauthenticated requests to the SMA 100 web interface that return account or session data, and treating any pre-patch exposure as suspect because SonicWall was not aware of any forensic data a user could view to determine whether a device had been attacked.",
145406
+ "was_this_required": false,
145407
+ "framework_requiring_it": "ISO-27001-2022-A.8.8",
145408
+ "adequacy": "SonicWall stated it had no forensic data a user could view to determine whether a device was attacked, so detection depends on external network logging rather than on-device evidence."
145409
+ },
145410
+ "response": {
145411
+ "what_would_have_worked": "Resetting passwords for every account that logged in through the SMA 100 web interface and enabling MFA after upgrading, as SonicWall directed.",
145412
+ "was_this_required": false,
145413
+ "framework_requiring_it": "UK-CAF-B4",
145414
+ "adequacy": "Because the flaw exposed usernames, passwords and session information, credential reset is the load-bearing step; upgrading firmware without it leaves harvested credentials usable."
145415
+ }
145416
+ },
145417
+ "framework_coverage": {
145418
+ "NIST-800-53-SI-2": {
145419
+ "covered": true,
145420
+ "adequate": false
145421
+ },
145422
+ "NIS2-Art21-patch-management": {
145423
+ "covered": true,
145424
+ "adequate": false
145425
+ },
145426
+ "UK-CAF-B4": {
145427
+ "covered": true,
145428
+ "adequate": false
145429
+ },
145430
+ "AU-Essential-8-Patch": {
145431
+ "covered": true,
145432
+ "adequate": false
145433
+ },
145434
+ "ISO-27001-2022-A.8.8": {
145435
+ "covered": true,
145436
+ "adequate": false
145437
+ }
145438
+ },
145439
+ "new_control_requirements": [
145440
+ {
145441
+ "id": "NEW-CTRL-032",
145442
+ "name": "PERIMETER-COMPROMISE-REBUILD-NOT-PATCH",
145443
+ "description": "After an internet-facing appliance is exposed to an actively exploited flaw that returns stored credentials or session data, the response rotates every credential the appliance held and does not treat installing the fixed build as the end of remediation.",
145444
+ "evidence": "SonicWall's notice for SNWLID-2021-0001 (CVE-2021-20016) cites the potential credential exposure and directs SMA 100 series 10.x customers to upgrade the firmware, reset the passwords of any users who may have logged in to the device through the web interface, and enable multifactor authentication. UNC2447 exploited the flaw before 10.2.0.5-29sv shipped on February 3, 2021, so the firmware alone left any credentials read before it usable.",
145445
+ "gap_closes": [
145446
+ "UK-CAF-B4",
145447
+ "AU-Essential-8-Patch"
145448
+ ]
145449
+ },
145450
+ {
145451
+ "id": "NEW-CTRL-038",
145452
+ "name": "MITIGATION-ACTIVE-PATCH-PENDING-VERDICT-CLASS",
145453
+ "description": "A remediation record tracks a vendor's interim mitigation as its own state from the vendor's first mitigation notice, and records a device that runs an unfixed build behind that mitigation as mitigated with residual risk, separately from a device running the fixed build.",
145454
+ "evidence": "From January 22 to February 2, 2021 SonicWall published interim measures for SMA 100 series 10.x (access restrictions and MFA from January 22, then password resets on February 1, then the built-in Web Application Firewall on February 2) before the 10.2.0.5-29sv firmware shipped on February 3, and it stated that the WAF does not replace the need to apply the patch and should only be used as a safety measure until the patched firmware is installed. An SMA 100 with the WAF enabled and firmware below 10.2.0.5-29sv is mitigated with residual risk, not remediated.",
145455
+ "gap_closes": [
145456
+ "NIST-800-53-SI-2"
145457
+ ]
145458
+ }
145459
+ ]
145300
145460
  }
145301
145461
  }
package/lib/cve-batch.js CHANGED
@@ -7,6 +7,7 @@
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
9
  const enrich = require('./cve-enrich.js');
10
+ const gaps = require('./gap-detectors.js');
10
11
 
11
12
  // The insertion point just after the top-level "_meta" member, brace-matched
12
13
  // from its key. The catalog is 2-space-indented and new members match it.
@@ -174,6 +175,13 @@ async function curateBatch({ factsPath, judgmentsPath, apply, catalogRoot, today
174
175
  }
175
176
  }
176
177
 
178
+ // New entries and lessons may not cite the curation input; the gap-detector
179
+ // budget covers only texts already in the catalog.
180
+ for (const f of gaps.pipelineWordingFindings({ 'cve-catalog': entries, 'zeroday-lessons': lessons }, { includeDrafts: true })) {
181
+ errors.push(`${f.id}: ${f.catalog === 'cve-catalog' ? 'entry' : 'lesson'} ${f.field} cites the curation ` +
182
+ `input or a field name ("Packet: ...", "the packet names ..."); state the fact and its source instead.`);
183
+ }
184
+
177
185
  // Reported as a validation error so the DRY RUN surfaces it; insertEntries
178
186
  // throws on the same condition, but only under --apply.
179
187
  const catText = fs.readFileSync(path.join(catalogRoot, 'data', 'cve-catalog.json'), 'utf8');
@@ -25,6 +25,63 @@ function hasPlaceholderLanguage(str) {
25
25
  return false;
26
26
  }
27
27
 
28
+ // Curation-pipeline wording in shipped prose: text that cites the input bundle a
29
+ // curator worked from ("Packet: ...", "the packet names ...") or quotes a catalog
30
+ // field name as its source. Each pattern is a construction a network packet cannot
31
+ // form, so "packet data", "the packet socket (AF_PACKET)" and "Packet Storm" do not match.
32
+ const PIPELINE_WORDING = [
33
+ /(?:^|[.;:!?,]['"’”)\]]*\s+|\(\s*)(?:the )?packet:/i,
34
+ /\b(?:per(?: the)?|(?:in|from|follows) the|this|own) packet:/i,
35
+ /\bpacket:\s*(?:cisa[_ ]kev|active_exploitation|patch_available|live_patch|cwe_refs|cvss|vector|attack_vector|rwep|affected(?:_versions)?|poc_available)\b/i,
36
+ /\bPacket fields?\s*(?::|for\s+(?:CVE-|this\b))/,
37
+ /\bPacket vector\b/,
38
+ /\bpacket(?:'s)? (?:attack_vector|live_patch_notes|cisa_kev|affected_versions|poc_description|patch_available|active_exploitation_notes|vendor_update_paths|cwe_refs|rwep_\w+)\b/i,
39
+ /\b[Tt]he packet (?:records|names|registers|supplies|lists|says|states|reports|marks|confirms|describes)\b/,
40
+ /\b(?:per|according to) the packet\b/i,
41
+ // A possessive followed by a curation noun, a catalog field identifier or a
42
+ // framework key. Words a network packet can also own (path, delivery, length,
43
+ // boundary) are left out.
44
+ /\bpacket's\s+(?:own|vector|stated|attack(?:\s+(?:vector|path))?|exploitation|remediation|live[- ]?patch|livepatch|chain|fix(?:ed)?|attacker|outcome|affected|flaw(?:-remediation)?|precondition|primitive|rwep|exploit|kev|gaps?|description|cit(?:ing|ed)|confirmed|defect|cwe\b|escalation|caf|recorded|requiredaction|product|essential(?:-eight)?|framing|cvss|reachability|timeline|chained|campaign|exposure|actor|interim|named|attribution|title|targeting|coverage|end-of-life|guidance|observed|recommended|threat|public-exploit|vendor|requirement|mitigation|documented|implant|lure|post-exploitation|patch-management|references?|notes?|framework|facts|advisory|versions?(?!\s+(?:field|number|byte|bits?)\b)|summary|deadline|(?:nist-800-53|uk-caf|au-essential-8|au-ism|iso-27001|nis2|dora|eu-ai-act|cwe-)[\w.-]*|(?:active_exploitation(?:_notes)?|affected_versions|ai_discovered|ai_discovery_\w+|attack_vector|cisa_kev\w*|cvss_\w+|cwe_refs|discovery_attribution_note|epss_\w+|framework_control_gaps|known_ransomware_use|live_patch_\w+|patch_available|patch_required_reboot|poc_available|poc_description|rwep_\w+|vendor_advisories|vendor_update_paths|verification_sources))\b/i,
45
+ ];
46
+
47
+ function hasPipelineWording(str) {
48
+ if (typeof str !== "string" || str.length === 0) return false;
49
+ return PIPELINE_WORDING.some((re) => re.test(str));
50
+ }
51
+
52
+ // Every string under a value, with its dotted path.
53
+ function _strings(value, pathSoFar, out) {
54
+ if (typeof value === "string") out.push([pathSoFar, value]);
55
+ else if (Array.isArray(value)) value.forEach((v, i) => _strings(v, `${pathSoFar}[${i}]`, out));
56
+ else if (value && typeof value === "object") {
57
+ for (const k of Object.keys(value)) _strings(value[k], pathSoFar ? `${pathSoFar}.${k}` : k, out);
58
+ }
59
+ return out;
60
+ }
61
+
62
+ // One finding per curated catalog or lesson text that carries pipeline wording.
63
+ // Drafts are skipped unless opts.includeDrafts is set, as it is for a batch about
64
+ // to be written, where every submitted object is checked.
65
+ function pipelineWordingFindings(loaded, opts = {}) {
66
+ const out = [];
67
+ for (const catalog of ["cve-catalog", "zeroday-lessons"]) {
68
+ const cat = loaded[catalog];
69
+ if (!cat) continue;
70
+ for (const id of Object.keys(cat)) {
71
+ if (id === "_meta") continue;
72
+ const e = cat[id];
73
+ if (!e || (e._auto_imported === true && !opts.includeDrafts)) continue;
74
+ for (const [field, s] of _strings(e, "", [])) {
75
+ if (hasPipelineWording(s)) {
76
+ out.push({ class: "pipeline-wording", catalog, id, field,
77
+ reason: "text cites the curation input or a catalog field name instead of stating the fact and its source" });
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+
28
85
  // Fields present but weak; what counts as weak is per catalog and field.
29
86
  function contentQualityFindings(loaded) {
30
87
  const out = [];
@@ -403,7 +460,8 @@ function runAllDetectors(loaded, opts = {}) {
403
460
  ...crossRefCompletenessFindings(loaded),
404
461
  ...schemaEvolutionFindings(loaded),
405
462
  ...operatorActionSlaFindings(loaded, opts),
406
- ...unusedOrphanFindings(loaded, orphanOpts)
463
+ ...unusedOrphanFindings(loaded, orphanOpts),
464
+ ...pipelineWordingFindings(loaded)
407
465
  ];
408
466
  }
409
467
 
@@ -416,11 +474,15 @@ const DETECTOR_CLASSES = [
416
474
  "cross-ref-completeness",
417
475
  "schema-evolution",
418
476
  "operator-action-sla",
419
- "unused-orphan"
477
+ "unused-orphan",
478
+ "pipeline-wording"
420
479
  ];
421
480
 
422
481
  module.exports = {
423
482
  hasPlaceholderLanguage,
483
+ hasPipelineWording,
484
+ pipelineWordingFindings,
485
+ PIPELINE_WORDING,
424
486
  daysSince,
425
487
  contentQualityFindings,
426
488
  temporalStalenessFindings,