@blamejs/exceptd-skills 0.12.13 → 0.12.15

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 (87) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/bin/exceptd.js +147 -9
  3. package/data/_indexes/_meta.json +45 -45
  4. package/data/_indexes/activity-feed.json +4 -4
  5. package/data/_indexes/catalog-summaries.json +29 -29
  6. package/data/_indexes/chains.json +3238 -3210
  7. package/data/_indexes/frequency.json +3 -0
  8. package/data/_indexes/jurisdiction-map.json +5 -3
  9. package/data/_indexes/section-offsets.json +712 -685
  10. package/data/_indexes/theater-fingerprints.json +1 -1
  11. package/data/_indexes/token-budget.json +355 -340
  12. package/data/atlas-ttps.json +144 -129
  13. package/data/attack-techniques.json +319 -76
  14. package/data/cve-catalog.json +515 -475
  15. package/data/cwe-catalog.json +1081 -759
  16. package/data/exploit-availability.json +63 -15
  17. package/data/framework-control-gaps.json +867 -843
  18. package/data/rfc-references.json +276 -276
  19. package/keys/EXPECTED_FINGERPRINT +1 -0
  20. package/lib/auto-discovery.js +21 -4
  21. package/lib/cross-ref-api.js +39 -6
  22. package/lib/cve-curation.js +18 -5
  23. package/lib/lint-skills.js +6 -1
  24. package/lib/playbook-runner.js +742 -78
  25. package/lib/refresh-external.js +40 -22
  26. package/lib/refresh-network.js +193 -17
  27. package/lib/scoring.js +20 -7
  28. package/lib/source-ghsa.js +219 -37
  29. package/lib/source-osv.js +381 -122
  30. package/lib/validate-catalog-meta.js +64 -9
  31. package/lib/validate-cve-catalog.js +56 -18
  32. package/lib/validate-indexes.js +88 -37
  33. package/lib/verify.js +72 -0
  34. package/manifest-snapshot.json +1 -1
  35. package/manifest-snapshot.sha256 +1 -0
  36. package/manifest.json +73 -73
  37. package/orchestrator/dispatcher.js +21 -1
  38. package/orchestrator/event-bus.js +52 -8
  39. package/orchestrator/index.js +279 -20
  40. package/orchestrator/pipeline.js +63 -2
  41. package/orchestrator/scanner.js +32 -10
  42. package/orchestrator/scheduler.js +150 -17
  43. package/package.json +3 -1
  44. package/sbom.cdx.json +7 -7
  45. package/scripts/check-manifest-snapshot.js +32 -0
  46. package/scripts/check-sbom-currency.js +65 -3
  47. package/scripts/check-test-coverage.js +142 -19
  48. package/scripts/predeploy.js +83 -39
  49. package/scripts/refresh-manifest-snapshot.js +55 -4
  50. package/scripts/validate-vendor-online.js +169 -0
  51. package/scripts/verify-shipped-tarball.js +106 -3
  52. package/skills/ai-attack-surface/skill.md +18 -10
  53. package/skills/ai-c2-detection/skill.md +7 -2
  54. package/skills/ai-risk-management/skill.md +5 -4
  55. package/skills/api-security/skill.md +3 -3
  56. package/skills/attack-surface-pentest/skill.md +5 -5
  57. package/skills/cloud-security/skill.md +1 -1
  58. package/skills/compliance-theater/skill.md +8 -8
  59. package/skills/container-runtime-security/skill.md +1 -1
  60. package/skills/dlp-gap-analysis/skill.md +5 -1
  61. package/skills/email-security-anti-phishing/skill.md +1 -1
  62. package/skills/exploit-scoring/skill.md +18 -18
  63. package/skills/framework-gap-analysis/skill.md +6 -6
  64. package/skills/global-grc/skill.md +3 -2
  65. package/skills/identity-assurance/skill.md +2 -2
  66. package/skills/incident-response-playbook/skill.md +4 -4
  67. package/skills/kernel-lpe-triage/skill.md +21 -2
  68. package/skills/mcp-agent-trust/skill.md +17 -10
  69. package/skills/mlops-security/skill.md +2 -1
  70. package/skills/ot-ics-security/skill.md +1 -1
  71. package/skills/policy-exception-gen/skill.md +3 -3
  72. package/skills/pqc-first/skill.md +1 -1
  73. package/skills/rag-pipeline-security/skill.md +7 -3
  74. package/skills/researcher/skill.md +20 -3
  75. package/skills/sector-energy/skill.md +1 -1
  76. package/skills/sector-federal-government/skill.md +1 -1
  77. package/skills/sector-financial/skill.md +3 -3
  78. package/skills/sector-healthcare/skill.md +2 -2
  79. package/skills/security-maturity-tiers/skill.md +7 -7
  80. package/skills/skill-update-loop/skill.md +19 -3
  81. package/skills/supply-chain-integrity/skill.md +1 -1
  82. package/skills/threat-model-currency/skill.md +11 -11
  83. package/skills/threat-modeling-methodology/skill.md +3 -3
  84. package/skills/webapp-security/skill.md +1 -1
  85. package/skills/zeroday-gap-learn/skill.md +51 -7
  86. package/vendor/blamejs/_PROVENANCE.json +4 -1
  87. package/vendor/blamejs/worker-pool.js +38 -0
@@ -0,0 +1 @@
1
+ SHA256:JX04VjFprM7+3gHJdO0Wi4tTCf1RKI9Roza3XOzAe0Y=
@@ -184,10 +184,21 @@ function buildKevDraftEntry(kevEntry, nvdPayload, epssPayload) {
184
184
  "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
185
185
  kevEntry.notes ? String(kevEntry.notes) : null,
186
186
  ].filter(Boolean),
187
- source_verified: false,
187
+ // v0.12.15 (audit M P1-B): schema requires source_verified to be a
188
+ // YYYY-MM-DD string OR null; the prior `false` boolean produced an
189
+ // entry that failed strict catalog validation. Use null to mean
190
+ // "not yet verified" — operators populate the date during curation.
191
+ source_verified: null,
188
192
  last_updated: TODAY,
189
193
  last_verified: TODAY,
190
- _auto_imported: {
194
+ // v0.12.15 (audit M P1-D): `_auto_imported` must be the boolean `true`
195
+ // for lib/validate-cve-catalog.js's draft-recognition check (strict
196
+ // `=== true` comparison). The prior object-shape was non-recognizable
197
+ // and the strict validator treated KEV-discovered drafts as
198
+ // hard-error entries instead of warning-tier drafts. The provenance
199
+ // metadata that used to be inline now lives in `_auto_imported_meta`.
200
+ _auto_imported: true,
201
+ _auto_imported_meta: {
191
202
  source: "KEV discovery",
192
203
  imported_at: TODAY,
193
204
  curation_needed: [
@@ -476,14 +487,20 @@ async function discoverNewRfcs(ctx, opts = {}) {
476
487
  skills_referencing: [],
477
488
  errata_count: null,
478
489
  last_verified: TODAY,
479
- _auto_imported: {
490
+ // v0.12.15 (audit M P1-D, P3-T): boolean `_auto_imported: true` for
491
+ // strict-validator recognition; provenance moved to sibling
492
+ // `_auto_imported_meta`. Errata-URL hint converted to a real template
493
+ // literal so the rfc number actually interpolates (the previous double-
494
+ // quoted string left `${number}` as literal text in operator output).
495
+ _auto_imported: true,
496
+ _auto_imported_meta: {
480
497
  source: `RFC discovery (IETF ${wg} working group)`,
481
498
  imported_at: TODAY,
482
499
  curation_needed: [
483
500
  "relevance — project-specific framing of how this RFC matters for mid-2026 threats",
484
501
  "lag_notes — what gaps remain or where the RFC falls short",
485
502
  "skills_referencing — list of skills that should cite this RFC",
486
- "errata_count — populate from <rfc-editor.org/errata/rfc${number}>",
503
+ `errata_count — populate from <rfc-editor.org/errata/rfc${number}>`,
487
504
  ],
488
505
  },
489
506
  };
@@ -21,6 +21,15 @@ const INDEX_DIR = path.join(DATA_DIR, '_indexes');
21
21
 
22
22
  const _cache = new Map();
23
23
 
24
+ // v0.12.14 (audit C-F7): catalog corruption no longer crashes the runner
25
+ // uncaught. A malformed JSON file in data/ used to produce a SyntaxError
26
+ // at require-time of any consumer (lib/playbook-runner.js), which threw
27
+ // out of the run() entrypoint without honoring AGENTS.md's "non-zero
28
+ // exit + {ok:false, error} to stderr" contract. Now: caught + degraded
29
+ // to an empty catalog with a recorded _loadError that downstream code
30
+ // can inspect.
31
+ const _loadErrors = [];
32
+
24
33
  function loadCatalog(filename) {
25
34
  if (_cache.has(filename)) return _cache.get(filename);
26
35
  const full = path.join(DATA_DIR, filename);
@@ -28,9 +37,17 @@ function loadCatalog(filename) {
28
37
  _cache.set(filename, {});
29
38
  return {};
30
39
  }
31
- const parsed = JSON.parse(fs.readFileSync(full, 'utf8'));
32
- _cache.set(filename, parsed);
33
- return parsed;
40
+ try {
41
+ const parsed = JSON.parse(fs.readFileSync(full, 'utf8'));
42
+ _cache.set(filename, parsed);
43
+ return parsed;
44
+ } catch (e) {
45
+ _loadErrors.push({ kind: 'catalog', file: filename, error: e.message });
46
+ const stub = {};
47
+ Object.defineProperty(stub, '_loadError', { value: e.message, enumerable: false });
48
+ _cache.set(filename, stub);
49
+ return stub;
50
+ }
34
51
  }
35
52
 
36
53
  function loadIndex(filename) {
@@ -40,9 +57,21 @@ function loadIndex(filename) {
40
57
  _cache.set('idx:' + filename, {});
41
58
  return {};
42
59
  }
43
- const parsed = JSON.parse(fs.readFileSync(full, 'utf8'));
44
- _cache.set('idx:' + filename, parsed);
45
- return parsed;
60
+ try {
61
+ const parsed = JSON.parse(fs.readFileSync(full, 'utf8'));
62
+ _cache.set('idx:' + filename, parsed);
63
+ return parsed;
64
+ } catch (e) {
65
+ _loadErrors.push({ kind: 'index', file: filename, error: e.message });
66
+ const stub = {};
67
+ Object.defineProperty(stub, '_loadError', { value: e.message, enumerable: false });
68
+ _cache.set('idx:' + filename, stub);
69
+ return stub;
70
+ }
71
+ }
72
+
73
+ function getLoadErrors() {
74
+ return _loadErrors.slice();
46
75
  }
47
76
 
48
77
  function entries(catalog) {
@@ -221,4 +250,8 @@ module.exports = {
221
250
  // Lower-level access (engine uses these directly)
222
251
  _loadCatalog: loadCatalog,
223
252
  _loadIndex: loadIndex,
253
+ // v0.12.14: surface accumulated catalog/index load errors. Returns
254
+ // [{kind, file, error}, ...] for every catalog/index whose JSON
255
+ // parse failed. Empty array on a healthy install.
256
+ getLoadErrors,
224
257
  };
@@ -50,11 +50,18 @@ let _cveSchemaCache = null;
50
50
  function loadCveEntrySchema() {
51
51
  if (_cveSchemaCache) return _cveSchemaCache;
52
52
  try {
53
+ // v0.12.15 (audit M P1-A): the prior version of this function looked for
54
+ // either `root.patternProperties["^CVE-\\d{4}-\\d+$"]` or an object
55
+ // `root.additionalProperties`. The actual schema at lib/schemas/cve-
56
+ // catalog.schema.json has NEITHER — its top level IS the entry shape
57
+ // (`{type:'object', required:[...], properties: {...}}`) because
58
+ // validate-cve-catalog.js iterates each CVE id key manually and runs
59
+ // the schema validator over each value. Result: loadCveEntrySchema()
60
+ // always returned null, the v0.12.12 codex P1 #1 fix (strict-schema
61
+ // gating of promotion) was silently disabled, and schema-violating
62
+ // entries promoted anyway. Use the root schema directly.
53
63
  const root = JSON.parse(fs.readFileSync(CVE_SCHEMA_PATH, "utf8"));
54
- const entrySchema =
55
- (root.patternProperties && root.patternProperties["^CVE-\\d{4}-\\d+$"]) ||
56
- (root.additionalProperties && typeof root.additionalProperties === "object" ? root.additionalProperties : null);
57
- _cveSchemaCache = entrySchema || null;
64
+ _cveSchemaCache = root || null;
58
65
  return _cveSchemaCache;
59
66
  } catch {
60
67
  return null;
@@ -264,7 +271,13 @@ function buildQuestionnaire(cveId, draft) {
264
271
  // Pull candidate catalogs. Each is optional — missing catalogs are skipped
265
272
  // gracefully. J7 makes these one-shot loads per process.
266
273
  const atlas = loadJson("data/atlas-ttps.json");
267
- const attack = loadJson("data/attack-ttps.json");
274
+ // v0.12.15 (audit M P1-E): the catalog ships as data/attack-techniques.json
275
+ // (renamed from data/attack-ttps.json before the v0.12.12 release; the
276
+ // canonical file path is also what lib/validate-cve-catalog.js consumes).
277
+ // The prior `data/attack-ttps.json` lookup silently fell back to an empty
278
+ // object via loadJsonRaw's ENOENT handling, so the ATT&CK candidate
279
+ // questionnaire branch always returned zero proposals.
280
+ const attack = loadJson("data/attack-techniques.json");
268
281
  const cwe = loadJson("data/cwe-catalog.json");
269
282
  const frameworkGaps = loadJson("data/framework-control-gaps.json");
270
283
 
@@ -635,8 +635,13 @@ function loadContext() {
635
635
  */
636
636
  function findOrphanSkillFiles(manifestSkills) {
637
637
  if (!fs.existsSync(SKILLS_DIR)) return [];
638
+ // F19 — manifest paths are stored as forward-slash strings by contract
639
+ // (lib/verify.js validateSkillPath() rejects backslashes). The previous
640
+ // path.sep split was a no-op on Linux and incorrect on Windows when
641
+ // mixed separators arrived through other ingest paths; the cleaner
642
+ // contract is to normalise the comparison key directly.
638
643
  const referenced = new Set(
639
- manifestSkills.map((s) => s.path.split(path.sep).join('/')),
644
+ manifestSkills.map((s) => String(s.path).replace(/\\/g, '/')),
640
645
  );
641
646
  const orphans = [];
642
647
  for (const entry of fs.readdirSync(SKILLS_DIR, { withFileTypes: true })) {