@blamejs/exceptd-skills 0.10.0 → 0.10.2

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.
@@ -160,7 +160,23 @@ function gapReport(frameworkIds, threatScenario, controlGaps, cveCatalog = {}) {
160
160
 
161
161
  const frameworkResults = {};
162
162
  for (const id of frameworkIds) {
163
- const frameworkGaps = relevantGaps.filter(([, g]) => g.framework?.includes(id));
163
+ // Match a framework filter ID against catalog entries by:
164
+ // - exact match against gap.framework (e.g. "NIST SP 800-53 Rev 5")
165
+ // - normalized substring match (strip case + spaces + hyphens, e.g. user
166
+ // passing "nist-800-53" matches catalog "NIST SP 800-53 Rev 5")
167
+ // - normalized prefix match on the gap KEY (e.g. user "nist-800-53"
168
+ // matches keys "NIST-800-53-SI-2", "NIST-800-53-SC-8")
169
+ // This makes the named-framework filter behave the same way `all` does
170
+ // when extracting per-framework subsets.
171
+ const normalize = (s) => String(s).toLowerCase().replace(/[\s_-]/g, '');
172
+ const idNorm = normalize(id);
173
+ const frameworkGaps = relevantGaps.filter(([key, g]) => {
174
+ if (!g.framework) return false;
175
+ if (g.framework === id) return true;
176
+ if (normalize(g.framework).includes(idNorm)) return true;
177
+ if (normalize(key).startsWith(idNorm)) return true;
178
+ return false;
179
+ });
164
180
  frameworkResults[id] = {
165
181
  gap_count: frameworkGaps.length,
166
182
  gaps: frameworkGaps.map(([key, g]) => ({
@@ -122,9 +122,16 @@ function preflight(playbook, runOpts = {}) {
122
122
  for (const pc of meta.preconditions || []) {
123
123
  const submitted = runOpts.precondition_checks?.[pc.id];
124
124
  if (submitted === undefined) {
125
- issues.push({ kind: 'precondition_unverified', id: pc.id, check: pc.check, on_fail: pc.on_fail });
125
+ const submission_hint = `Submit precondition_checks in your evidence JSON, e.g. { "precondition_checks": { "${pc.id}": true } }. The runner lifts this into runOpts before the gate evaluates.`;
126
+ issues.push({ kind: 'precondition_unverified', id: pc.id, check: pc.check, on_fail: pc.on_fail, submission_hint });
126
127
  if (pc.on_fail === 'halt') {
127
- return { ok: false, blocked_by: 'precondition', reason: `Precondition ${pc.id} (${pc.check}) not verified by host AI; on_fail=halt.`, issues };
128
+ return {
129
+ ok: false,
130
+ blocked_by: 'precondition',
131
+ reason: `Precondition ${pc.id} (${pc.check}) not verified by host AI; on_fail=halt.`,
132
+ remediation: submission_hint,
133
+ issues
134
+ };
128
135
  }
129
136
  continue;
130
137
  }
@@ -199,6 +206,21 @@ function look(playbookId, directiveId, runOpts = {}) {
199
206
  playbook_id: playbookId,
200
207
  directive_id: directiveId,
201
208
  air_gap_mode: airGap,
209
+ // Preconditions are surfaced here so the host AI can verify them with its
210
+ // own probes (Bash:test -f /proc/version, etc.) and declare the results
211
+ // back through submission.precondition_checks. Without this list, the AI
212
+ // is blind to the gate and run() will halt with a precondition_unverified
213
+ // failure the AI can't diagnose. See AGENTS.md Hard Rule context.
214
+ preconditions: (playbook._meta.preconditions || []).map(pc => ({
215
+ id: pc.id,
216
+ description: pc.description,
217
+ check: pc.check,
218
+ on_fail: pc.on_fail
219
+ })),
220
+ precondition_submission_shape: {
221
+ hint: 'Include precondition_checks: { "<precondition-id>": true|false } in your submission JSON. The runner lifts it into runOpts before evaluating the gate.',
222
+ example: { precondition_checks: { 'linux-platform': true, 'uname-available': true } }
223
+ },
202
224
  artifacts: (l.artifacts || []).map(a => ({
203
225
  ...a,
204
226
  // Surface the air-gap alternative as the primary source when air_gap_mode
@@ -259,8 +281,19 @@ function detect(playbookId, directiveId, agentSubmission = {}) {
259
281
  const hasDeterministicHit = hits.some(r => r.deterministic);
260
282
  const hasHighConfHit = hits.some(r => r.confidence === 'high' || r.confidence === 'deterministic');
261
283
 
284
+ // Agent override: if signals.detection_classification is explicitly set to
285
+ // one of the four legal values, honor it. Engine computes its own
286
+ // classification as a fallback. Use the override when the agent has run the
287
+ // full false_positive_profile checks and reached an explicit verdict —
288
+ // engine-computed classification can't represent "I saw the indicators and
289
+ // confirmed they're all benign" without this override.
290
+ const override = (agentSubmission.signals && agentSubmission.signals.detection_classification);
291
+ const validOverrides = new Set(['detected', 'inconclusive', 'not_detected', 'clean']);
292
+
262
293
  let classification;
263
- if (hasDeterministicHit || hasHighConfHit) {
294
+ if (override && validOverrides.has(override)) {
295
+ classification = override === 'clean' ? 'not_detected' : override;
296
+ } else if (hasDeterministicHit || hasHighConfHit) {
264
297
  classification = 'detected';
265
298
  } else if (hits.length === 0 && indicatorResults.every(r => r.verdict === 'miss')) {
266
299
  classification = 'not_detected';
@@ -293,10 +326,20 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}) {
293
326
 
294
327
  // Match catalogued CVEs from the domain.cve_refs list. The agent submits
295
328
  // signal values; engine joins to the catalog for RWEP context.
329
+ // VEX filter (agentSignals.vex_filter): a set of CVE IDs the operator
330
+ // has formally declared not_affected via a CycloneDX/OpenVEX statement.
331
+ // We drop those from matched_cves before scoring, and surface them
332
+ // separately so the analyze response still records the disposition.
296
333
  const cveRefs = playbook.domain.cve_refs || [];
297
- const matchedCves = cveRefs
298
- .map(id => xref.byCve(id))
299
- .filter(r => r.found);
334
+ const vexFilter = agentSignals.vex_filter instanceof Set ? agentSignals.vex_filter
335
+ : (Array.isArray(agentSignals.vex_filter) ? new Set(agentSignals.vex_filter) : null);
336
+ const allMatches = cveRefs.map(id => xref.byCve(id)).filter(r => r.found);
337
+ const matchedCves = vexFilter
338
+ ? allMatches.filter(c => !vexFilter.has(c.cve_id))
339
+ : allMatches;
340
+ const vexDropped = vexFilter
341
+ ? allMatches.filter(c => vexFilter.has(c.cve_id)).map(c => c.cve_id)
342
+ : [];
300
343
 
301
344
  // RWEP composition: start from the catalogue's per-CVE rwep_score (already
302
345
  // baked from KEV + PoC + AI-disc + active-exploitation + blast-radius), then
@@ -321,8 +364,21 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}) {
321
364
  const blastRadiusScore = agentSignals.blast_radius_score || (blastRubric[0]?.blast_radius_score ?? null);
322
365
 
323
366
  // compliance_theater_check — engine surfaces the test; agent runs it; we
324
- // accept the verdict in agentSignals.theater_verdict.
325
- const theaterVerdict = agentSignals.theater_verdict || (an.compliance_theater_check ? 'pending_agent_run' : null);
367
+ // accept the verdict in agentSignals.theater_verdict. When agent didn't
368
+ // submit a verdict but the detect phase reached a clear classification,
369
+ // derive one rather than leaving the field stuck in 'pending_agent_run':
370
+ // detect.classification = not_detected → theater_verdict = clear
371
+ // detect.classification = detected → theater_verdict = pending_agent_run
372
+ // (agent still must run reality_test)
373
+ // detect.classification = inconclusive → theater_verdict = pending_agent_run
374
+ // Aliases 'clean' / 'no_theater' map to 'clear' for ergonomics.
375
+ let theaterVerdict = agentSignals.theater_verdict;
376
+ if (theaterVerdict === 'clean' || theaterVerdict === 'no_theater') theaterVerdict = 'clear';
377
+ if (!theaterVerdict && an.compliance_theater_check) {
378
+ const cls = detectResult.classification;
379
+ theaterVerdict = cls === 'not_detected' ? 'clear' : 'pending_agent_run';
380
+ }
381
+ theaterVerdict = theaterVerdict || (an.compliance_theater_check ? 'pending_agent_run' : null);
326
382
 
327
383
  // framework_gap_mapping — engine emits the mapping verbatim; analyze does
328
384
  // not compute new gaps here, just attaches the playbook-declared ones.
@@ -340,9 +396,31 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}) {
340
396
  phase: 'analyze',
341
397
  playbook_id: playbookId,
342
398
  directive_id: directiveId,
399
+ // Hard Rule #1 (AGENTS.md): every CVE reference must carry CVSS + KEV +
400
+ // PoC + AI-discovery + active-exploitation + patch/live-patch availability.
401
+ // Pull every required field from the catalog entry; null is only emitted
402
+ // when the catalog itself lacks the value, never when we just forgot to
403
+ // forward it. EPSS is included because validate-cves --live populates it.
343
404
  matched_cves: matchedCves.map(c => ({
344
- cve_id: c.cve_id, rwep: c.rwep_score, cisa_kev: c.cisa_kev,
345
- active_exploitation: c.active_exploitation, ai_discovered: c.ai_discovered
405
+ cve_id: c.cve_id,
406
+ rwep: c.rwep_score,
407
+ cvss_score: c.entry?.cvss_score ?? null,
408
+ cvss_vector: c.entry?.cvss_vector ?? null,
409
+ cisa_kev: c.cisa_kev,
410
+ cisa_kev_date: c.entry?.cisa_kev_date ?? null,
411
+ cisa_kev_due_date: c.entry?.cisa_kev_due_date ?? null,
412
+ poc_available: c.entry?.poc_available ?? null,
413
+ ai_discovered: c.ai_discovered,
414
+ ai_assisted_weaponization: c.entry?.ai_assisted_weaponization ?? null,
415
+ active_exploitation: c.active_exploitation,
416
+ patch_available: c.entry?.patch_available ?? null,
417
+ patch_required_reboot: c.entry?.patch_required_reboot ?? null,
418
+ live_patch_available: c.entry?.live_patch_available ?? null,
419
+ epss_score: c.entry?.epss_score ?? null,
420
+ epss_date: c.entry?.epss_date ?? null,
421
+ atlas_refs: c.atlas_refs,
422
+ attack_refs: c.attack_refs,
423
+ affected_versions: c.entry?.affected_versions ?? null,
346
424
  })),
347
425
  rwep: { base: baseRwep, adjusted: adjustedRwep, breakdown: rwepBreakdown, threshold: directive ? resolvedPhase(playbook, directiveId, 'direct').rwep_threshold : null },
348
426
  blast_radius_score: blastRadiusScore,
@@ -355,10 +433,44 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}) {
355
433
  verdict_text: theaterVerdict === 'theater' ? an.compliance_theater_check?.theater_verdict_if_gap : null
356
434
  },
357
435
  framework_gap_mapping: frameworkGaps,
358
- escalations
436
+ escalations,
437
+ vex: vexFilter ? {
438
+ filter_applied: true,
439
+ dropped_cve_count: vexDropped.length,
440
+ dropped_cves: vexDropped,
441
+ note: vexDropped.length
442
+ ? `${vexDropped.length} CVE(s) dropped from analyze because the operator-supplied VEX statement marks them not_affected / resolved / false_positive. They remain in cve-catalog.json; the disposition lives in the VEX file.`
443
+ : "VEX filter supplied; zero matches dropped (no CVEs in domain.cve_refs matched the VEX not-affected set)."
444
+ } : null
359
445
  };
360
446
  }
361
447
 
448
+ /**
449
+ * Extract a set of "not affected" CVE IDs from a VEX document. Supports
450
+ * CycloneDX VEX (analysis.state in {not_affected, resolved, false_positive})
451
+ * and OpenVEX (statements[].status === "not_affected"). Returns a Set<string>.
452
+ */
453
+ function vexFilterFromDoc(doc) {
454
+ const out = new Set();
455
+ if (!doc || typeof doc !== 'object') return out;
456
+
457
+ // CycloneDX shape
458
+ for (const v of (doc.vulnerabilities || [])) {
459
+ const state = v.analysis && v.analysis.state;
460
+ if (state === 'not_affected' || state === 'resolved' || state === 'false_positive') {
461
+ if (v.id) out.add(v.id);
462
+ }
463
+ }
464
+ // OpenVEX shape
465
+ for (const s of (doc.statements || [])) {
466
+ if (s.status === 'not_affected' || s.status === 'fixed') {
467
+ const id = s.vulnerability && (s.vulnerability['@id'] || s.vulnerability.name || s.vulnerability);
468
+ if (typeof id === 'string') out.add(id);
469
+ }
470
+ }
471
+ return out;
472
+ }
473
+
362
474
  // --- phase 6: validate ---
363
475
 
364
476
  function validate(playbookId, directiveId, analyzeResult, agentSignals = {}) {
@@ -797,7 +909,9 @@ function plan(opts = {}) {
797
909
  return {
798
910
  id,
799
911
  domain: pb.domain,
912
+ scope: pb._meta.scope || null,
800
913
  threat_currency_score: pb._meta.threat_currency_score,
914
+ air_gap_mode: !!pb._meta.air_gap_mode,
801
915
  directives: pb.directives.map(d => ({ id: d.id, title: d.title, applies_to: d.applies_to }))
802
916
  };
803
917
  })
@@ -817,6 +931,7 @@ module.exports = {
817
931
  validate,
818
932
  close,
819
933
  run,
934
+ vexFilterFromDoc,
820
935
  // internal helpers exposed for tests
821
936
  _resolvedPhase: resolvedPhase,
822
937
  _deepMerge: deepMerge,
@@ -36,6 +36,11 @@
36
36
  },
37
37
  "owner": { "type": "string", "description": "GitHub handle or team responsible for currency." },
38
38
  "air_gap_mode": { "type": "boolean", "description": "When true, all artifact collection and lookups use offline fallbacks. No network." },
39
+ "scope": {
40
+ "type": "string",
41
+ "description": "Primary investigation scope. Drives CLI auto-detect + --scope filtering. system = host/kernel/packages/sysctl; code = repo walk / lockfiles / IaC; service = network APIs / MCP / AI providers; cross-cutting = pure-analyze correlation layer.",
42
+ "enum": ["system", "code", "service", "cross-cutting"]
43
+ },
39
44
  "preconditions": {
40
45
  "type": "array",
41
46
  "description": "Must hold before the playbook runs.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_comment": "Auto-generated by scripts/refresh-manifest-snapshot.js — do not hand-edit. Public skill surface used by check-manifest-snapshot.js to detect breaking removals.",
3
- "_generated_at": "2026-05-12T05:39:54.946Z",
3
+ "_generated_at": "2026-05-12T13:48:57.281Z",
4
4
  "atlas_version": "5.1.0",
5
5
  "skill_count": 38,
6
6
  "skills": [
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceptd-security",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation",
5
5
  "homepage": "https://exceptd.com",
6
6
  "license": "Apache-2.0",
@@ -52,7 +52,7 @@
52
52
  ],
53
53
  "last_threat_review": "2026-05-01",
54
54
  "signature": "WprHkO1KOjQtCBj6/EJghBTNyNKJhn7O2HDbAQZPi5jn4flwHpSrtP8LC15a4Unoh+xiIIgGhvTHZIQFHGMpBQ==",
55
- "signed_at": "2026-05-12T05:39:54.542Z",
55
+ "signed_at": "2026-05-12T13:48:56.849Z",
56
56
  "cwe_refs": [
57
57
  "CWE-125",
58
58
  "CWE-362",
@@ -116,7 +116,7 @@
116
116
  ],
117
117
  "last_threat_review": "2026-05-01",
118
118
  "signature": "fg20bOXGRkPUdLmegeXpTM4hnzl/ArgcVc88rItZN5DdsnFnzPgUU1PwCI82zooyj2GfxJHYjxNkq5qd2zNPBg==",
119
- "signed_at": "2026-05-12T05:39:54.544Z",
119
+ "signed_at": "2026-05-12T13:48:56.851Z",
120
120
  "cwe_refs": [
121
121
  "CWE-1039",
122
122
  "CWE-1426",
@@ -179,7 +179,7 @@
179
179
  ],
180
180
  "last_threat_review": "2026-05-01",
181
181
  "signature": "6JuSzkSSFzFHEZ3ANzqjtIbKPOkwJeKhQ+8WAPB4+dTRvDSeg46n3D88XfGaNd2z7pmg/i8p9ZoImQcHFS4BCg==",
182
- "signed_at": "2026-05-12T05:39:54.544Z",
182
+ "signed_at": "2026-05-12T13:48:56.851Z",
183
183
  "cwe_refs": [
184
184
  "CWE-22",
185
185
  "CWE-345",
@@ -225,7 +225,7 @@
225
225
  "framework_gaps": [],
226
226
  "last_threat_review": "2026-05-01",
227
227
  "signature": "PYSw9abiYfW+y7IkY8udJG5LSds2a4rMimlw3rrdD0zE3vunEeV/y7oTmDD4o83OqHSCKNzF/7vMhvd/noqICQ==",
228
- "signed_at": "2026-05-12T05:39:54.545Z"
228
+ "signed_at": "2026-05-12T13:48:56.851Z"
229
229
  },
230
230
  {
231
231
  "name": "compliance-theater",
@@ -256,7 +256,7 @@
256
256
  ],
257
257
  "last_threat_review": "2026-05-01",
258
258
  "signature": "BMFmmJYP3HsHIjUqnhw8E3MiMGZJsI/eDq51we+nxUicZ8nFUQT9DhmRntAqOs6BUnsfiQNNLc/rrsNh8yg1CQ==",
259
- "signed_at": "2026-05-12T05:39:54.545Z"
259
+ "signed_at": "2026-05-12T13:48:56.852Z"
260
260
  },
261
261
  {
262
262
  "name": "exploit-scoring",
@@ -285,7 +285,7 @@
285
285
  ],
286
286
  "last_threat_review": "2026-05-01",
287
287
  "signature": "VGPyDwy5BRlpn1lZthhPB6ytb4ZcU2j0KtCZbaMkyLdMugQJtK2yEuwrsDH4yEtAhTB6/A4B3eSygJckum49Ag==",
288
- "signed_at": "2026-05-12T05:39:54.546Z"
288
+ "signed_at": "2026-05-12T13:48:56.853Z"
289
289
  },
290
290
  {
291
291
  "name": "rag-pipeline-security",
@@ -322,7 +322,7 @@
322
322
  ],
323
323
  "last_threat_review": "2026-05-01",
324
324
  "signature": "XkFGpsNnXBVslkQ48usEu9l1LjPiV2ppW+M4B63zXFBP2Puh52qYCffEPjUHYhoO5bjgTM7yCbK8XF/Dzk5wBw==",
325
- "signed_at": "2026-05-12T05:39:54.546Z",
325
+ "signed_at": "2026-05-12T13:48:56.853Z",
326
326
  "cwe_refs": [
327
327
  "CWE-1395",
328
328
  "CWE-1426"
@@ -379,7 +379,7 @@
379
379
  ],
380
380
  "last_threat_review": "2026-05-01",
381
381
  "signature": "1Xqy7Kxxy6GpTvuYJPdllPzVDRFxb7N6AuxKuoaO4v91CiZLmiXt0sTIWImKJ3p9Eup6rJNDdsY71dolFhHNBA==",
382
- "signed_at": "2026-05-12T05:39:54.546Z",
382
+ "signed_at": "2026-05-12T13:48:56.853Z",
383
383
  "d3fend_refs": [
384
384
  "D3-CA",
385
385
  "D3-CSPP",
@@ -414,7 +414,7 @@
414
414
  "framework_gaps": [],
415
415
  "last_threat_review": "2026-05-01",
416
416
  "signature": "QNLOmAL54S/Cmk4cdO4L2BCGkqZ/FgY4UBsKWtg/EEW+YXF5ev+a8XsUT8q5veuUa2VYcYna7rD1iAnE+2PDBA==",
417
- "signed_at": "2026-05-12T05:39:54.546Z",
417
+ "signed_at": "2026-05-12T13:48:56.853Z",
418
418
  "cwe_refs": [
419
419
  "CWE-1188"
420
420
  ]
@@ -442,7 +442,7 @@
442
442
  "framework_gaps": [],
443
443
  "last_threat_review": "2026-05-01",
444
444
  "signature": "aFHq4cSl3CKchnVITxx+BrAEWD33WtFFJoQtwAug5g9R3/3ABtjaXYGVQaZcdcG1AIZkMoGSPywgLQWDY7ZDCw==",
445
- "signed_at": "2026-05-12T05:39:54.547Z"
445
+ "signed_at": "2026-05-12T13:48:56.854Z"
446
446
  },
447
447
  {
448
448
  "name": "global-grc",
@@ -474,7 +474,7 @@
474
474
  "framework_gaps": [],
475
475
  "last_threat_review": "2026-05-01",
476
476
  "signature": "viCTUWdy6euvd2KTAo6sLvarK/FZkDtYGocxBt0H+fY94kLQGW8K5cSpqIWdUF5NUytSHBCiG4YcSze8P9Z/BQ==",
477
- "signed_at": "2026-05-12T05:39:54.547Z"
477
+ "signed_at": "2026-05-12T13:48:56.854Z"
478
478
  },
479
479
  {
480
480
  "name": "zeroday-gap-learn",
@@ -501,7 +501,7 @@
501
501
  "framework_gaps": [],
502
502
  "last_threat_review": "2026-05-01",
503
503
  "signature": "6PkUaHQi3Hxuqq/Jp4GYckvfqVEofmeT87NUH0T+pwyjlc+xZkoqNPn65f7ldciEPL86JIPi3/dDTKQbIFFBCw==",
504
- "signed_at": "2026-05-12T05:39:54.547Z"
504
+ "signed_at": "2026-05-12T13:48:56.854Z"
505
505
  },
506
506
  {
507
507
  "name": "pqc-first",
@@ -553,7 +553,7 @@
553
553
  ],
554
554
  "last_threat_review": "2026-05-01",
555
555
  "signature": "ZenFTEzWx+DzrSXlNXhbZ70vOdJSXfrnKkAwqMlBf5nlDf38V1/hG4XCKj43snQXWr4mVJOX6ilqFLTYNIjnBw==",
556
- "signed_at": "2026-05-12T05:39:54.548Z",
556
+ "signed_at": "2026-05-12T13:48:56.855Z",
557
557
  "cwe_refs": [
558
558
  "CWE-327"
559
559
  ],
@@ -600,7 +600,7 @@
600
600
  ],
601
601
  "last_threat_review": "2026-05-01",
602
602
  "signature": "ih0vpd2v2zS31JSJv7SnABoya8JlJdrXZXx4rBnrsV3Assj+dbjAP0pQ1HMT/5RX8yTTswRQsg0bJV3qmbJ3Bw==",
603
- "signed_at": "2026-05-12T05:39:54.548Z"
603
+ "signed_at": "2026-05-12T13:48:56.855Z"
604
604
  },
605
605
  {
606
606
  "name": "security-maturity-tiers",
@@ -637,7 +637,7 @@
637
637
  ],
638
638
  "last_threat_review": "2026-05-01",
639
639
  "signature": "Lv8dHiwIqUbNsywCCB/+pYWGF+MHCvxVn1IAvR7Cnif5fy0sICv0N4SVsSb621qAAkHNshpfxqwuhbuQnE1TBA==",
640
- "signed_at": "2026-05-12T05:39:54.549Z",
640
+ "signed_at": "2026-05-12T13:48:56.856Z",
641
641
  "cwe_refs": [
642
642
  "CWE-1188"
643
643
  ]
@@ -672,7 +672,7 @@
672
672
  "framework_gaps": [],
673
673
  "last_threat_review": "2026-05-11",
674
674
  "signature": "BS+wrL28HHYhBpe+v84VLoq9KPBXu6alfG968katfGIoLNYQueaHP931bRmlkrjfeb6qbDf067GWdPEh7nroAw==",
675
- "signed_at": "2026-05-12T05:39:54.549Z"
675
+ "signed_at": "2026-05-12T13:48:56.856Z"
676
676
  },
677
677
  {
678
678
  "name": "attack-surface-pentest",
@@ -743,7 +743,7 @@
743
743
  "PTES revision incorporating AI-surface enumeration"
744
744
  ],
745
745
  "signature": "vLhIYT/CC3IzxMRa+UPeqGSZTvthuwUeTMGNFMm37+TaEk0TtfwPrPyrBJLHw4W6Wt7+pufjHs46X3nTgzoRAg==",
746
- "signed_at": "2026-05-12T05:39:54.549Z"
746
+ "signed_at": "2026-05-12T13:48:56.856Z"
747
747
  },
748
748
  {
749
749
  "name": "fuzz-testing-strategy",
@@ -803,7 +803,7 @@
803
803
  "OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
804
804
  ],
805
805
  "signature": "TOcQLy/427cuf0Lw90J7A0oIeuhUmf9NXb6tOUS5K3SazCKTJujPgYSVAPZOYf1zZrRAY/aq0iqELd5cLyk5DA==",
806
- "signed_at": "2026-05-12T05:39:54.549Z"
806
+ "signed_at": "2026-05-12T13:48:56.857Z"
807
807
  },
808
808
  {
809
809
  "name": "dlp-gap-analysis",
@@ -878,7 +878,7 @@
878
878
  "Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
879
879
  ],
880
880
  "signature": "u4IN7escQa5V+OgdtaJXLdvhmNiGZsdmGOvebTLZ30WoImT+WiksvaqSa0POGdbr6HzFkALe2RrZEH9Tr0U6Dg==",
881
- "signed_at": "2026-05-12T05:39:54.550Z"
881
+ "signed_at": "2026-05-12T13:48:56.857Z"
882
882
  },
883
883
  {
884
884
  "name": "supply-chain-integrity",
@@ -955,7 +955,7 @@
955
955
  "OpenSSF model-signing — emerging Sigstore-based signing standard for ML model weights; track for production adoption"
956
956
  ],
957
957
  "signature": "eTGQJ3gnG24WggfwuFNNIFOWV/ttPxTa3pvx9OH28m5KDS1a4ZmOR7K8y01wk/su8bH0ClYYRfoBfKQOtRswAg==",
958
- "signed_at": "2026-05-12T05:39:54.550Z"
958
+ "signed_at": "2026-05-12T13:48:56.857Z"
959
959
  },
960
960
  {
961
961
  "name": "defensive-countermeasure-mapping",
@@ -1012,7 +1012,7 @@
1012
1012
  ],
1013
1013
  "last_threat_review": "2026-05-11",
1014
1014
  "signature": "q7gFLPoqf/8bqATR6gt/nj0EoyUOlfzi+bZ0bT3pC9KW7O6M/ji9fT+AXSGNp6PKd+70ACb3mkMGmWgjLpQXCg==",
1015
- "signed_at": "2026-05-12T05:39:54.550Z"
1015
+ "signed_at": "2026-05-12T13:48:56.857Z"
1016
1016
  },
1017
1017
  {
1018
1018
  "name": "identity-assurance",
@@ -1079,7 +1079,7 @@
1079
1079
  "d3fend_refs": [],
1080
1080
  "last_threat_review": "2026-05-11",
1081
1081
  "signature": "pX8rhrrzuyG3iRrPORLqTZAjzGdWK/bKPUGJG5WHSZcv4LB0kQXOit4sHG0exdXxI6HY8jyX67QY4r5vEHHACw==",
1082
- "signed_at": "2026-05-12T05:39:54.550Z"
1082
+ "signed_at": "2026-05-12T13:48:56.858Z"
1083
1083
  },
1084
1084
  {
1085
1085
  "name": "ot-ics-security",
@@ -1135,7 +1135,7 @@
1135
1135
  "d3fend_refs": [],
1136
1136
  "last_threat_review": "2026-05-11",
1137
1137
  "signature": "ypb8kNZQRdyu5mWeveB7sjCjNKXS1yXvjDJv88muzwhOs/a4Fu/Gb532js5NKyy+eCw/emrphpTZaL8R9a2lBA==",
1138
- "signed_at": "2026-05-12T05:39:54.551Z"
1138
+ "signed_at": "2026-05-12T13:48:56.858Z"
1139
1139
  },
1140
1140
  {
1141
1141
  "name": "coordinated-vuln-disclosure",
@@ -1187,7 +1187,7 @@
1187
1187
  "NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
1188
1188
  ],
1189
1189
  "signature": "346Lt+277ycRNsyAOGwLSONi4awgxKy3hP9G+BWjwaa8ySmTeqbYsbyyhtxjeohk9bV2SF+Hl2q4JdSvc/2qCQ==",
1190
- "signed_at": "2026-05-12T05:39:54.551Z"
1190
+ "signed_at": "2026-05-12T13:48:56.858Z"
1191
1191
  },
1192
1192
  {
1193
1193
  "name": "threat-modeling-methodology",
@@ -1237,7 +1237,7 @@
1237
1237
  "PASTA v2 updates incorporating AI/ML application threats"
1238
1238
  ],
1239
1239
  "signature": "ewTvG5vu3ngFHyXgBur5vSKDFQsOZx0x79djGMricl7LCvQf5//OG6LZKXa+AOuEq58prRS+HgzrFA1DiTfeCQ==",
1240
- "signed_at": "2026-05-12T05:39:54.551Z"
1240
+ "signed_at": "2026-05-12T13:48:56.859Z"
1241
1241
  },
1242
1242
  {
1243
1243
  "name": "webapp-security",
@@ -1311,7 +1311,7 @@
1311
1311
  "d3fend_refs": [],
1312
1312
  "last_threat_review": "2026-05-11",
1313
1313
  "signature": "ZHjbKu0Em92Kimr2esL1g93mf9TmcsChBhVEMWf/lFrjeLcg8nyHEIcDstIZ3FWYgc6MQNHnc3Rup3Xp/Za1Cw==",
1314
- "signed_at": "2026-05-12T05:39:54.552Z"
1314
+ "signed_at": "2026-05-12T13:48:56.859Z"
1315
1315
  },
1316
1316
  {
1317
1317
  "name": "ai-risk-management",
@@ -1361,7 +1361,7 @@
1361
1361
  "d3fend_refs": [],
1362
1362
  "last_threat_review": "2026-05-11",
1363
1363
  "signature": "1KRxjCbAX0Rs5NTOioi1w/f1SOzDQrtRoXjTDtzEwJ+d1QzFf9cqmBlp0uXmGpL0bzEaHWIctjigSychmoL2Dw==",
1364
- "signed_at": "2026-05-12T05:39:54.552Z"
1364
+ "signed_at": "2026-05-12T13:48:56.859Z"
1365
1365
  },
1366
1366
  {
1367
1367
  "name": "sector-healthcare",
@@ -1421,7 +1421,7 @@
1421
1421
  "d3fend_refs": [],
1422
1422
  "last_threat_review": "2026-05-11",
1423
1423
  "signature": "eiajFh7w7d4g+/crGalTtw9Qsu0deVsdHkdthZSy595ifGmgu0zaFD8usKThbPhOdUCCclTYkZYz5GalQmkhCw==",
1424
- "signed_at": "2026-05-12T05:39:54.552Z"
1424
+ "signed_at": "2026-05-12T13:48:56.860Z"
1425
1425
  },
1426
1426
  {
1427
1427
  "name": "sector-financial",
@@ -1502,7 +1502,7 @@
1502
1502
  "TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
1503
1503
  ],
1504
1504
  "signature": "iSZR/fYESQVyjkcqj+O+yzU0BQfaELH5s7WizzUTWvDPDTD2ZyOnZTT1r/Zfx2l4mbPmVeFGWdYnnVFTk/i3Aw==",
1505
- "signed_at": "2026-05-12T05:39:54.553Z"
1505
+ "signed_at": "2026-05-12T13:48:56.860Z"
1506
1506
  },
1507
1507
  {
1508
1508
  "name": "sector-federal-government",
@@ -1571,7 +1571,7 @@
1571
1571
  "Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
1572
1572
  ],
1573
1573
  "signature": "Wjdo5YXEL8XeNZkaEueG1DOUoyalstNPzQkxD/cwP5iMrJWg/Ly+sC0Oluuqm3aU7d63z55PrbGQCJD0XVZqBg==",
1574
- "signed_at": "2026-05-12T05:39:54.553Z"
1574
+ "signed_at": "2026-05-12T13:48:56.861Z"
1575
1575
  },
1576
1576
  {
1577
1577
  "name": "sector-energy",
@@ -1636,7 +1636,7 @@
1636
1636
  "ICS-CERT advisory feed (https://www.cisa.gov/news-events/cybersecurity-advisories/ics-advisories) for vendor CVEs in Siemens, Rockwell, Schneider Electric, ABB, GE Vernova, Hitachi Energy, AVEVA / OSIsoft PI"
1637
1637
  ],
1638
1638
  "signature": "c/l7dOHe0Zj6Ag3abUaEie6o0f8M4rhY5aPI9/wG4z6FDue9PzCVw8vUGoITFgg89g97lMfy2C3CE2PegQoFCw==",
1639
- "signed_at": "2026-05-12T05:39:54.553Z"
1639
+ "signed_at": "2026-05-12T13:48:56.861Z"
1640
1640
  },
1641
1641
  {
1642
1642
  "name": "api-security",
@@ -1705,7 +1705,7 @@
1705
1705
  "d3fend_refs": [],
1706
1706
  "last_threat_review": "2026-05-11",
1707
1707
  "signature": "9FgcJvYeo07QxQ+mnVRQk4jYLDMO/AVSXMs8cueO2f/qMOTQmrhBMVhj5ze7hzvXpGkp7EK/3Q1XKqde61JMAg==",
1708
- "signed_at": "2026-05-12T05:39:54.554Z"
1708
+ "signed_at": "2026-05-12T13:48:56.861Z"
1709
1709
  },
1710
1710
  {
1711
1711
  "name": "cloud-security",
@@ -1786,7 +1786,7 @@
1786
1786
  "CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
1787
1787
  ],
1788
1788
  "signature": "xRA0XZf7VPtuBtbsm41bay9yBLphw/hlL3YxIUrpko5g9ldM3oJe9o1qSwzIj/wSnQSI29qqPpNsnlks+HEOCA==",
1789
- "signed_at": "2026-05-12T05:39:54.554Z"
1789
+ "signed_at": "2026-05-12T13:48:56.862Z"
1790
1790
  },
1791
1791
  {
1792
1792
  "name": "container-runtime-security",
@@ -1848,7 +1848,7 @@
1848
1848
  "d3fend_refs": [],
1849
1849
  "last_threat_review": "2026-05-11",
1850
1850
  "signature": "GcU50DStuN1gU/Evm/sFRgeieQbqffVp12rgbGnasRX89Q7kM4ltFXB+bgCXHIvICzYb78hPIifWQb9UVupWBQ==",
1851
- "signed_at": "2026-05-12T05:39:54.554Z"
1851
+ "signed_at": "2026-05-12T13:48:56.862Z"
1852
1852
  },
1853
1853
  {
1854
1854
  "name": "mlops-security",
@@ -1919,7 +1919,7 @@
1919
1919
  "MITRE ATLAS v5.2 — track AML.T0010 sub-technique expansion and any new MLOps-pipeline-specific TTPs"
1920
1920
  ],
1921
1921
  "signature": "onIazpFoL1t4PMNRsoF06ggnl7BzCKjt0x+ZmVfWfyt1V06DgllsrbN3AAz4+g4jW2Sc71q0vIFKfwEUWpGVAQ==",
1922
- "signed_at": "2026-05-12T05:39:54.555Z"
1922
+ "signed_at": "2026-05-12T13:48:56.863Z"
1923
1923
  },
1924
1924
  {
1925
1925
  "name": "incident-response-playbook",
@@ -1981,7 +1981,7 @@
1981
1981
  "NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
1982
1982
  ],
1983
1983
  "signature": "P0Yv4CtqbnBNP6nSIxQUYYHL7T7ci+iE7iE2UXVfnMPeWVdKG2nvRePjBXc3JZTLima1Txn/I5ocDNhLTIeUAQ==",
1984
- "signed_at": "2026-05-12T05:39:54.555Z"
1984
+ "signed_at": "2026-05-12T13:48:56.863Z"
1985
1985
  },
1986
1986
  {
1987
1987
  "name": "email-security-anti-phishing",
@@ -2034,7 +2034,7 @@
2034
2034
  "d3fend_refs": [],
2035
2035
  "last_threat_review": "2026-05-11",
2036
2036
  "signature": "2pv81lLRbazpHqundCANb3YiLB4lkVsYctIDvI8rxSvHxhPS9jYXqmAoB5APSdDuOaew6XqpfZOehQUj9WmyBw==",
2037
- "signed_at": "2026-05-12T05:39:54.555Z"
2037
+ "signed_at": "2026-05-12T13:48:56.863Z"
2038
2038
  },
2039
2039
  {
2040
2040
  "name": "age-gates-child-safety",
@@ -2102,7 +2102,7 @@
2102
2102
  "US state adult-site age-verification laws — 19+ states by mid-2026 (TX HB 18 upheld by SCOTUS June 2025 in Free Speech Coalition v. Paxton); track ongoing challenges in remaining states"
2103
2103
  ],
2104
2104
  "signature": "BJ/YYnGVXeSBaR9oWAVrcNX7Wz+kE8R4CghX6+XEI/qY89fyrkKNNwo2veqqf49wffJhHVJ1wTp8ZDECjNp+Dw==",
2105
- "signed_at": "2026-05-12T05:39:54.556Z"
2105
+ "signed_at": "2026-05-12T13:48:56.864Z"
2106
2106
  }
2107
2107
  ]
2108
2108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
5
5
  "keywords": [
6
6
  "ai-security",
package/sbom.cdx.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:7ad6ee8c-6fb3-4eee-b37d-d9b770e964b6",
4
+ "serialNumber": "urn:uuid:2b2f295c-38c8-41a6-8821-59d2cf412800",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-05-12T05:38:55.099Z",
7
+ "timestamp": "2026-05-12T13:48:57.728Z",
8
8
  "tools": [
9
9
  {
10
10
  "name": "hand-written",
@@ -13,10 +13,10 @@
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.10.0",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.10.2",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.10.0",
19
+ "version": "0.10.2",
20
20
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
21
21
  "licenses": [
22
22
  {
@@ -25,11 +25,11 @@
25
25
  }
26
26
  }
27
27
  ],
28
- "purl": "pkg:npm/%40blamejs/exceptd-skills@0.10.0",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.10.2",
29
29
  "externalReferences": [
30
30
  {
31
31
  "type": "distribution",
32
- "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.10.0"
32
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.10.2"
33
33
  },
34
34
  {
35
35
  "type": "vcs",