@blamejs/exceptd-skills 0.19.33 → 0.19.34

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 (119) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/bin/exceptd.js +896 -2824
  3. package/data/_indexes/_meta.json +2 -2
  4. package/lib/auto-discovery.js +56 -286
  5. package/lib/canonical-eq.js +7 -40
  6. package/lib/citation-resolve.js +22 -70
  7. package/lib/collectors/ai-api.js +20 -54
  8. package/lib/collectors/cicd-pipeline-compromise.js +40 -108
  9. package/lib/collectors/citation-hygiene.js +72 -210
  10. package/lib/collectors/containers.js +41 -130
  11. package/lib/collectors/cred-stores.js +31 -115
  12. package/lib/collectors/crypto-codebase.js +55 -138
  13. package/lib/collectors/crypto.js +24 -54
  14. package/lib/collectors/hardening.js +20 -78
  15. package/lib/collectors/kernel.js +16 -46
  16. package/lib/collectors/library-author.js +57 -206
  17. package/lib/collectors/mcp.js +24 -70
  18. package/lib/collectors/runtime.js +24 -86
  19. package/lib/collectors/sbom.js +34 -106
  20. package/lib/collectors/scan-excludes.js +31 -138
  21. package/lib/collectors/secrets.js +62 -178
  22. package/lib/cross-ref-api.js +39 -123
  23. package/lib/currency-severity.js +8 -27
  24. package/lib/cve-batch.js +13 -21
  25. package/lib/cve-cli.js +13 -20
  26. package/lib/cve-curation.js +72 -239
  27. package/lib/cve-regression-watcher.js +29 -152
  28. package/lib/cvss.js +13 -54
  29. package/lib/doctor-bucketing.js +3 -19
  30. package/lib/exit-codes.js +10 -42
  31. package/lib/flag-suggest.js +7 -25
  32. package/lib/framework-gap.js +35 -114
  33. package/lib/gap-detectors.js +37 -159
  34. package/lib/id-validation.js +9 -30
  35. package/lib/job-queue.js +13 -36
  36. package/lib/lint-skills.js +64 -232
  37. package/lib/playbook-runner.js +693 -2095
  38. package/lib/prefetch.js +100 -376
  39. package/lib/refresh-external.js +199 -627
  40. package/lib/refresh-network.js +75 -307
  41. package/lib/rfc-cli.js +23 -68
  42. package/lib/scoring.js +77 -145
  43. package/lib/sign.js +43 -229
  44. package/lib/source-advisories.js +43 -194
  45. package/lib/source-ghsa.js +37 -120
  46. package/lib/source-osv.js +94 -266
  47. package/lib/ttp-mapper.js +14 -24
  48. package/lib/upstream-check-cli.js +10 -28
  49. package/lib/upstream-check.js +19 -44
  50. package/lib/validate-catalog-meta.js +17 -61
  51. package/lib/validate-cve-catalog.js +43 -119
  52. package/lib/validate-indexes.js +25 -76
  53. package/lib/validate-package.js +16 -62
  54. package/lib/validate-playbooks.js +69 -275
  55. package/lib/validate-vendor.js +16 -49
  56. package/lib/verify.js +56 -286
  57. package/lib/version-pins.js +5 -34
  58. package/lib/worker-pool.js +11 -30
  59. package/lib/xml-tokenizer.js +47 -152
  60. package/manifest.json +53 -53
  61. package/orchestrator/dispatcher.js +17 -68
  62. package/orchestrator/event-bus.js +11 -74
  63. package/orchestrator/index.js +138 -412
  64. package/orchestrator/pipeline.js +28 -85
  65. package/orchestrator/scanner.js +34 -138
  66. package/orchestrator/scheduler.js +20 -84
  67. package/package.json +1 -1
  68. package/sbom.cdx.json +241 -241
  69. package/scripts/audit-catalog-gaps.js +9 -62
  70. package/scripts/audit-cross-skill.js +5 -31
  71. package/scripts/audit-perf.js +6 -16
  72. package/scripts/backfill-theater-test.js +7 -64
  73. package/scripts/bootstrap.js +12 -44
  74. package/scripts/build-indexes.js +40 -154
  75. package/scripts/builders/activity-feed.js +4 -14
  76. package/scripts/builders/catalog-summaries.js +3 -10
  77. package/scripts/builders/currency.js +7 -20
  78. package/scripts/builders/cwe-chains.js +7 -30
  79. package/scripts/builders/did-ladders.js +6 -13
  80. package/scripts/builders/frequency.js +5 -19
  81. package/scripts/builders/jurisdiction-clocks.js +6 -25
  82. package/scripts/builders/recipes.js +6 -14
  83. package/scripts/builders/section-offsets.js +13 -51
  84. package/scripts/builders/stale-content.js +7 -28
  85. package/scripts/builders/summary-cards.js +8 -29
  86. package/scripts/builders/theater-fingerprints.js +12 -27
  87. package/scripts/builders/token-budget.js +4 -31
  88. package/scripts/check-agents-md-collectors.js +11 -54
  89. package/scripts/check-catalog-gap-budget.js +15 -32
  90. package/scripts/check-changelog-extract.js +18 -48
  91. package/scripts/check-codebase-patterns-currency.js +6 -22
  92. package/scripts/check-codebase-patterns.js +50 -143
  93. package/scripts/check-epss-consistency.js +9 -64
  94. package/scripts/check-framework-gap-coverage.js +13 -31
  95. package/scripts/check-manifest-snapshot.js +13 -73
  96. package/scripts/check-sbom-currency.js +44 -142
  97. package/scripts/check-test-count.js +15 -52
  98. package/scripts/check-test-coverage.js +66 -197
  99. package/scripts/check-test-subjects.js +21 -62
  100. package/scripts/check-ttp-references.js +14 -38
  101. package/scripts/check-ttp-upstream.js +8 -40
  102. package/scripts/check-version-bump.js +9 -61
  103. package/scripts/check-version-tags.js +20 -121
  104. package/scripts/predeploy.js +38 -184
  105. package/scripts/refresh-manifest-snapshot.js +16 -38
  106. package/scripts/refresh-mitre-atlas.js +3 -8
  107. package/scripts/refresh-mitre-attack.js +1 -8
  108. package/scripts/refresh-mitre-d3fend.js +3 -9
  109. package/scripts/refresh-mitre-ics-attack.js +3 -8
  110. package/scripts/refresh-reverse-refs.js +27 -94
  111. package/scripts/refresh-rfc-index.js +2 -10
  112. package/scripts/refresh-sbom.js +31 -161
  113. package/scripts/refresh-upstream-catalogs.js +40 -137
  114. package/scripts/release.js +69 -232
  115. package/scripts/run-e2e-scenarios.js +24 -71
  116. package/scripts/sync-manifest-metadata.js +10 -34
  117. package/scripts/sync-package-description.js +8 -17
  118. package/scripts/validate-vendor-online.js +13 -44
  119. package/scripts/verify-shipped-tarball.js +35 -140
@@ -1,31 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * Scheduled task coordinator for skill currency maintenance.
5
- *
6
- * Schedules: weekly currency check, monthly CVE validation reminder, annual
7
- * full audit reminder. Emits events via event-bus.js when currency thresholds
8
- * are breached.
9
- *
10
- * This is a simple interval-based scheduler. For production use, swap for a
11
- * proper cron daemon or cloud scheduler without changing the task definitions.
12
- *
13
- * Bootstrap-fire policy. Long-cadence tasks (monthly, annual) are also
14
- * evaluated on `start()` so a freshly-restarted watcher does not silently
15
- * skip a due interval. Whether a task fires on bootstrap is gated by a
16
- * persisted "last fired" timestamp store at
17
- * `~/.exceptd/scheduler-last-fired.json` — the task fires only when the
18
- * elapsed time since the persisted timestamp exceeds the interval. The
19
- * weekly currency check always fires on bootstrap (legacy behavior).
20
- *
21
- * Implementation note — INT32 overflow guard. `setInterval` / `setTimeout`
22
- * delay values are coerced to a signed 32-bit integer; any value above
23
- * 2^31 - 1 ms (~24.8 days) is silently clamped to 1 ms by Node, which
24
- * causes the handler to fire ~1000×/sec. The MONTHLY_CVE_VALIDATION and
25
- * ANNUAL_AUDIT intervals both exceed that limit. `scheduleEvery` wraps the
26
- * underlying timer with a short tick interval (capped at SAFE_MAX_MS) and
27
- * compares wall-clock elapsed time against the requested interval, so any
28
- * delay — including multi-year intervals — fires exactly when due.
4
+ * Interval-based coordinator for skill currency maintenance: weekly currency
5
+ * check, monthly CVE validation reminder, annual full audit. Breached currency
6
+ * thresholds are emitted on event-bus.js.
29
7
  */
30
8
 
31
9
  const fs = require('fs');
@@ -36,7 +14,7 @@ const { bus, EVENT_TYPES } = require('./event-bus');
36
14
  const { currencyCheck } = require('./pipeline');
37
15
 
38
16
  const SAFE_MAX_MS = 2_147_483_647; // INT32 max — Node's setTimeout/setInterval ceiling.
39
- const TICK_MS = Math.min(SAFE_MAX_MS, 24 * 60 * 60 * 1000); // 24h tick by default.
17
+ const TICK_MS = Math.min(SAFE_MAX_MS, 24 * 60 * 60 * 1000);
40
18
 
41
19
  const INTERVALS = {
42
20
  WEEKLY_CURRENCY: 7 * 24 * 60 * 60 * 1000,
@@ -58,13 +36,7 @@ const LAST_FIRED_KEYS = {
58
36
  let unschedulers = [];
59
37
  let running = false;
60
38
 
61
- // --- persistent last-fired store ---
62
-
63
- /**
64
- * Resolve the path of the last-fired persistence file. Defaults to
65
- * `~/.exceptd/scheduler-last-fired.json`. Honors EXCEPTD_HOME for the test
66
- * suite so unit tests stay isolated from the maintainer's real home dir.
67
- */
39
+ /** EXCEPTD_HOME overrides the root so tests stay off the real home dir. */
68
40
  function _lastFiredStorePath() {
69
41
  const root = process.env.EXCEPTD_HOME || path.join(os.homedir(), '.exceptd');
70
42
  return path.join(root, 'scheduler-last-fired.json');
@@ -85,9 +57,7 @@ function _saveLastFired(store) {
85
57
  fs.mkdirSync(path.dirname(p), { recursive: true });
86
58
  fs.writeFileSync(p, JSON.stringify(store, null, 2));
87
59
  } catch (err) {
88
- // Persistence is best-effort. A failed write only means the next start
89
- // can't tell whether the task fired; it does not break the running
90
- // scheduler.
60
+ // Best-effort: a failed write only costs the next start its last-fired knowledge.
91
61
  console.error('[scheduler] could not persist last-fired:', err.message);
92
62
  }
93
63
  }
@@ -107,24 +77,16 @@ function _shouldBootstrapFire(key, intervalMs) {
107
77
  return (Date.now() - last) >= intervalMs;
108
78
  }
109
79
 
110
- // --- scheduling primitive ---
111
-
112
80
  /**
113
- * Schedule `handler` to fire every `intervalMs`, safely for intervals that
114
- * exceed Node's INT32 setTimeout ceiling. Returns an unschedule function.
81
+ * Schedule `handler` every `intervalMs` ms, returning an unschedule function.
82
+ * Throws RangeError unless intervalMs is a positive finite number.
115
83
  *
116
- * @param {number} intervalMs Desired interval in milliseconds (any positive value).
117
- * @param {Function} handler Function to invoke on each interval.
118
- * @returns {Function} Call to stop further firings.
84
+ * setInterval coerces its delay to signed 32-bit — above 2^31-1 ms it clamps to
85
+ * 1 ms and fires ~1000x/sec, which the monthly and annual intervals would hit.
86
+ * So the timer ticks at TICK_MS or less and compares wall-clock elapsed.
119
87
  */
120
88
  function scheduleEvery(intervalMs, handler) {
121
- // T P1-4: lower-bound guard. v0.12.12 added the INT32 overflow clamp
122
- // (upper bound) but never asserted intervalMs > 0. `scheduleEvery(0, fn)`
123
- // would set a 0ms interval that fires ~10k times per second; negatives
124
- // (-100) coerce the same way and NaN drives setInterval into a 1ms tick.
125
- // All three exhaust the event loop. Refuse the call rather than silently
126
- // floor — the scheduler is a long-lived primitive and a footgun here
127
- // poisons every periodic task in the watcher.
89
+ // Zero, negative and NaN drive a ~1 ms tick, so refuse rather than floor.
128
90
  if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
129
91
  throw new RangeError(`scheduleEvery: intervalMs must be a positive finite number, got ${intervalMs}`);
130
92
  }
@@ -138,23 +100,15 @@ function scheduleEvery(intervalMs, handler) {
138
100
  }
139
101
  };
140
102
  const id = setInterval(tick, Math.min(intervalMs, TICK_MS));
141
- // Deliberately NOT calling id.unref(): the `watch` orchestrator verb
142
- // is long-running and relies on the scheduler timers to keep the event
143
- // loop alive (the event bus has no I/O of its own). Callers that don't
144
- // want the timer to hold the loop open should call the returned
145
- // unschedule function in their teardown path.
103
+ // No id.unref(): `watch` is long-running and relies on these timers to hold the
104
+ // event loop open — the event bus has no I/O of its own.
146
105
  return () => clearInterval(id);
147
106
  }
148
107
 
149
- // --- public API ---
150
-
151
108
  /**
152
- * Start the scheduler. Runs the weekly task immediately, then schedules all
153
- * three on their intervals. Monthly and annual tasks are also "bootstrap
154
- * fired" on start when the persisted last-fired timestamp is older than the
155
- * interval (or absent), so a restarted watcher does not silently skip a due
156
- * window. Bootstrap-fire happens inside the same try/catch the periodic
157
- * wrapper uses so a single thrown task cannot crash the watcher.
109
+ * Start the scheduler: the weekly task runs immediately, then all three run on
110
+ * their intervals. Monthly and annual also bootstrap-fire when the persisted
111
+ * last-fired timestamp is absent or older than the interval.
158
112
  */
159
113
  function start() {
160
114
  if (running) return;
@@ -165,15 +119,12 @@ function start() {
165
119
  catch (e) { console.error('[scheduler] ' + label + ' failed:', e); }
166
120
  };
167
121
 
168
- // Weekly always fires on bootstrap (legacy behavior).
122
+ // Weekly always fires on bootstrap — ungated, unlike the two below.
169
123
  safeRun('weekly currency bootstrap', () => {
170
124
  runWeeklyCurrencyCheck();
171
125
  _markFired(LAST_FIRED_KEYS.WEEKLY_CURRENCY);
172
126
  });
173
127
 
174
- // Monthly + annual bootstrap-fire only when the persisted timestamp is
175
- // older than the interval. This closes the "freshly-restarted watcher
176
- // never fires the long-cadence task" gap.
177
128
  if (_shouldBootstrapFire(LAST_FIRED_KEYS.MONTHLY_CVE_VALIDATION, INTERVALS.MONTHLY_CVE_VALIDATION)) {
178
129
  safeRun('monthly CVE bootstrap', () => {
179
130
  runMonthlyCveValidation();
@@ -203,9 +154,6 @@ function start() {
203
154
  console.log('[scheduler] Started. Weekly currency check, monthly CVE validation, annual audit scheduled.');
204
155
  }
205
156
 
206
- /**
207
- * Stop the scheduler and clear all timers.
208
- */
209
157
  function stop() {
210
158
  for (const off of unschedulers) {
211
159
  try { off(); } catch { /* ignore */ }
@@ -215,30 +163,18 @@ function stop() {
215
163
  console.log('[scheduler] Stopped.');
216
164
  }
217
165
 
218
- /**
219
- * Run just the currency check immediately (for CLI use).
220
- * @returns {object} Currency report
221
- */
222
166
  function runCurrencyNow() {
223
167
  return runWeeklyCurrencyCheck();
224
168
  }
225
169
 
226
- // --- task implementations ---
227
-
228
170
  function runWeeklyCurrencyCheck() {
229
171
  const timestamp = new Date().toISOString();
230
172
  console.log(`[scheduler] Running weekly currency check — ${timestamp}`);
231
173
 
232
174
  const { currency_report, action_required, critical_count } = currencyCheck();
233
175
 
234
- // Emit ONE aggregated SKILL_CURRENCY_LOW_AGGREGATE event per run instead
235
- // of N per-skill events. Per-run aggregate prevents downstream consumers
236
- // (`watch`, dashboards, alerting webhooks) from receiving an event storm
237
- // when N skills are simultaneously stale — common after a long pause
238
- // between runs or on first bootstrap. The aggregate payload carries
239
- // critical_count + the full array of stale skills so consumers can still
240
- // drill in. The legacy per-skill SKILL_CURRENCY_LOW signature is preserved
241
- // for callers (and tests) that consume bus.skillCurrencyLow() directly.
176
+ // ONE aggregate event per run, not N per-skill: per-skill events storm every
177
+ // consumer. The payload carries the full stale list so a consumer can drill in.
242
178
  const critical = currency_report.filter(s => s.currency_score < CURRENCY_THRESHOLDS.critical);
243
179
  if (critical.length > 0) {
244
180
  bus.emit(EVENT_TYPES.SKILL_CURRENCY_LOW_AGGREGATE, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.19.33",
3
+ "version": "0.19.34",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (1417 CVEs / 236 CWEs / 794 ATT&CK + ICS / 174 ATLAS / 468 D3FEND / 8889 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
5
5
  "keywords": [
6
6
  "ai-security",