@blamejs/exceptd-skills 0.19.32 → 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 (127) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/exceptd.js +896 -2824
  3. package/data/_indexes/_meta.json +8 -8
  4. package/data/_indexes/activity-feed.json +2 -2
  5. package/data/_indexes/catalog-summaries.json +7 -7
  6. package/data/_indexes/chains.json +60118 -0
  7. package/data/attack-techniques.json +267 -7
  8. package/data/cve-catalog.json +9991 -3
  9. package/data/cwe-catalog.json +109 -2
  10. package/data/framework-control-gaps.json +578 -3
  11. package/data/zeroday-lessons.json +8330 -1
  12. package/lib/auto-discovery.js +56 -286
  13. package/lib/canonical-eq.js +7 -40
  14. package/lib/citation-resolve.js +22 -70
  15. package/lib/collectors/ai-api.js +20 -54
  16. package/lib/collectors/cicd-pipeline-compromise.js +40 -108
  17. package/lib/collectors/citation-hygiene.js +72 -210
  18. package/lib/collectors/containers.js +41 -130
  19. package/lib/collectors/cred-stores.js +31 -115
  20. package/lib/collectors/crypto-codebase.js +55 -138
  21. package/lib/collectors/crypto.js +24 -54
  22. package/lib/collectors/hardening.js +20 -78
  23. package/lib/collectors/kernel.js +16 -46
  24. package/lib/collectors/library-author.js +57 -206
  25. package/lib/collectors/mcp.js +24 -70
  26. package/lib/collectors/runtime.js +24 -86
  27. package/lib/collectors/sbom.js +34 -106
  28. package/lib/collectors/scan-excludes.js +31 -138
  29. package/lib/collectors/secrets.js +62 -178
  30. package/lib/cross-ref-api.js +39 -123
  31. package/lib/currency-severity.js +8 -27
  32. package/lib/cve-batch.js +13 -21
  33. package/lib/cve-cli.js +13 -20
  34. package/lib/cve-curation.js +72 -239
  35. package/lib/cve-regression-watcher.js +29 -152
  36. package/lib/cvss.js +13 -54
  37. package/lib/doctor-bucketing.js +3 -19
  38. package/lib/exit-codes.js +10 -42
  39. package/lib/flag-suggest.js +7 -25
  40. package/lib/framework-gap.js +35 -114
  41. package/lib/gap-detectors.js +37 -159
  42. package/lib/id-validation.js +9 -30
  43. package/lib/job-queue.js +13 -36
  44. package/lib/lint-skills.js +64 -232
  45. package/lib/playbook-runner.js +693 -2095
  46. package/lib/prefetch.js +100 -376
  47. package/lib/refresh-external.js +199 -627
  48. package/lib/refresh-network.js +75 -307
  49. package/lib/rfc-cli.js +23 -68
  50. package/lib/scoring.js +77 -145
  51. package/lib/sign.js +43 -229
  52. package/lib/source-advisories.js +43 -194
  53. package/lib/source-ghsa.js +37 -120
  54. package/lib/source-osv.js +94 -266
  55. package/lib/ttp-mapper.js +14 -24
  56. package/lib/upstream-check-cli.js +10 -28
  57. package/lib/upstream-check.js +19 -44
  58. package/lib/validate-catalog-meta.js +17 -61
  59. package/lib/validate-cve-catalog.js +43 -119
  60. package/lib/validate-indexes.js +25 -76
  61. package/lib/validate-package.js +16 -62
  62. package/lib/validate-playbooks.js +69 -275
  63. package/lib/validate-vendor.js +16 -49
  64. package/lib/verify.js +56 -286
  65. package/lib/version-pins.js +5 -34
  66. package/lib/worker-pool.js +11 -30
  67. package/lib/xml-tokenizer.js +47 -152
  68. package/manifest.json +53 -53
  69. package/orchestrator/dispatcher.js +17 -68
  70. package/orchestrator/event-bus.js +11 -74
  71. package/orchestrator/index.js +138 -412
  72. package/orchestrator/pipeline.js +28 -85
  73. package/orchestrator/scanner.js +34 -138
  74. package/orchestrator/scheduler.js +20 -84
  75. package/package.json +2 -2
  76. package/sbom.cdx.json +253 -253
  77. package/scripts/audit-catalog-gaps.js +9 -62
  78. package/scripts/audit-cross-skill.js +5 -31
  79. package/scripts/audit-perf.js +6 -16
  80. package/scripts/backfill-theater-test.js +7 -64
  81. package/scripts/bootstrap.js +12 -44
  82. package/scripts/build-indexes.js +40 -154
  83. package/scripts/builders/activity-feed.js +4 -14
  84. package/scripts/builders/catalog-summaries.js +3 -10
  85. package/scripts/builders/currency.js +7 -20
  86. package/scripts/builders/cwe-chains.js +7 -30
  87. package/scripts/builders/did-ladders.js +6 -13
  88. package/scripts/builders/frequency.js +5 -19
  89. package/scripts/builders/jurisdiction-clocks.js +6 -25
  90. package/scripts/builders/recipes.js +6 -14
  91. package/scripts/builders/section-offsets.js +13 -51
  92. package/scripts/builders/stale-content.js +7 -28
  93. package/scripts/builders/summary-cards.js +8 -29
  94. package/scripts/builders/theater-fingerprints.js +12 -27
  95. package/scripts/builders/token-budget.js +4 -31
  96. package/scripts/check-agents-md-collectors.js +11 -54
  97. package/scripts/check-catalog-gap-budget.js +15 -32
  98. package/scripts/check-changelog-extract.js +18 -48
  99. package/scripts/check-codebase-patterns-currency.js +6 -22
  100. package/scripts/check-codebase-patterns.js +50 -143
  101. package/scripts/check-epss-consistency.js +9 -64
  102. package/scripts/check-framework-gap-coverage.js +13 -31
  103. package/scripts/check-manifest-snapshot.js +13 -73
  104. package/scripts/check-sbom-currency.js +44 -142
  105. package/scripts/check-test-count.js +15 -52
  106. package/scripts/check-test-coverage.js +66 -197
  107. package/scripts/check-test-subjects.js +21 -62
  108. package/scripts/check-ttp-references.js +14 -38
  109. package/scripts/check-ttp-upstream.js +8 -40
  110. package/scripts/check-version-bump.js +9 -61
  111. package/scripts/check-version-tags.js +20 -121
  112. package/scripts/predeploy.js +38 -184
  113. package/scripts/refresh-manifest-snapshot.js +16 -38
  114. package/scripts/refresh-mitre-atlas.js +3 -8
  115. package/scripts/refresh-mitre-attack.js +1 -8
  116. package/scripts/refresh-mitre-d3fend.js +3 -9
  117. package/scripts/refresh-mitre-ics-attack.js +3 -8
  118. package/scripts/refresh-reverse-refs.js +27 -94
  119. package/scripts/refresh-rfc-index.js +2 -10
  120. package/scripts/refresh-sbom.js +31 -161
  121. package/scripts/refresh-upstream-catalogs.js +40 -137
  122. package/scripts/release.js +69 -232
  123. package/scripts/run-e2e-scenarios.js +24 -71
  124. package/scripts/sync-manifest-metadata.js +10 -34
  125. package/scripts/sync-package-description.js +8 -17
  126. package/scripts/validate-vendor-online.js +13 -44
  127. package/scripts/verify-shipped-tarball.js +35 -140
@@ -1,44 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  /**
4
- * scripts/audit-catalog-gaps.js
5
- *
6
- * Walks every data/*.json catalog and surfaces three classes of gap:
7
- *
8
- * 1. missing-context entries that exist but lack one of the
9
- * documented context-search fields (e.g. RFC
10
- * without abstract; ATT&CK technique without
11
- * platforms; CVE without iocs)
12
- *
13
- * 2. dangling-ref forward references from one catalog into
14
- * another that do not resolve (e.g. CVE
15
- * entry's cwe_refs cites CWE-XXX but the
16
- * local cwe-catalog does not carry that ID)
17
- *
18
- * 3. draft-debt per-catalog count of _auto_imported rows
19
- * relative to operator-curated rows. High
20
- * draft-debt = bulk-imported surface that has
21
- * not been refined yet.
22
- *
23
- * Output: structured JSON to stdout (default) or human-readable summary
24
- * with `--pretty`. Returns exit 0 in --warn-only mode (default); exit
25
- * 1 in --strict mode if any class triggers.
26
- *
27
- * Usage:
28
- * node scripts/audit-catalog-gaps.js # JSON
29
- * node scripts/audit-catalog-gaps.js --pretty # human
30
- * node scripts/audit-catalog-gaps.js --strict # exit 1 on gap
31
- * node scripts/audit-catalog-gaps.js --catalog cve # one catalog
32
- * node scripts/audit-catalog-gaps.js --class missing-context
33
- *
34
- * npm: `npm run audit-catalog-gaps`
35
- *
36
- * Design note: the gap analyzer is a separate detection plane from
37
- * lib/validate-cve-catalog.js (schema validation, predeploy gate) and
38
- * scripts/refresh-reverse-refs.js (forward/reverse-ref currency). The
39
- * validator polices what's strictly required by the schema; the gap
40
- * analyzer polices the recommended-but-not-required context envelope
41
- * that lets an AI consumer find an entry by topic instead of by ID.
4
+ * Walks every data/*.json catalog for gaps: entries missing a documented context
5
+ * field, cross-catalog refs that do not resolve, and per-catalog draft debt.
6
+ * Warn-only by default; --strict exits 1 when any class triggers. Distinct from
7
+ * lib/validate-cve-catalog.js, which polices what the schema strictly requires.
42
8
  */
43
9
 
44
10
  const fs = require("fs");
@@ -48,10 +14,7 @@ const ROOT = path.join(__dirname, "..");
48
14
  const DATA = path.join(ROOT, "data");
49
15
  const TODAY = new Date().toISOString().slice(0, 10);
50
16
 
51
- // Per-catalog required context fields. Each entry in the array is a
52
- // field path (dot-separated for nested) and a non-emptiness predicate.
53
- // Pillar / Class / Pillar-abstraction CWEs and similar can opt out via
54
- // the suppression key on the entry (_gap_skip: { fields: [...] }).
17
+ // Per-catalog required context fields; an entry opts out with _gap_skip: { fields: [...] }.
55
18
  const SPEC = {
56
19
  "cve-catalog": {
57
20
  file: "cve-catalog.json",
@@ -133,11 +96,6 @@ const SPEC = {
133
96
  { field: "control_name", check: (v) => typeof v === "string" && v.length > 0, label: "control_name" },
134
97
  { field: "real_requirement", check: (v) => typeof v === "string" && v.length > 20, label: "real_requirement (>20 chars)" },
135
98
  { field: "theater_test", check: (v) => v && typeof v.claim === "string" && typeof v.test === "string", label: "theater_test{claim,test}" },
136
- // evidence_cves is required UNLESS the entry declares forward_looking:true.
137
- // v0.13.19 used per-entry _gap_skip annotations on 84 framework gaps;
138
- // v0.13.20 replaces that with a first-class schema field operators can
139
- // see in the JSON. The check honors forward_looking via the entry
140
- // parameter — see the SCHEMA_FORWARD_LOOKING block in inspect().
141
99
  { field: "evidence_cves", check: (v, entry) => (entry && entry.forward_looking === true) || (Array.isArray(v) && v.length > 0), label: "evidence_cves (or forward_looking:true)" }
142
100
  ],
143
101
  refs: []
@@ -180,10 +138,7 @@ function inspect(catalogKey) {
180
138
  const skip = e._gap_skip && Array.isArray(e._gap_skip.fields) ? new Set(e._gap_skip.fields) : new Set();
181
139
  for (const r of spec.required_context) {
182
140
  if (skip.has(r.field)) continue;
183
- // Pass the entry as the second argument so per-field checks can
184
- // inspect class-level schema flags (forward_looking, etc.). The
185
- // legacy check-functions only consumed the value; new ones can
186
- // opt into entry-aware evaluation.
141
+ // The second argument lets a check consult entry-level flags like forward_looking.
187
142
  if (!r.check(e[r.field], e)) {
188
143
  report.missing_context.push({ id, field: r.field, label: r.label });
189
144
  }
@@ -199,7 +154,6 @@ function inspectRefs(allCatalogs) {
199
154
  const attCat = allCatalogs["attack-techniques"];
200
155
  const atlCat = allCatalogs["atlas-ttps"];
201
156
  const fwCat = allCatalogs["framework-control-gaps"];
202
- // Build presence sets keyed by id (sans _meta).
203
157
  const cweSet = new Set(Object.keys(cweCat).filter((k) => k !== "_meta"));
204
158
  const attSet = new Set(Object.keys(attCat).filter((k) => k !== "_meta"));
205
159
  const atlSet = new Set(Object.keys(atlCat).filter((k) => k !== "_meta"));
@@ -246,7 +200,6 @@ function emitPretty(report) {
246
200
  if (r.missing_context.length === 0) {
247
201
  lines.push(" ✓ context complete on every entry");
248
202
  } else {
249
- // Group by field for tidier output.
250
203
  const byField = new Map();
251
204
  for (const m of r.missing_context) {
252
205
  if (!byField.has(m.field)) byField.set(m.field, []);
@@ -278,7 +231,6 @@ function emitPretty(report) {
278
231
  const pct = r.entries === 0 ? 0 : ((r.auto_imported / r.entries) * 100).toFixed(1);
279
232
  lines.push(` ${r.catalog.padEnd(28)} ${r.auto_imported} / ${r.entries} (${pct}%)`);
280
233
  }
281
- // v0.13.21 extended findings sections.
282
234
  const ext = report.extended_findings || {};
283
235
  const extClasses = Object.keys(ext).sort();
284
236
  if (extClasses.length > 0) {
@@ -297,11 +249,7 @@ function emitPretty(report) {
297
249
  return lines.join("\n");
298
250
  }
299
251
 
300
- // Valid finding-class names for the `--class` filter. v0.13.21 added 7
301
- // extended detection classes for gaps the v0.13.19 detector did not
302
- // surface (content-quality / temporal-staleness / logical-consistency /
303
- // cross-ref-completeness / schema-evolution / operator-action-sla /
304
- // unused-orphan). Each is implemented in lib/gap-detectors.js.
252
+ // Names `--class` accepts; the extended ones are implemented in lib/gap-detectors.js.
305
253
  const VALID_CLASSES = new Set([
306
254
  "missing-context", "dangling-ref", "draft-debt",
307
255
  "content-quality", "temporal-staleness", "logical-consistency",
@@ -334,12 +282,11 @@ function main() {
334
282
  perCatalog.push(inspect(k));
335
283
  allLoaded[k] = loadCatalog(SPEC[k].file);
336
284
  }
337
- // Load all needed catalogs for cross-ref pass even when --catalog scoped.
285
+ // The cross-ref pass needs every catalog, even when --catalog scopes the audit.
338
286
  for (const k of Object.keys(SPEC)) if (!allLoaded[k]) allLoaded[k] = loadCatalog(SPEC[k].file);
339
287
  const dangling = opts.catalog && opts.catalog !== "cve-catalog" ? [] : inspectRefs(allLoaded);
340
288
 
341
- // v0.13.21 extended detectors. --catalog scoping mutes them (they're
342
- // cross-catalog by nature); --class scoping filters down to one.
289
+ // --catalog mutes the extended detectors; they are cross-catalog by nature.
343
290
  const extendedFindings = opts.catalog
344
291
  ? []
345
292
  : EXTENDED_DETECTORS.runAllDetectors(allLoaded, {});
@@ -1,29 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
- * scripts/audit-cross-skill.js
4
- *
5
- * Comprehensive cross-skill accuracy / bug audit. Run after any
6
- * skill add / rename / dispatch-rewire. Surfaces:
7
- *
8
- * - manifest paths that don't exist on disk
9
- * - skill directories on disk with no manifest entry
10
- * - frontmatter `name` drift from manifest `name`
11
- * - skills missing from the researcher dispatch table
12
- * - skills missing from AGENTS.md Quick Skill Reference
13
- * - version drift between package.json / manifest.json / CHANGELOG.md
14
- * - manifest-snapshot.json drift from manifest.json
15
- * - sbom.cdx.json drift from live skill / catalog counts
16
- * - broken ref: any cwe_refs / d3fend_refs / framework_gaps / atlas_refs /
17
- * rfc_refs / dlp_refs that doesn't resolve in its catalog
18
- * - RFC catalog reverse-references that drift from manifest forward-refs
19
- * - skill-update-loop "Affected skills" blocks referencing nonexistent skills
20
- * - stale references to renamed skills in any tracked file
21
- * - trigger collisions between skills (informational)
22
- * - README badge count drift
23
- *
24
- * Exit non-zero on any finding (excluding trigger collisions which are informational).
25
- *
26
- * Usage: node scripts/audit-cross-skill.js
3
+ * Cross-skill accuracy audit: manifest, skill files, catalogs and docs must
4
+ * agree. Exits non-zero on any finding; trigger collisions are informational.
27
5
  */
28
6
 
29
7
  const fs = require("fs");
@@ -196,7 +174,7 @@ for (const f of trackedDocs) {
196
174
  const body = fs.readFileSync(ABS(f), "utf8");
197
175
  for (const tok of staleTokens) {
198
176
  if (body.includes(tok)) {
199
- // CHANGELOG legitimately records the rename in the 0.5.4 entry.
177
+ // The CHANGELOG legitimately records the rename.
200
178
  if (f === "CHANGELOG.md") continue;
201
179
  note(`STALE RENAME REF in ${f}: contains "${tok}"`);
202
180
  }
@@ -228,11 +206,8 @@ if (badgeMatch && Number(badgeMatch[1]) !== skills.length) {
228
206
  const jurBadge = readme.match(/jurisdictions-(\d+)-/);
229
207
  const liveJurs = (() => {
230
208
  const g = JSON.parse(fs.readFileSync(ABS("data/global-frameworks.json"), "utf8"));
231
- // Canonical jurisdiction count: every non-metadata top-level entry in the
232
- // registry. GLOBAL (the International / Multi-Jurisdiction standards scope:
233
- // ISO, CSA, CIS) is a counted entry, matching the README badge and the
234
- // catalog-summary index. Only `_`-prefixed keys (_meta, _notification_summary,
235
- // _patch_sla_summary) are metadata and excluded.
209
+ // Every non-metadata top-level entry, GLOBAL included — matches the README
210
+ // badge and the catalog-summary index.
236
211
  return Object.keys(g).filter((k) => !k.startsWith("_")).length;
237
212
  })();
238
213
  if (jurBadge && Number(jurBadge[1]) !== liveJurs) {
@@ -249,7 +224,6 @@ if (cntClaim) {
249
224
  }
250
225
  }
251
226
 
252
- // Output
253
227
  console.log("\n=== CROSS-SKILL AUDIT ===");
254
228
  console.log(`Skills: ${skills.length}`);
255
229
  console.log(`Catalogs: ${liveCatalogs}`);
@@ -1,10 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
- * scripts/audit-perf.js
4
- *
5
- * Micro-benchmarks the hot paths a skill / orchestrator / audit
6
- * actually exercises. Times each operation so we can decide what's
7
- * worth pre-computing into a seeded index.
3
+ * scripts/audit-perf.js — micro-benchmarks the hot paths a skill, orchestrator
4
+ * or audit exercises, to decide what is worth pre-computing into a seeded index.
8
5
  *
9
6
  * Usage: node scripts/audit-perf.js
10
7
  */
@@ -29,13 +26,11 @@ console.log("\n=== exceptd hot-path performance ===\n");
29
26
  console.log("Operation Time");
30
27
  console.log("-".repeat(70));
31
28
 
32
- // 1. Load manifest
33
29
  const manifest = bench("load manifest.json (parse)", () =>
34
30
  JSON.parse(fs.readFileSync(ABS("manifest.json"), "utf8"))
35
31
  );
36
32
  const skills = manifest.skills;
37
33
 
38
- // 2. Load every data catalog
39
34
  const catalogs = [
40
35
  "cve-catalog.json",
41
36
  "atlas-ttps.json",
@@ -54,12 +49,11 @@ const catalogObjs = bench(`load all ${catalogs.length} data catalogs`, () => {
54
49
  return out;
55
50
  });
56
51
 
57
- // 3. Read every skill body
58
52
  bench(`read all ${skills.length} skill.md bodies`, () => {
59
53
  for (const s of skills) fs.readFileSync(ABS(s.path), "utf8");
60
54
  });
61
55
 
62
- // 4. Parse every skill frontmatter (the linter's expensive op)
56
+ // The linter's expensive operation.
63
57
  function parseFm(text) {
64
58
  if (!text.startsWith("---")) return null;
65
59
  const end = text.indexOf("\n---", 3);
@@ -90,7 +84,7 @@ bench(`parse all ${skills.length} skill frontmatters`, () => {
90
84
  for (const s of skills) parseFm(fs.readFileSync(ABS(s.path), "utf8"));
91
85
  });
92
86
 
93
- // 5. Trigger lookup (what the dispatcher does)
87
+ // The lookup the dispatcher performs.
94
88
  const flatTriggers = [];
95
89
  for (const s of skills) for (const t of s.triggers || []) flatTriggers.push([t.toLowerCase(), s.name]);
96
90
  bench("trigger string-match against all skills (single query)", () => {
@@ -98,16 +92,14 @@ bench("trigger string-match against all skills (single query)", () => {
98
92
  return flatTriggers.filter(([t]) => t.includes(q) || q.includes(t));
99
93
  });
100
94
 
101
- // 6. Cross-reference lookup: which skills cite a given CWE?
102
95
  bench("xref: which skills cite CWE-79? (linear scan)", () => {
103
96
  const refSet = "CWE-79";
104
97
  return skills.filter((s) => (s.cwe_refs || []).includes(refSet)).map((s) => s.name);
105
98
  });
106
99
 
107
- // 7. Multi-hop chain: CVE → CWE → ATLAS → framework_gaps for one CVE
108
100
  const cve = catalogObjs["cve-catalog.json"]["CVE-2026-31431"];
109
101
  bench("multi-hop chain: CVE-2026-31431 → CWE → ATLAS → frameworks", () => {
110
- // skills that mention CVE → their CWE refs → their ATLAS refs → their framework gaps
102
+ // CVE → the citing skills' CWE refs → their ATLAS refs → their framework gaps.
111
103
  const skillsCiting = skills.filter((s) =>
112
104
  (catalogObjs["cve-catalog.json"]["CVE-2026-31431"].evidence_cves || []).length > 0 // dummy filter
113
105
  );
@@ -123,7 +115,6 @@ bench("multi-hop chain: CVE-2026-31431 → CWE → ATLAS → frameworks", () =>
123
115
  return { cwes: [...cwes], atlases: [...atlases], fws: [...fws] };
124
116
  });
125
117
 
126
- // 8. Forward_watch aggregator (read 38 skill files, parse frontmatter, union all forward_watch)
127
118
  bench(`watchlist aggregator (full scan, ${skills.length} skills)`, () => {
128
119
  const watch = new Set();
129
120
  for (const s of skills) {
@@ -133,9 +124,8 @@ bench(`watchlist aggregator (full scan, ${skills.length} skills)`, () => {
133
124
  return watch.size;
134
125
  });
135
126
 
136
- // 9. Full cross-skill audit
137
127
  bench("full cross-skill audit script (subprocess overhead included)", () => {
138
- // Simulate: load manifest + all catalogs + all skill files + compute every refset
128
+ // Simulated: load the manifest, catalogs and skill files, walk every refset.
139
129
  for (const s of skills) {
140
130
  fs.readFileSync(ABS(s.path), "utf8");
141
131
  for (const f of s.cwe_refs || []) { /* lookup */ }
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env node
2
- // One-shot backfill of theater_test field for data/framework-control-gaps.json.
3
- // Hard Rule #6: every compliance-framework finding includes a specific test
4
- // that distinguishes paper compliance from actual security.
5
- //
6
- // Per-entry tests are authored against the entry's framework + control_name +
7
- // real_requirement so each one discriminates the named framework's paper
8
- // language from the named real-world threat.
2
+ // One-shot backfill of the theater_test field in data/framework-control-gaps.json.
3
+ // Hard Rule #6: every compliance-framework finding carries a specific test that
4
+ // tells paper compliance from actual security.
9
5
 
10
6
  const fs = require('fs');
11
7
  const path = require('path');
@@ -14,15 +10,10 @@ const CATALOG_PATH = path.resolve(__dirname, '..', 'data', 'framework-control-ga
14
10
 
15
11
  const PAPER = 'compliance-theater';
16
12
 
17
- // Map of entry-key → theater_test. Hand-authored, grouped by framework family
18
- // so the discriminating test fits the language an auditor for THAT framework
19
- // uses. Where two entries share the same audit pattern (e.g. several NIST
20
- // 800-53 SI-* controls), the tests are similar in shape but worded against
21
- // the specific control text — never literally copy-pasted.
13
+ // entry-key → theater_test, hand-authored and grouped by framework family so
14
+ // each test speaks the language an auditor for that framework uses.
22
15
  const TESTS = {
23
- // ---------------------------------------------------------------------
24
16
  // Universal / cross-framework AI gaps
25
- // ---------------------------------------------------------------------
26
17
  'ALL-AI-PIPELINE-INTEGRITY': {
27
18
  claim: "We monitor our AI providers for security and treat model updates like any other vendor change.",
28
19
  test: "Pull the change-control register for the last 4 quarters; filter for entries where the affected asset is an externally hosted LLM, embedding model, or AI provider API. Count how many record (a) the model version pinned at the time, (b) a behavioural regression suite executed against the new version, and (c) the provider changelog reviewed with sign-off. Theater verdict if fewer than 90% of provider-side model updates produced an in-scope change-control entry, or if any sampled entry lacks a regression-suite artifact.",
@@ -42,9 +33,7 @@ const TESTS = {
42
33
  verdict_when_failed: PAPER
43
34
  },
44
35
 
45
- // ---------------------------------------------------------------------
46
36
  // Australian frameworks (Essential 8, ISM)
47
- // ---------------------------------------------------------------------
48
37
  'AU-Essential-8-App-Hardening': {
49
38
  claim: "We hardened user applications per Essential Eight Maturity Level 2; browsers and Office are locked down.",
50
39
  test: "Take the operator's hardened-application list. Confirm whether it enumerates AI coding assistants (Copilot, Cursor, Claude Code, Windsurf), MCP servers, and AI-tool config files (.claude/settings.json, .cursor/mcp.json, .vscode/settings.json:chat.tools.autoApprove) as in-scope. Pick a developer endpoint at random; verify those config files are integrity-monitored with the same alerting profile as security-sensitive files. Theater verdict if AI assistants are absent from the hardened-application list or if a config-file modification on the sampled endpoint would not generate an integrity alert.",
@@ -70,9 +59,7 @@ const TESTS = {
70
59
  verdict_when_failed: PAPER
71
60
  },
72
61
 
73
- // ---------------------------------------------------------------------
74
62
  // CIS Controls
75
- // ---------------------------------------------------------------------
76
63
  'CIS-Controls-v8-Control7': {
77
64
  claim: "We meet CIS Control 7 IG3 by remediating critical vulnerabilities within one month.",
78
65
  test: "Pull the vulnerability register for the past 12 months. Filter for CVEs that appeared on CISA KEV with public PoC during the period. For each, measure (a) time from KEV listing to verified mitigation, and (b) whether the mitigation was a live patch, configuration change, or isolation. Theater verdict if any KEV+PoC entry exceeded 4h to verified mitigation or if 'monthly cadence' was applied to a KEV-listed CVE.",
@@ -80,9 +67,7 @@ const TESTS = {
80
67
  verdict_when_failed: PAPER
81
68
  },
82
69
 
83
- // ---------------------------------------------------------------------
84
70
  // CMMC / FedRAMP
85
- // ---------------------------------------------------------------------
86
71
  'CMMC-2.0-Level-2': {
87
72
  claim: "We are CMMC Level 2 attested across all 110 NIST 800-171 controls; CUI is protected end-to-end.",
88
73
  test: "Walk the 3.4.1 (CM) asset inventory and check for AI assistants and MCP servers with CUI-adjacent access. Then inspect 3.13 system-and-communications protections to confirm AI-API egress is enumerated as a CUI exfiltration channel with monitoring. Theater verdict if AI assistants are absent from the asset inventory, or if AI-API egress at the CUI boundary has no monitoring rule, or if cross-walks to UK DEF STAN / AU DISP for joint programmes are missing.",
@@ -96,9 +81,7 @@ const TESTS = {
96
81
  verdict_when_failed: PAPER
97
82
  },
98
83
 
99
- // ---------------------------------------------------------------------
100
84
  // CWE / SBOM standards
101
- // ---------------------------------------------------------------------
102
85
  'CWE-Top-25-2024-meta': {
103
86
  claim: "Our SAST/DAST coverage maps to the CWE Top 25; we test for the most dangerous weaknesses.",
104
87
  test: "Pull the SAST/DAST rule pack and enumerate which CWE IDs each rule targets. Confirm rules exist for AI-specific CWE classes (CWE-1039 model integrity, CWE-1395 dependency on vulnerable third-party component, prompt-injection class CWEs). Run the rule pack against a known-vulnerable test fixture containing prompt-injection patterns. Theater verdict if AI-relevant CWE IDs are absent from the rule pack, or if the fixture run produces zero findings on the planted prompt-injection.",
@@ -118,9 +101,7 @@ const TESTS = {
118
101
  verdict_when_failed: PAPER
119
102
  },
120
103
 
121
- // ---------------------------------------------------------------------
122
104
  // EU DORA family
123
- // ---------------------------------------------------------------------
124
105
  'DORA-Art28': {
125
106
  claim: "Our DORA Art. 28 ICT third-party register covers all critical or important function dependencies.",
126
107
  test: "From the Art. 28 register, sample 5 third-party ICT services consumed in CIF (critical or important function) flows. For each, verify presence of build-provenance metadata (SLSA producer identifier, workflow file hash, cache key surface). Check for monthly producer-side cache verification evidence. Theater verdict if any sampled CIF dependency lacks build-provenance metadata, or if cache verification has not run in the last 90 days.",
@@ -164,9 +145,7 @@ const TESTS = {
164
145
  verdict_when_failed: PAPER
165
146
  },
166
147
 
167
- // ---------------------------------------------------------------------
168
148
  // EU AI Act
169
- // ---------------------------------------------------------------------
170
149
  'EU-AI-Act-Art-15': {
171
150
  claim: "Our high-risk AI system meets the EU AI Act Art. 15 'appropriate level of cybersecurity'.",
172
151
  test: "Request the cybersecurity test pack. Confirm presence of (a) prompt-injection red-team results bound to OWASP LLM Top 10, (b) RAG-corpus integrity test results, (c) model-extraction-resistance assessment, (d) MCP/plugin trust verification log. Then check incident-reporting bridge to NIS2 + DORA. Theater verdict if any of (a)-(d) are absent or older than 12 months, or if the bridge to NIS2/DORA notification clocks is undocumented.",
@@ -198,9 +177,7 @@ const TESTS = {
198
177
  verdict_when_failed: PAPER
199
178
  },
200
179
 
201
- // ---------------------------------------------------------------------
202
180
  // EU CRA
203
- // ---------------------------------------------------------------------
204
181
  'EU-CRA-Art13': {
205
182
  claim: "We satisfy EU CRA Art. 13 essential cybersecurity requirements with technical documentation on file.",
206
183
  test: "Request the canonical build-pipeline definition for the most recent release. Confirm publication alongside the release artifact (workflow file hash, runner attestation, secrets scope). Pick the release-being-installed at a downstream operator; verify its build pipeline matches the published definition by comparing producer-side hashes. Confirm the incident-notification clock starts from FIRST awareness (not from confirmed exploit). Theater verdict if pipeline definitions are unpublished, hashes diverge, or the clock policy starts later than first awareness.",
@@ -208,9 +185,7 @@ const TESTS = {
208
185
  verdict_when_failed: PAPER
209
186
  },
210
187
 
211
- // ---------------------------------------------------------------------
212
188
  // HIPAA
213
- // ---------------------------------------------------------------------
214
189
  'HIPAA-Security-Rule-164.312(a)(1)': {
215
190
  claim: "We meet HIPAA 164.312(a)(1) access controls; PHI is access-controlled with unique user IDs.",
216
191
  test: "Inventory AI providers in use; for each consuming PHI, locate a BAA covering prompt retention + training opt-out + breach notification within HIPAA timelines. Inspect prompt-flow telemetry for PHI; confirm DLP minimisation runs pre-egress. Confirm AI agent sessions have controls separate from human user controls. Theater verdict if any AI provider consuming PHI lacks a BAA, if DLP is absent on prompt egress, or if AI agent sessions inherit human controls without separation.",
@@ -242,9 +217,7 @@ const TESTS = {
242
217
  verdict_when_failed: PAPER
243
218
  },
244
219
 
245
- // ---------------------------------------------------------------------
246
220
  // HITRUST
247
- // ---------------------------------------------------------------------
248
221
  'HITRUST-CSF-v11.4-09.l': {
249
222
  claim: "We meet HITRUST CSF 09.l outsourced services management for all third-party providers.",
250
223
  test: "Pull the third-party register. Filter for AI providers; confirm AI vendors are inventoried separately from general SaaS. Spot-check 5 AI vendors for AI-specific contractual clauses (prompt retention, training opt-out, residency, model version pinning, prompt-breach notification). Search for self-signup AI usage on developer endpoints; confirm a policy prohibits it for in-scope data. Theater verdict if AI is bucketed inside generic SaaS, if any sampled AI vendor lacks AI-specific clauses, or if self-signup AI is in evidence on a developer endpoint that touches in-scope data.",
@@ -252,9 +225,7 @@ const TESTS = {
252
225
  verdict_when_failed: PAPER
253
226
  },
254
227
 
255
- // ---------------------------------------------------------------------
256
228
  // IEC 62443 / NIST 800-82 / NERC CIP — OT / ICS
257
- // ---------------------------------------------------------------------
258
229
  'IEC-62443-3-3': {
259
230
  claim: "Our IACS architecture meets IEC 62443-3-3 system security requirements.",
260
231
  test: "Inspect the zone-and-conduit diagram. Confirm AI operator assistants and AI-API egress paths from the corporate-to-OT boundary are enumerated as conduits with documented security levels. Sample 3 OT operator workstations; confirm any installed AI assistants are inventoried and that prompt-injection-class threats appear in the threat model. Theater verdict if AI conduits are absent from the zone diagram, or if AI assistants on OT operator workstations are not threat-modelled.",
@@ -274,9 +245,7 @@ const TESTS = {
274
245
  verdict_when_failed: PAPER
275
246
  },
276
247
 
277
- // ---------------------------------------------------------------------
278
248
  // ISO 27001 / ISO 27017 / ISO 23894 / ISO 42001
279
- // ---------------------------------------------------------------------
280
249
  'ISO-27001-2022-A.8.16': {
281
250
  claim: "Our monitoring activities under ISO 27001:2022 A.8.16 cover all in-scope systems.",
282
251
  test: "From the SIEM event-source inventory, confirm AI-API egress events, MCP server invocations, and AI-agent action audit logs are ingested. Sample one alert from each class in the past 30 days; confirm an analyst reviewed it. Theater verdict if any of those source classes are missing from the SIEM, or if no AI/MCP-related alert has been triaged in the past 90 days despite traffic being present.",
@@ -320,9 +289,7 @@ const TESTS = {
320
289
  verdict_when_failed: PAPER
321
290
  },
322
291
 
323
- // ---------------------------------------------------------------------
324
292
  // NIS2
325
- // ---------------------------------------------------------------------
326
293
  'NIS2-Art21-incident-handling': {
327
294
  claim: "We can meet NIS2 Art. 21 incident handling obligations including the 24h early warning.",
328
295
  test: "Run a tabletop with a synthetic significant-incident inject affecting an essential-service flow at T0. Stopwatch elapsed time to a Competent Authority early warning containing initial assessment, severity, and impact. Theater verdict if elapsed exceeds 24h, if no on-call is named to start the clock, or if the playbook has not been exercised in the past 12 months.",
@@ -348,9 +315,7 @@ const TESTS = {
348
315
  verdict_when_failed: PAPER
349
316
  },
350
317
 
351
- // ---------------------------------------------------------------------
352
318
  // NIST SPs and AI RMF
353
- // ---------------------------------------------------------------------
354
319
  'NIST-800-115': {
355
320
  claim: "Our pen-test methodology aligns with NIST SP 800-115 technical guidance.",
356
321
  test: "Pull the most recent pen-test report. Confirm coverage of AI/MCP attack surfaces (prompt injection, MCP plugin trust, RAG corpus integrity, AI-API egress). Confirm the testing methodology document references AI-specific test classes and tooling. Theater verdict if AI/MCP testing is absent from the methodology, or if the pen-test report contains no AI-class findings despite AI being in production.",
@@ -436,9 +401,7 @@ const TESTS = {
436
401
  verdict_when_failed: PAPER
437
402
  },
438
403
 
439
- // ---------------------------------------------------------------------
440
404
  // OWASP family
441
- // ---------------------------------------------------------------------
442
405
  'OWASP-ASVS-v5.0-V14': {
443
406
  claim: "Our application meets OWASP ASVS v5.0 V14 configuration controls.",
444
407
  test: "For any AI-mediated feature, confirm V14-equivalent controls cover prompt-isolation, output-sanitisation, and tool-grant defaults. Confirm SDK pinning and provider-version pinning where supported. Theater verdict if AI-feature configuration management is informal (no pinned versions, no documented prompt-isolation policy).",
@@ -476,9 +439,7 @@ const TESTS = {
476
439
  verdict_when_failed: PAPER
477
440
  },
478
441
 
479
- // ---------------------------------------------------------------------
480
442
  // PCI DSS family
481
- // ---------------------------------------------------------------------
482
443
  'PCI-DSS-4.0-6.3.3': {
483
444
  claim: "We address security vulnerabilities in custom and bespoke software per PCI DSS 6.3.3.",
484
445
  test: "Confirm the SDLC includes prompt-injection-class CWE coverage in code review for AI-mediated features. Inspect change tickets for AI-feature changes; confirm reviewer attestation includes AI-class threat sign-off. Theater verdict if AI-mediated changes bypass the prompt-injection threat-review gate.",
@@ -510,9 +471,7 @@ const TESTS = {
510
471
  verdict_when_failed: PAPER
511
472
  },
512
473
 
513
- // ---------------------------------------------------------------------
514
474
  // PSD2 / PTES
515
- // ---------------------------------------------------------------------
516
475
  'PSD2-RTS-SCA': {
517
476
  claim: "Our payment authentication satisfies PSD2 RTS-SCA strong customer authentication requirements.",
518
477
  test: "Inventory payment-initiation flows. For any AI-mediated initiation (agent-initiated transactions, copilot-drafted payments), confirm an explicit delegated-authority attestation per transaction class with scope (amount, counterparty, frequency). Confirm a distinct audit indicator marks AI-mediated transactions. Theater verdict if AI initiations inherit the human-user SCA evidence path without delegated-authority attestation.",
@@ -526,9 +485,7 @@ const TESTS = {
526
485
  verdict_when_failed: PAPER
527
486
  },
528
487
 
529
- // ---------------------------------------------------------------------
530
488
  // SLSA
531
- // ---------------------------------------------------------------------
532
489
  'SLSA-v1.0-Build-L3': {
533
490
  claim: "Our build pipeline is SLSA Build L3 with non-falsifiable provenance signed by a hardened build platform.",
534
491
  test: "Pull the SLSA provenance attestation for the most-recent release. Confirm the build platform is hosted/hardened, the attestation is signed, and the materials cover the full source-of-truth. Then confirm AI-authorship attestation (per-block provenance for AI-generated code with reviewer identity) is present. Confirm any model artefacts shipped have a Model Track equivalent attestation. Theater verdict if attestations exist but AI-authored diffs lack reviewer attestation, or if model artefacts ship at SLSA L0/L1 equivalent without explicit model-track attestation.",
@@ -536,9 +493,7 @@ const TESTS = {
536
493
  verdict_when_failed: PAPER
537
494
  },
538
495
 
539
- // ---------------------------------------------------------------------
540
496
  // SOC 2
541
- // ---------------------------------------------------------------------
542
497
  'SOC2-CC6-logical-access': {
543
498
  claim: "Our SOC 2 CC6 logical and physical access controls cover all in-scope systems.",
544
499
  test: "Sample AI-agent invocation flows. Confirm authorisation-context evidence per invocation (scope, tools, data sensitivity). Confirm prompt logging captures sufficient detail for post-incident analysis (input chain, output, tool calls). Confirm anomaly detection alerts on AI-agent actions outside baseline. Theater verdict if AI-agent actions are not separately authorised, prompts are unlogged, or anomaly detection is absent.",
@@ -570,9 +525,7 @@ const TESTS = {
570
525
  verdict_when_failed: PAPER
571
526
  },
572
527
 
573
- // ---------------------------------------------------------------------
574
528
  // SWIFT CSCF
575
- // ---------------------------------------------------------------------
576
529
  'SWIFT-CSCF-v2026-1.1': {
577
530
  claim: "Our SWIFT secure zone is segregated and protected per CSCF v2026 1.1.",
578
531
  test: "Inspect the secure-zone policy. Confirm explicit prohibition or strict gating of LLM assistants inside the secure zone. Confirm AI-API egress from administrative jump zones is enumerated as a named conduit with monitoring. Confirm AI-generated MT/MX message drafts are flagged as a distinct review class. Cross-walk to DORA Art. 28 register. Theater verdict if LLM assistants are silently permitted, AI-API egress is unmonitored, or no DORA cross-walk exists.",
@@ -580,9 +533,7 @@ const TESTS = {
580
533
  verdict_when_failed: PAPER
581
534
  },
582
535
 
583
- // ---------------------------------------------------------------------
584
536
  // UK CAF
585
- // ---------------------------------------------------------------------
586
537
  'UK-CAF-A1': {
587
538
  claim: "Our governance satisfies UK CAF A1 with board-level cyber risk accountability.",
588
539
  test: "Pull the board governance pack. Confirm an AI-systems-in-use inventory is reviewed at board cadence, an MCP/plugin trust register exists, and accountability for AI security outcomes maps to a named executive in the NIS2/CCRA scope. Theater verdict if AI is absent from board-pack contents, or if AI accountability is unassigned at executive level.",
@@ -626,9 +577,7 @@ const TESTS = {
626
577
  verdict_when_failed: PAPER
627
578
  },
628
579
 
629
- // ---------------------------------------------------------------------
630
580
  // VEX
631
- // ---------------------------------------------------------------------
632
581
  'VEX-CSAF-v2.1': {
633
582
  claim: "We publish VEX statements via OASIS CSAF 2.1 for our products.",
634
583
  test: "Pull the published CSAF 2.1 documents. Confirm AI-component identifier scheme presence (model + version + adapters + tokenizer). Confirm at least one VEX statement covers an AI-class vulnerability (jailbreak, prompt injection, embedding inversion). Confirm chaining of base-model VEX statements to derived-model VEX statements where applicable. Theater verdict if AI components are absent from the identifier scheme, or if no AI-class VEX statements exist despite AI components shipping.",
@@ -636,9 +585,7 @@ const TESTS = {
636
585
  verdict_when_failed: PAPER
637
586
  },
638
587
 
639
- // ---------------------------------------------------------------------
640
588
  // FCC / Telecom
641
- // ---------------------------------------------------------------------
642
589
  'FCC-CPNI-4.1': {
643
590
  claim: "Our annual CPNI certification satisfies FCC CPNI obligations.",
644
591
  test: "Confirm quarterly LI-gateway activation auditing (Salt-Typhoon/PRC threat model). Confirm gNB firmware hash attestation and signaling-anomaly baselines per PLMN-pair. Pull the most recent CPNI certification; confirm those operational artefacts are referenced. Theater verdict if certification is annual-only without LI-gateway/firmware-hash/signaling artefacts.",
@@ -676,9 +623,7 @@ const TESTS = {
676
623
  verdict_when_failed: PAPER
677
624
  },
678
625
 
679
- // ---------------------------------------------------------------------
680
626
  // Federated identity / IdP
681
- // ---------------------------------------------------------------------
682
627
  'NIST-800-53-IA-5-Federated': {
683
628
  claim: "Our IA-5 authenticator management covers federated identity providers.",
684
629
  test: "Inspect IdP control-plane: continuous attestation of token-signing certificate fingerprints, claim-transformation rule baseline with per-modification change-control attestation, management-API-token inventory with TTL + scope + source-IP enforcement. Theater verdict if attestation is snapshot-only (quarterly) rather than continuous, or if management-API tokens lack TTL/scope/source-IP enforcement.",
@@ -734,9 +679,7 @@ const TESTS = {
734
679
  verdict_when_failed: PAPER
735
680
  },
736
681
 
737
- // ---------------------------------------------------------------------
738
682
  // Ransomware playbook entries (RANSOMWARE-GAP-*)
739
- // ---------------------------------------------------------------------
740
683
  'OFAC-SDN-Payment-Block': {
741
684
  claim: "Our incident response covers OFAC sanctions screening before any ransomware payment.",
742
685
  test: "Run a tabletop where the inject is a ransomware demand from an attribution-likely-sanctioned actor. Stopwatch the workflow: attribution-evidence package assembled → cross-jurisdiction lookup (OFAC SDN + EU 2014/833 + UK OFSI + AU DFAT + JP MOF) → counsel-signed attestation → pay/restore decision. Theater verdict if any cross-jurisdiction list is missing, counsel-signed attestation is unrehearsed, or the tabletop has not been exercised in the past 12 months.",
@@ -796,8 +739,8 @@ function backfill() {
796
739
  process.exit(2);
797
740
  }
798
741
 
799
- // Re-emit with stable 2-space indentation matching the file's existing style.
800
- // Trailing newline preserved.
742
+ // Two-space indent and a trailing newline, matching the catalog on disk, so
743
+ // the rewrite diffs only where a theater_test landed.
801
744
  const out = JSON.stringify(data, null, 2) + '\n';
802
745
  fs.writeFileSync(CATALOG_PATH, out);
803
746
  console.log(`Updated ${updated}/${keys.length} entries with theater_test.`);