@blamejs/exceptd-skills 0.12.6 → 0.12.8

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 (65) hide show
  1. package/AGENTS.md +14 -0
  2. package/CHANGELOG.md +97 -0
  3. package/bin/exceptd.js +189 -52
  4. package/data/_indexes/_meta.json +37 -37
  5. package/data/_indexes/activity-feed.json +26 -26
  6. package/data/_indexes/catalog-summaries.json +8 -8
  7. package/data/_indexes/chains.json +238 -0
  8. package/data/_indexes/frequency.json +63 -5
  9. package/data/_indexes/jurisdiction-map.json +13 -3
  10. package/data/_indexes/section-offsets.json +881 -845
  11. package/data/_indexes/summary-cards.json +2 -2
  12. package/data/_indexes/token-budget.json +145 -125
  13. package/data/atlas-ttps.json +189 -1
  14. package/data/cwe-catalog.json +290 -1
  15. package/data/d3fend-catalog.json +163 -1
  16. package/data/framework-control-gaps.json +243 -0
  17. package/data/playbooks/containers.json +23 -5
  18. package/data/playbooks/cred-stores.json +9 -9
  19. package/data/playbooks/crypto.json +8 -8
  20. package/data/playbooks/hardening.json +46 -10
  21. package/data/playbooks/library-author.json +16 -20
  22. package/data/playbooks/mcp.json +64 -1
  23. package/data/playbooks/runtime.json +7 -7
  24. package/data/playbooks/sbom.json +11 -11
  25. package/data/playbooks/secrets.json +4 -4
  26. package/data/rfc-references.json +144 -0
  27. package/lib/refresh-external.js +25 -5
  28. package/lib/schemas/skill-frontmatter.schema.json +2 -2
  29. package/manifest-snapshot.json +1 -1
  30. package/manifest.json +67 -67
  31. package/package.json +2 -1
  32. package/sbom.cdx.json +6 -6
  33. package/scripts/check-sbom-currency.js +87 -0
  34. package/scripts/check-test-coverage.README.md +148 -0
  35. package/scripts/check-test-coverage.js +455 -0
  36. package/scripts/hooks/pre-commit.sh +19 -0
  37. package/scripts/predeploy.js +16 -30
  38. package/skills/age-gates-child-safety/skill.md +3 -0
  39. package/skills/ai-attack-surface/skill.md +4 -1
  40. package/skills/ai-c2-detection/skill.md +6 -1
  41. package/skills/ai-risk-management/skill.md +3 -0
  42. package/skills/api-security/skill.md +3 -0
  43. package/skills/attack-surface-pentest/skill.md +3 -0
  44. package/skills/cloud-security/skill.md +3 -0
  45. package/skills/container-runtime-security/skill.md +3 -0
  46. package/skills/coordinated-vuln-disclosure/skill.md +8 -1
  47. package/skills/defensive-countermeasure-mapping/skill.md +1 -1
  48. package/skills/dlp-gap-analysis/skill.md +3 -0
  49. package/skills/email-security-anti-phishing/skill.md +9 -1
  50. package/skills/identity-assurance/skill.md +6 -1
  51. package/skills/incident-response-playbook/skill.md +8 -2
  52. package/skills/kernel-lpe-triage/skill.md +24 -4
  53. package/skills/mcp-agent-trust/skill.md +4 -1
  54. package/skills/mlops-security/skill.md +3 -0
  55. package/skills/ot-ics-security/skill.md +3 -0
  56. package/skills/rag-pipeline-security/skill.md +3 -0
  57. package/skills/sector-energy/skill.md +3 -0
  58. package/skills/sector-federal-government/skill.md +3 -0
  59. package/skills/sector-financial/skill.md +3 -0
  60. package/skills/sector-healthcare/skill.md +3 -0
  61. package/skills/security-maturity-tiers/skill.md +19 -1
  62. package/skills/skill-update-loop/skill.md +32 -0
  63. package/skills/supply-chain-integrity/skill.md +3 -0
  64. package/skills/threat-modeling-methodology/skill.md +3 -0
  65. package/skills/webapp-security/skill.md +3 -0
@@ -376,7 +376,7 @@
376
376
  {
377
377
  "id": "aws-access-key-id",
378
378
  "type": "log_pattern",
379
- "value": "AKIA[0-9A-Z]{16}",
379
+ "value": "Within the secret-regex-scan-text-files artifact, bounded by the repo-tree scope and applied to env-files, auth-config-files, and iac-credential-bearers content: regex AKIA[0-9A-Z]{16}",
380
380
  "description": "AWS Access Key ID. Long-lived IAM user credential. Scraper-bot priority target.",
381
381
  "confidence": "deterministic",
382
382
  "deterministic": true,
@@ -448,7 +448,7 @@
448
448
  {
449
449
  "id": "ssh-private-key-block",
450
450
  "type": "log_pattern",
451
- "value": "-----BEGIN ((RSA|EC|DSA|OPENSSH|ENCRYPTED) )?PRIVATE KEY-----",
451
+ "value": "Across the union of secret-regex-scan-text-files (text-file content) and ssh-private-keys (file inventory) artifacts: regex `-----BEGIN ((RSA|EC|DSA|OPENSSH|ENCRYPTED) )?PRIVATE KEY-----` matches, OR the ssh-private-keys artifact lists any private-key file under the repo tree (committed key material is always a finding regardless of regex hit on the surrounding text)",
452
452
  "description": "Inline SSH/PEM private key material in a text file.",
453
453
  "confidence": "deterministic",
454
454
  "deterministic": true,
@@ -475,7 +475,7 @@
475
475
  {
476
476
  "id": "world-writable-env-file",
477
477
  "type": "file_path",
478
- "value": ".env / .env.* / .envrc with mode 0666 or 0664 (group/world writable)",
478
+ "value": "Within the world-writable-secret-files artifact, restricted to entries from the env-files artifact: any .env / .env.* / .envrc with mode 0666 or 0664 (group/world writable)",
479
479
  "description": "Env file writable by group or world. Tampering primitive.",
480
480
  "confidence": "deterministic",
481
481
  "deterministic": true,
@@ -484,7 +484,7 @@
484
484
  {
485
485
  "id": "ssh-key-bad-perms",
486
486
  "type": "file_path",
487
- "value": "~/.ssh/id_* file with mode != 0600",
487
+ "value": "Within the world-writable-secret-files artifact, restricted to entries from the ssh-private-keys artifact and ~/.ssh/id_* paths: any private-key file with mode != 0600",
488
488
  "description": "SSH private key with permissive permissions. ssh-agent refuses to load; user often chmods to 0644 or 0666 'to make it work'.",
489
489
  "confidence": "deterministic",
490
490
  "deterministic": true,
@@ -320,5 +320,149 @@
320
320
  "pqc-first"
321
321
  ],
322
322
  "last_verified": "2026-05-11"
323
+ },
324
+ "RFC-7489": {
325
+ "number": 7489,
326
+ "title": "Domain-based Message Authentication, Reporting, and Conformance (DMARC)",
327
+ "status": "Informational",
328
+ "published": "2015-03",
329
+ "tracker": "https://www.rfc-editor.org/info/rfc7489",
330
+ "relevance": "Defines DMARC — the email-authentication policy framework that binds SPF + DKIM results to a published domain-owner policy. Operator-facing email security depends on a published DMARC record with `p=reject` or `p=quarantine`; relaxed policies (`p=none`) are equivalent to no DMARC for spoofing-defense purposes. Cited alongside DKIM (RFC 6376) and SPF (RFC 7208) as the authoritative tri-RFC for anti-spoofing posture.",
331
+ "skills_referencing": [
332
+ "email-security-anti-phishing"
333
+ ],
334
+ "last_verified": "2026-05-13"
335
+ },
336
+ "RFC-6376": {
337
+ "number": 6376,
338
+ "title": "DomainKeys Identified Mail (DKIM) Signatures",
339
+ "status": "Internet Standard",
340
+ "published": "2011-09",
341
+ "tracker": "https://www.rfc-editor.org/info/rfc6376",
342
+ "relevance": "Cryptographic signing of email by the originating domain. DMARC verification relies on either SPF or DKIM aligning with the From-header domain. Operationally, DKIM is the load-bearing half of DMARC — SPF breaks on legitimate forwarding paths; DKIM survives them. Key-length lag is the recurring framework gap: many deployments still use 1024-bit RSA keys despite IETF guidance to rotate to ≥2048-bit.",
343
+ "skills_referencing": [
344
+ "email-security-anti-phishing"
345
+ ],
346
+ "last_verified": "2026-05-13"
347
+ },
348
+ "RFC-7208": {
349
+ "number": 7208,
350
+ "title": "Sender Policy Framework (SPF) for Authorizing Use of Domains in Email",
351
+ "status": "Proposed Standard",
352
+ "published": "2014-04",
353
+ "tracker": "https://www.rfc-editor.org/info/rfc7208",
354
+ "relevance": "Authorizes which IPs may send mail on behalf of a domain. DMARC's path-based authentication half. Limits: a strict 10-DNS-lookup ceiling that operators routinely blow past through nested includes, leading to permerror DMARC outcomes; SPF breaks across legitimate forwarders, which is why DKIM (RFC 6376) is the load-bearing half operationally.",
355
+ "skills_referencing": [
356
+ "email-security-anti-phishing"
357
+ ],
358
+ "last_verified": "2026-05-13"
359
+ },
360
+ "RFC-8616": {
361
+ "number": 8616,
362
+ "title": "Email Authentication for Internationalized Mail",
363
+ "status": "Proposed Standard",
364
+ "published": "2019-06",
365
+ "tracker": "https://www.rfc-editor.org/info/rfc8616",
366
+ "relevance": "Updates DKIM / SPF / DMARC handling for internationalized domain names (IDN) and internationalized email-address local parts. Operator-facing: phishing campaigns increasingly leverage IDN homoglyphs (e.g. Cyrillic `а` for Latin `a`), and a DMARC implementation that doesn't honor RFC 8616 normalization rules can fail to align IDN-bearing From-headers correctly. Treat as a compatibility prerequisite for any anti-spoofing posture covering global mail flows.",
367
+ "skills_referencing": [
368
+ "email-security-anti-phishing"
369
+ ],
370
+ "last_verified": "2026-05-13"
371
+ },
372
+ "RFC-8461": {
373
+ "number": 8461,
374
+ "title": "SMTP MTA Strict Transport Security (MTA-STS)",
375
+ "status": "Proposed Standard",
376
+ "published": "2018-09",
377
+ "tracker": "https://www.rfc-editor.org/info/rfc8461",
378
+ "relevance": "Forces TLS on inbound SMTP between MTAs that publish a `_mta-sts` policy. Closes the historical 'opportunistic-TLS-downgrade' attack window where a network-positioned attacker stripped the STARTTLS handshake. Operator-facing: an MTA-STS policy in `enforce` mode plus monitoring via TLSRPT (RFC 8460) is the baseline for inbound email confidentiality. Phishing-defense relevance is indirect — STARTTLS stripping enables session-layer manipulation that downstream defenses (DMARC, content classifiers) cannot recover from.",
379
+ "skills_referencing": [
380
+ "email-security-anti-phishing"
381
+ ],
382
+ "last_verified": "2026-05-13"
383
+ },
384
+ "ISO-29147": {
385
+ "number": null,
386
+ "title": "ISO/IEC 29147:2018 Information technology — Security techniques — Vulnerability disclosure",
387
+ "status": "International Standard",
388
+ "published": "2018-10",
389
+ "tracker": "https://www.iso.org/standard/72311.html",
390
+ "relevance": "Internationally-recognized vulnerability-disclosure procedure standard. Operator-facing: an org claiming a CVD program must demonstrate the documented procedures cover receipt, triage, advisory drafting, notification, and post-disclosure review. Twin to ISO 30111 (handling); together they form the disclosure ↔ handling pair. The RFC 9116 (security.txt) artifact is the operator-facing surface that operationalizes ISO 29147 receipt requirements.",
391
+ "skills_referencing": [
392
+ "coordinated-vuln-disclosure"
393
+ ],
394
+ "last_verified": "2026-05-13"
395
+ },
396
+ "ISO-30111": {
397
+ "number": null,
398
+ "title": "ISO/IEC 30111:2019 Information technology — Security techniques — Vulnerability handling processes",
399
+ "status": "International Standard",
400
+ "published": "2019-10",
401
+ "tracker": "https://www.iso.org/standard/69725.html",
402
+ "relevance": "Internal handling counterpart to ISO 29147. Defines the internal processes (investigation, resolution, release) that follow a disclosed vulnerability through to fix. Operator-facing: paired with ISO 29147 it supplies the end-to-end CVD lifecycle. Many compliance frameworks (NIS2, EU CRA) reference 'a documented vulnerability handling process'; ISO 30111 is the canonical artifact that satisfies the wording.",
403
+ "skills_referencing": [
404
+ "coordinated-vuln-disclosure"
405
+ ],
406
+ "last_verified": "2026-05-13"
407
+ },
408
+ "RFC-9116": {
409
+ "number": 9116,
410
+ "title": "A File Format to Aid in Security Vulnerability Disclosure",
411
+ "status": "Proposed Standard",
412
+ "published": "2022-04",
413
+ "tracker": "https://www.rfc-editor.org/info/rfc9116",
414
+ "relevance": "Defines the `/.well-known/security.txt` file format. Operator-facing CVD entry point: researchers reaching a domain consult `/.well-known/security.txt` for the disclosure contact + policy URL + preferred encryption + acknowledgments page. Absence is a recurring CVD-friction finding; presence with a stale `Expires` header is equivalent to absence. Pair with ISO 29147 receipt requirements.",
415
+ "skills_referencing": [
416
+ "coordinated-vuln-disclosure"
417
+ ],
418
+ "last_verified": "2026-05-13"
419
+ },
420
+ "CSAF-2.0": {
421
+ "number": null,
422
+ "title": "OASIS Common Security Advisory Framework Version 2.0",
423
+ "status": "OASIS Standard",
424
+ "published": "2022-11",
425
+ "tracker": "https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html",
426
+ "relevance": "Machine-readable security advisory format adopted by EU CRA, CISA, and major vendor PSIRTs. Replaces the CVRF-1.2 format. Operator-facing: CSAF 2.0 documents carry the same advisory content traditionally found in vendor PDFs but in a parseable JSON form that consumers can ingest for fleet-wide impact assessment. exceptd emits CSAF-2.0 close.evidence_package bundles from `exceptd run --format csaf-2.0` for downstream auditor consumption.",
427
+ "skills_referencing": [
428
+ "coordinated-vuln-disclosure"
429
+ ],
430
+ "last_verified": "2026-05-13"
431
+ },
432
+ "RFC-6545": {
433
+ "number": 6545,
434
+ "title": "Real-time Inter-network Defense (RID)",
435
+ "status": "Proposed Standard",
436
+ "published": "2012-04",
437
+ "tracker": "https://www.rfc-editor.org/info/rfc6545",
438
+ "relevance": "Defines the RID schema for exchanging incident-response data between organizations and CSIRTs. Operator-facing relevance is via the IODEF (RFC 7970) payload that RID transports — the pair is the IETF-standardized cross-organizational incident-coordination protocol. Adoption lags; in practice many SOCs use ad-hoc CSV/JSON exchanges instead, leaving compliance with 'documented coordination channel' requirements weakly evidenced.",
439
+ "skills_referencing": [
440
+ "incident-response-playbook"
441
+ ],
442
+ "last_verified": "2026-05-13"
443
+ },
444
+ "RFC-6546": {
445
+ "number": 6546,
446
+ "title": "Transport of Real-time Inter-network Defense (RID) Messages over HTTP/TLS",
447
+ "status": "Proposed Standard",
448
+ "published": "2012-04",
449
+ "tracker": "https://www.rfc-editor.org/info/rfc6546",
450
+ "relevance": "Specifies the HTTP-over-TLS transport for RID (RFC 6545) messages. Operator-facing: provides the wire-level protocol for IODEF/RID message exchange when an organization commits to standardized incident-data coordination. Pair this with RFC 6545 (schema) and RFC 7970 (IODEF v2 payload).",
451
+ "skills_referencing": [
452
+ "incident-response-playbook"
453
+ ],
454
+ "last_verified": "2026-05-13"
455
+ },
456
+ "RFC-7970": {
457
+ "number": 7970,
458
+ "title": "The Incident Object Description Exchange Format Version 2",
459
+ "status": "Proposed Standard",
460
+ "published": "2016-11",
461
+ "tracker": "https://www.rfc-editor.org/info/rfc7970",
462
+ "relevance": "IODEF v2 — the structured-incident payload format carried by RID. Operator-facing: IODEF v2 is the canonical machine-readable incident record. Use cases include cross-CSIRT coordination, regulator submissions where structured data is requested, and SIEM-to-SIEM federation. Adoption is sector-uneven; healthcare and financial sectors have stronger uptake than general industry.",
463
+ "skills_referencing": [
464
+ "incident-response-playbook"
465
+ ],
466
+ "last_verified": "2026-05-13"
323
467
  }
324
468
  }
@@ -43,6 +43,21 @@ const ROOT = path.join(__dirname, "..");
43
43
  const ABS = (p) => path.join(ROOT, p);
44
44
  const TODAY = new Date().toISOString().slice(0, 10);
45
45
 
46
+ // v0.12.8: the CVE catalog path used by refresh-external is overridable so
47
+ // tests can redirect to a tempdir instead of mutating the real shipped
48
+ // data/cve-catalog.json. Resolution order:
49
+ // 1. opts.catalog (--catalog CLI arg)
50
+ // 2. process.env.EXCEPTD_CVE_CATALOG (env var)
51
+ // 3. ROOT/data/cve-catalog.json (default)
52
+ // All four write-sites in this file route through resolveCatalogPath() so
53
+ // that the redirect is consistent across the advisory-import, GHSA-import,
54
+ // and per-source merge code paths.
55
+ function resolveCatalogPath(opts) {
56
+ if (opts && opts.catalog) return path.resolve(opts.catalog);
57
+ if (process.env.EXCEPTD_CVE_CATALOG) return path.resolve(process.env.EXCEPTD_CVE_CATALOG);
58
+ return ABS("data/cve-catalog.json");
59
+ }
60
+
46
61
  function parseArgs(argv) {
47
62
  const out = {
48
63
  apply: false,
@@ -64,6 +79,8 @@ function parseArgs(argv) {
64
79
  else if (a === "--help" || a === "-h") out.help = true;
65
80
  else if (a === "--advisory") { out.advisory = argv[++i]; }
66
81
  else if (a.startsWith("--advisory=")) { out.advisory = a.slice("--advisory=".length); }
82
+ else if (a === "--catalog") { out.catalog = argv[++i]; }
83
+ else if (a.startsWith("--catalog=")) { out.catalog = a.slice("--catalog=".length); }
67
84
  else if (a === "--from-cache") {
68
85
  // accept either --from-cache <path> or --from-cache (default path)
69
86
  const next = argv[i + 1];
@@ -204,7 +221,7 @@ const KEV_SOURCE = {
204
221
  }
205
222
  ctx.cveCatalog._meta = ctx.cveCatalog._meta || {};
206
223
  ctx.cveCatalog._meta.last_updated = TODAY;
207
- writeJson(ABS("data/cve-catalog.json"), ctx.cveCatalog);
224
+ writeJson(ctx.cvePath || ABS("data/cve-catalog.json"), ctx.cveCatalog);
208
225
  return { updated: updated + added, added, drift_updated: updated, errors };
209
226
  },
210
227
  };
@@ -279,7 +296,7 @@ const EPSS_SOURCE = {
279
296
  }
280
297
  ctx.cveCatalog._meta = ctx.cveCatalog._meta || {};
281
298
  ctx.cveCatalog._meta.last_updated = TODAY;
282
- writeJson(ABS("data/cve-catalog.json"), ctx.cveCatalog);
299
+ writeJson(ctx.cvePath || ABS("data/cve-catalog.json"), ctx.cveCatalog);
283
300
  return { updated, errors };
284
301
  },
285
302
  };
@@ -324,7 +341,7 @@ const NVD_SOURCE = {
324
341
  }
325
342
  ctx.cveCatalog._meta = ctx.cveCatalog._meta || {};
326
343
  ctx.cveCatalog._meta.last_updated = TODAY;
327
- writeJson(ABS("data/cve-catalog.json"), ctx.cveCatalog);
344
+ writeJson(ctx.cvePath || ABS("data/cve-catalog.json"), ctx.cveCatalog);
328
345
  return { updated, errors };
329
346
  },
330
347
  };
@@ -714,9 +731,11 @@ function synthesizeFromFixture(ctx, sourceName) {
714
731
  // --- IO helpers --------------------------------------------------------
715
732
 
716
733
  function loadCtx(opts) {
734
+ const cvePath = resolveCatalogPath(opts);
717
735
  const ctx = {
718
736
  manifest: JSON.parse(fs.readFileSync(ABS("manifest.json"), "utf8")),
719
- cveCatalog: JSON.parse(fs.readFileSync(ABS("data/cve-catalog.json"), "utf8")),
737
+ cvePath, // remember the resolved path; applyDiff callbacks write through it
738
+ cveCatalog: JSON.parse(fs.readFileSync(cvePath, "utf8")),
720
739
  rfcCatalog: JSON.parse(fs.readFileSync(ABS("data/rfc-references.json"), "utf8")),
721
740
  cweCatalog: JSON.parse(fs.readFileSync(ABS("data/cwe-catalog.json"), "utf8")),
722
741
  d3fendCatalog: JSON.parse(fs.readFileSync(ABS("data/d3fend-catalog.json"), "utf8")),
@@ -828,7 +847,8 @@ async function seedSingleAdvisory(opts) {
828
847
  }
829
848
 
830
849
  // Apply: write to cve-catalog.json with the _auto_imported flag.
831
- const catalogPath = ABS("data/cve-catalog.json");
850
+ // v0.12.8: honor --catalog / EXCEPTD_CVE_CATALOG so tests can redirect.
851
+ const catalogPath = resolveCatalogPath(opts);
832
852
  const catalog = JSON.parse(fs.readFileSync(catalogPath, "utf8"));
833
853
  if (catalog[cveId] && !catalog[cveId]._auto_imported && !catalog[cveId]._draft) {
834
854
  // Refuse to overwrite a human-curated entry.
@@ -71,9 +71,9 @@
71
71
  "type": "array",
72
72
  "items": {
73
73
  "type": "string",
74
- "pattern": "^(RFC-[0-9]+|DRAFT-[A-Z0-9-]+)$"
74
+ "pattern": "^(RFC-[0-9]+|DRAFT-[A-Z0-9-]+|ISO-[0-9]+|CSAF-[0-9]+\\.[0-9]+)$"
75
75
  },
76
- "description": "Optional. IETF RFC numbers (e.g. RFC-8446) or Internet-Draft slugs (e.g. DRAFT-IETF-TLS-ECDHE-MLKEM) the skill depends on. Each must resolve in data/rfc-references.json."
76
+ "description": "Optional. IETF RFC numbers (e.g. RFC-8446), Internet-Draft slugs (e.g. DRAFT-IETF-TLS-ECDHE-MLKEM), ISO standards (e.g. ISO-29147), or OASIS CSAF advisory format (CSAF-2.0). Each must resolve in data/rfc-references.json."
77
77
  },
78
78
  "cwe_refs": {
79
79
  "type": "array",
@@ -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-13T03:58:09.357Z",
3
+ "_generated_at": "2026-05-13T13:51:52.021Z",
4
4
  "atlas_version": "5.1.0",
5
5
  "skill_count": 38,
6
6
  "skills": [