@blamejs/exceptd-skills 0.16.25 → 0.16.29

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 (79) hide show
  1. package/AGENTS.md +5 -5
  2. package/ARCHITECTURE.md +3 -3
  3. package/CHANGELOG.md +28 -0
  4. package/CONTEXT.md +2 -2
  5. package/README.md +6 -6
  6. package/agents/threat-researcher.md +2 -2
  7. package/bin/exceptd.js +41 -8
  8. package/data/_indexes/_meta.json +41 -40
  9. package/data/_indexes/activity-feed.json +240 -240
  10. package/data/_indexes/catalog-summaries.json +3 -3
  11. package/data/_indexes/currency.json +64 -64
  12. package/data/_indexes/jurisdiction-map.json +31 -158
  13. package/data/_indexes/recipes.json +1 -1
  14. package/data/_indexes/section-offsets.json +510 -510
  15. package/data/_indexes/summary-cards.json +33 -33
  16. package/data/_indexes/token-budget.json +200 -200
  17. package/data/atlas-ttps.json +7 -7
  18. package/data/attack-techniques.json +5 -5
  19. package/data/framework-control-gaps.json +3 -3
  20. package/lib/auto-discovery.js +15 -9
  21. package/lib/collectors/library-author.js +26 -9
  22. package/lib/collectors/secrets.js +8 -1
  23. package/lib/cvss.js +108 -0
  24. package/lib/lint-skills.js +6 -1
  25. package/lib/playbook-runner.js +17 -4
  26. package/lib/prefetch.js +97 -5
  27. package/lib/refresh-external.js +25 -13
  28. package/lib/schemas/manifest.schema.json +1 -1
  29. package/lib/schemas/skill-frontmatter.schema.json +1 -1
  30. package/lib/validate-indexes.js +5 -0
  31. package/lib/version-pins.js +3 -3
  32. package/manifest-snapshot.json +2 -2
  33. package/manifest-snapshot.sha256 +1 -1
  34. package/manifest.json +124 -124
  35. package/orchestrator/pipeline.js +16 -4
  36. package/package.json +1 -1
  37. package/sbom.cdx.json +170 -140
  38. package/scripts/build-indexes.js +12 -1
  39. package/scripts/builders/catalog-summaries.js +1 -1
  40. package/scripts/builders/recipes.js +1 -1
  41. package/scripts/check-sbom-currency.js +76 -14
  42. package/scripts/refresh-sbom.js +1 -1
  43. package/scripts/run-e2e-scenarios.js +48 -17
  44. package/scripts/sync-package-description.js +74 -0
  45. package/scripts/verify-shipped-tarball.js +18 -7
  46. package/skills/age-gates-child-safety/skill.md +3 -3
  47. package/skills/ai-attack-surface/skill.md +4 -4
  48. package/skills/ai-c2-detection/skill.md +5 -5
  49. package/skills/api-security/skill.md +2 -2
  50. package/skills/attack-surface-pentest/skill.md +4 -4
  51. package/skills/cloud-security/skill.md +3 -3
  52. package/skills/compliance-theater/skill.md +3 -3
  53. package/skills/container-runtime-security/skill.md +3 -3
  54. package/skills/coordinated-vuln-disclosure/skill.md +2 -2
  55. package/skills/defensive-countermeasure-mapping/skill.md +3 -3
  56. package/skills/dlp-gap-analysis/skill.md +5 -5
  57. package/skills/exploit-scoring/skill.md +2 -2
  58. package/skills/framework-gap-analysis/skill.md +4 -4
  59. package/skills/fuzz-testing-strategy/skill.md +2 -2
  60. package/skills/incident-response-playbook/skill.md +3 -3
  61. package/skills/mcp-agent-trust/skill.md +2 -2
  62. package/skills/mlops-security/skill.md +3 -3
  63. package/skills/ot-ics-security/skill.md +3 -3
  64. package/skills/policy-exception-gen/skill.md +3 -3
  65. package/skills/pqc-first/skill.md +2 -2
  66. package/skills/rag-pipeline-security/skill.md +4 -4
  67. package/skills/ransomware-response/skill.md +2 -2
  68. package/skills/sector-energy/skill.md +2 -2
  69. package/skills/sector-federal-government/skill.md +2 -2
  70. package/skills/sector-financial/skill.md +4 -4
  71. package/skills/sector-healthcare/skill.md +3 -3
  72. package/skills/security-maturity-tiers/skill.md +1 -1
  73. package/skills/skill-update-loop/skill.md +6 -6
  74. package/skills/supply-chain-integrity/skill.md +2 -2
  75. package/skills/threat-model-currency/skill.md +8 -8
  76. package/skills/threat-modeling-methodology/skill.md +2 -2
  77. package/skills/webapp-security/skill.md +2 -2
  78. package/skills/zeroday-gap-learn/skill.md +3 -3
  79. package/sources/validators/cve-validator.js +27 -18
@@ -266,6 +266,13 @@ const OUTPUTS = [
266
266
  for (const s of ctx.skills) {
267
267
  const body = ctx.skillBodies[s.name];
268
268
  for (const code of codes) {
269
+ // Skip bare 2-letter ISO codes in free-text matching. `\bID\b`,
270
+ // `\bCA\b`, `\bNO\b` etc. collide with prose words ("the ID",
271
+ // "US-based") and control/countermeasure id grammar (`\bCA\b` matches
272
+ // inside `D3-CA`, `\bSA\b` inside `SA-12`), polluting coverage
273
+ // (Indonesia landed on 41/51 skills). These jurisdictions are mapped
274
+ // via the curated NAME_TO_CODE regulation-name table below instead.
275
+ if (code.length <= 2) continue;
269
276
  const re = new RegExp("\\b" + code + "\\b");
270
277
  if (re.test(body) && !out[code].skills.includes(s.name)) out[code].skills.push(s.name);
271
278
  }
@@ -499,7 +506,7 @@ const OUTPUTS = [
499
506
  {
500
507
  name: "stale-content",
501
508
  file: "stale-content.json",
502
- deps: [isManifest, isAnySkillBody, isAnyCatalog],
509
+ deps: [isManifest, isAnySkillBody, isAnyCatalog, (p) => p === "README.md"],
503
510
  build: (ctx) => {
504
511
  const { buildStaleContent } = require("./builders/stale-content");
505
512
  return buildStaleContent({ root: ctx.root, manifest: ctx.manifest, skills: ctx.skills, catalogFiles: ctx.catalogFiles });
@@ -518,6 +525,10 @@ function loadPriorMeta() {
518
525
  function liveSourceSet(ctx) {
519
526
  const out = new Set();
520
527
  out.add("manifest.json");
528
+ // README.md is consumed by the stale-content builder (badge-count drift), so
529
+ // it must be a hashed source — otherwise a README edit is invisible to
530
+ // --changed and the validate-indexes freshness gate.
531
+ if (fs.existsSync(ABS("README.md"))) out.add("README.md");
521
532
  for (const c of ctx.catalogFiles) out.add(c);
522
533
  for (const s of ctx.skills) out.add(s.path);
523
534
  return out;
@@ -18,7 +18,7 @@ const path = require("path");
18
18
  const CATALOG_PURPOSES = {
19
19
  "cve-catalog.json": "Per-CVE record (CVSS, EPSS, CISA KEV, RWEP, AI-discovery, vendor advisories, framework gaps, ATLAS/ATT&CK mappings). Cross-validated against NVD + CISA KEV + FIRST EPSS via validate-cves.",
20
20
  "cwe-catalog.json": "MITRE CWE entries used by the project (subset with skill citations), with severity hint and category. Pinned to a CWE catalog version.",
21
- "atlas-ttps.json": "MITRE ATLAS TTPs (AML.T0xxx) cited by skills, with tactic, name, description. Pinned to ATLAS v5.6.0 (May 2026).",
21
+ "atlas-ttps.json": "MITRE ATLAS TTPs (AML.T0xxx) cited by skills, with tactic, name, description. Pinned to ATLAS v2026.05 (May 2026).",
22
22
  "d3fend-catalog.json": "MITRE D3FEND countermeasures (D3-xxx) keyed by id, with tactic + name. Pinned to D3FEND v1.3.0 release.",
23
23
  "framework-control-gaps.json": "Per-control framework gap declarations: SI-2, A.8.8, PCI 6.3.3, etc. Each entry names the control, the lag, the evidence CVE, and remediation guidance.",
24
24
  "global-frameworks.json": "Multi-jurisdiction framework registry: per-jurisdiction applicable frameworks × patch_sla / notification_sla / critical_controls / framework_gaps (jurisdiction count is reported by entry_count, not duplicated here). Cross-cutting authority for jurisdiction-clocks index.",
@@ -21,7 +21,7 @@ const RECIPES = [
21
21
  when_to_use: "Before scoping or executing a red-team engagement against a model, agentic system, or AI feature.",
22
22
  typical_jurisdictions: ["US", "EU", "UK", "GLOBAL"],
23
23
  steps: [
24
- { skill: "ai-attack-surface", why: "Comprehensive attack-surface inventory mapped to ATLAS v5.6.0 with gap flags." },
24
+ { skill: "ai-attack-surface", why: "Comprehensive attack-surface inventory mapped to ATLAS v2026.05 with gap flags." },
25
25
  { skill: "ai-c2-detection", why: "Detection coverage for AI-as-C2 (PROMPTFLUX / SesameOp / AI-API egress) before testing." },
26
26
  { skill: "mcp-agent-trust", why: "MCP server trust boundary for the engineering toolchain side of the surface." },
27
27
  { skill: "rag-pipeline-security", why: "RAG ingestion provenance + prompt-injection chain coverage." },
@@ -136,19 +136,42 @@ function checkSbomCurrency(root) {
136
136
  );
137
137
  }
138
138
 
139
- // component-level cross-check. A renamed or version-bumped
140
- // skill that never made it into the SBOM refresh will pass the count
141
- // check (the cardinality is unchanged) but the per-component name +
142
- // version comparison surfaces it. Two component classes are recognised:
143
- //
144
- // 1. Skill components bom-ref begins with "skill:" OR the component
145
- // name matches a manifest.skills[].name. Each one must exist in
146
- // manifest.skills with the same version.
147
- // 2. Vendor components — bom-ref begins with "vendor:". Validated
148
- // against vendor/blamejs/_PROVENANCE.json when present.
149
- //
150
- // Components that don't fit either pattern are surfaced as warnings
151
- // (not errors) so the gate isn't brittle against future component types.
139
+ // The "N catalogs" and "N jurisdictions" free-text counts in the same
140
+ // description string were never validated only the per-catalog entry tokens
141
+ // and the skill count were. Pin them to the live values so a stale
142
+ // description (e.g. after an auto-refresh changed a count) fails the gate.
143
+ const catalogMatch = description.match(/(\d+)\s+catalogs?\b/i);
144
+ if (catalogMatch && Number(catalogMatch[1]) !== liveCatalogs) {
145
+ errors.push(
146
+ `SBOM description catalog count is ${Number(catalogMatch[1])} but live data/ has ${liveCatalogs} catalogs — description is stale; update package.json.description and \`npm run refresh-sbom\``
147
+ );
148
+ }
149
+ const liveJurisdictions = (() => {
150
+ try {
151
+ const gf = JSON.parse(fs.readFileSync(path.join(dataDir, "global-frameworks.json"), "utf8"));
152
+ // Non-underscore top-level keys — the canonical jurisdiction count the
153
+ // README badge and catalog-summaries use.
154
+ return Object.keys(gf).filter((k) => !k.startsWith("_")).length;
155
+ } catch {
156
+ return null;
157
+ }
158
+ })();
159
+ const jurisdictionMatch = description.match(/(\d+)\s+jurisdictions?\b/i);
160
+ if (liveJurisdictions !== null && jurisdictionMatch && Number(jurisdictionMatch[1]) !== liveJurisdictions) {
161
+ errors.push(
162
+ `SBOM description jurisdiction count is ${Number(jurisdictionMatch[1])} but live global-frameworks.json has ${liveJurisdictions} — description is stale; update package.json.description and \`npm run refresh-sbom\``
163
+ );
164
+ }
165
+
166
+ // Component-level cross-check (defense-in-depth). In normal operation
167
+ // refresh-sbom emits NO per-skill "skill:" components — skill drift is caught
168
+ // by the file:skills/<name>/skill.md and file:manifest.json content hashes in
169
+ // the file: component pass below (a bumped or renamed skill changes those
170
+ // bytes). This branch is therefore not exercised by a clean SBOM, but it is
171
+ // retained as a tamper guard: a forged or buggy SBOM that injected a skill
172
+ // component with a stale version (or a skill name no longer in the manifest)
173
+ // is still caught here. Vendor components are validated against
174
+ // vendor/blamejs/_PROVENANCE.json.
152
175
  const components = Array.isArray(sbom.components) ? sbom.components : [];
153
176
  const skillByName = new Map(
154
177
  (manifest.skills || []).map((s) => [s.name, s])
@@ -281,6 +304,45 @@ function checkSbomCurrency(root) {
281
304
  fileComponentsChecked++;
282
305
  }
283
306
 
307
+ // Completeness + bundle-digest integrity. The per-file pass above verifies
308
+ // every RECORDED file: component, but never checked that every SHIPPED file
309
+ // (the package.json.files expansion) actually HAS a component — a
310
+ // newly-shipped file would ship unhashed and silent. And the aggregate
311
+ // bundle digest in metadata.component.hashes[] was never recomputed. Reuse
312
+ // refresh-sbom's exact allowlist expansion + digest so the gate can't drift
313
+ // from the generator.
314
+ try {
315
+ const { expandAllowlist, bundleDigest } = require("./refresh-sbom");
316
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
317
+ const expected = expandAllowlist(pkg.files || []);
318
+ const fileComps = components.filter(
319
+ (c) => typeof c["bom-ref"] === "string" && c["bom-ref"].startsWith("file:")
320
+ );
321
+ const fileCompNames = new Set(fileComps.map((c) => c.name));
322
+ for (const rel of expected) {
323
+ if (!fileCompNames.has(rel)) {
324
+ errors.push(
325
+ `Shipped file "${rel}" (package.json.files) has no file: component in the SBOM — run \`npm run refresh-sbom\``
326
+ );
327
+ }
328
+ }
329
+ // Recompute the aggregate bundle digest from the file: components' recorded
330
+ // SHA-256 hashes and compare to metadata.component.hashes[] (the per-file
331
+ // pass already tied each recorded hash to live bytes).
332
+ const compHashes = (sbom.metadata && sbom.metadata.component && sbom.metadata.component.hashes) || [];
333
+ const recorded = (compHashes.find((h) => h && h.alg === "SHA-256") || {}).content;
334
+ if (recorded && fileComps.length) {
335
+ const recomputed = bundleDigest(fileComps);
336
+ if (recomputed !== recorded) {
337
+ errors.push(
338
+ `SBOM bundle digest mismatch: metadata.component.hashes SHA-256 ${String(recorded).slice(0, 12)}… != recomputed ${recomputed.slice(0, 12)}… from file: components — run \`npm run refresh-sbom\``
339
+ );
340
+ }
341
+ }
342
+ } catch (e) {
343
+ errors.push(`SBOM completeness/bundle-digest check failed: ${e.message}`);
344
+ }
345
+
284
346
  return {
285
347
  ok: errors.length === 0,
286
348
  errors,
@@ -310,6 +372,6 @@ function main() {
310
372
  );
311
373
  }
312
374
 
313
- module.exports = { checkSbomCurrency, resolveRoot };
375
+ module.exports = { checkSbomCurrency, resolveRoot, DESCRIPTION_ENTRY_TOKENS, catalogEntryCount };
314
376
 
315
377
  if (require.main === module) main();
@@ -409,4 +409,4 @@ if (require.main === module) {
409
409
  main();
410
410
  }
411
411
 
412
- module.exports = { buildSbom };
412
+ module.exports = { buildSbom, expandAllowlist, bundleDigest };
@@ -110,6 +110,47 @@ function tryParseJson(s) {
110
110
  return null;
111
111
  }
112
112
 
113
+ // Evaluate a spawnSync result against a scenario's expectations. Pure: takes
114
+ // the raw spawnSync result so the failure logic is unit-testable without
115
+ // spawning a process. Surfaces spawn-level failures (timeout/launch error)
116
+ // that res.status alone hides, and refuses to pass a scenario that binds no
117
+ // assertion.
118
+ function evaluateScenario(scenario, expect, res) {
119
+ const stdout = res.stdout || "";
120
+ const stderr = res.stderr || "";
121
+ const status = res.status;
122
+ const body = tryParseJson(stdout);
123
+ const failures = [];
124
+
125
+ // spawnSync failure channels: a timeout sets res.error (ETIMEDOUT) +
126
+ // res.signal 'SIGTERM' with status null; a launch failure (ENOENT/EACCES)
127
+ // sets res.error with status null. Reading only res.status lets a killed-
128
+ // or-never-launched run masquerade as a plain non-zero exit or a JSON-parse
129
+ // failure, hiding the real cause.
130
+ if (res.error) failures.push(`spawn error: ${res.error.code || res.error.message}`);
131
+ if (res.signal) failures.push(`killed by signal ${res.signal}${res.signal === "SIGTERM" ? " (likely the 60s timeout)" : ""}`);
132
+
133
+ // Assertion floor: every scenario must bind at least one positive check.
134
+ // Without an expect_exit or a json_path_* matcher, both gates below are
135
+ // skipped and the scenario would pass for ANY CLI behavior, including a
136
+ // crash. (stderr_must_not_match is a negative guard and cannot bind
137
+ // behavior on its own, so it does not satisfy the floor.)
138
+ const hasExitAssertion = typeof scenario.expect_exit === "number";
139
+ const hasJsonAssertion = !!(expect.json_path_equals || expect.json_path_present || expect.json_path_min || expect.json_path_match);
140
+ if (!hasExitAssertion && !hasJsonAssertion) {
141
+ failures.push("scenario has no binding assertion (set expect_exit or an expect.json_path_* matcher) — refusing to pass vacuously");
142
+ }
143
+
144
+ if (hasExitAssertion && status !== scenario.expect_exit) {
145
+ failures.push(`exit: want ${scenario.expect_exit}, got ${status}`);
146
+ }
147
+ if (!body && hasJsonAssertion) {
148
+ failures.push(`stdout did not parse as JSON; first 200 chars: ${stdout.slice(0, 200)}`);
149
+ }
150
+ if (body) failures.push(...diffExpect(body, expect, { stdout, stderr, status }));
151
+ return failures;
152
+ }
153
+
113
154
  function runScenario(scenarioPath) {
114
155
  const name = path.basename(scenarioPath);
115
156
  const scenarioFile = path.join(scenarioPath, "scenario.json");
@@ -165,28 +206,16 @@ function runScenario(scenarioPath) {
165
206
  timeout: 60000,
166
207
  });
167
208
 
168
- const stdout = res.stdout || "";
169
- const stderr = res.stderr || "";
170
- const status = res.status;
171
- const body = tryParseJson(stdout);
172
-
173
- const failures = [];
174
- if (typeof scenario.expect_exit === "number" && status !== scenario.expect_exit) {
175
- failures.push(`exit: want ${scenario.expect_exit}, got ${status}`);
176
- }
177
- if (!body && (expect.json_path_equals || expect.json_path_present || expect.json_path_min || expect.json_path_match)) {
178
- failures.push(`stdout did not parse as JSON; first 200 chars: ${stdout.slice(0, 200)}`);
179
- }
180
- if (body) failures.push(...diffExpect(body, expect, { stdout, stderr, status }));
209
+ const failures = evaluateScenario(scenario, expect, res);
181
210
 
182
211
  return {
183
212
  name,
184
213
  description: scenario.description || "",
185
214
  ok: failures.length === 0,
186
- exit_status: status,
215
+ exit_status: res.status,
187
216
  failures,
188
- stdout_preview: stdout.slice(0, 200),
189
- stderr_preview: stderr.slice(0, 200),
217
+ stdout_preview: (res.stdout || "").slice(0, 200),
218
+ stderr_preview: (res.stderr || "").slice(0, 200),
190
219
  };
191
220
  } finally {
192
221
  fs.rmSync(work, { recursive: true, force: true });
@@ -237,4 +266,6 @@ function main() {
237
266
  process.exit(failed.length === 0 ? 0 : 1);
238
267
  }
239
268
 
240
- main();
269
+ module.exports = { evaluateScenario, diffExpect, runScenario };
270
+
271
+ if (require.main === module) main();
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * scripts/sync-package-description.js
5
+ *
6
+ * Regenerate the count-bearing tokens embedded in package.json.description from
7
+ * the live catalogs + manifest, so the description stays in sync when an
8
+ * auto-refresh changes an entry count. refresh-sbom copies the description into
9
+ * sbom.cdx.json, and check-sbom-currency validates every token against the live
10
+ * counts — without this sync, the first refresh that changes a count would fail
11
+ * the SBOM description-token gate on the auto-PR.
12
+ *
13
+ * Targeted, format-preserving: replaces only the integer in each known
14
+ * "<N> <label>" token (skills / catalogs / jurisdictions / per-catalog entry
15
+ * counts). Reuses check-sbom-currency's token table so the two can't drift.
16
+ *
17
+ * Run before refresh-sbom in the refresh apply path (and idempotent locally).
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ const { DESCRIPTION_ENTRY_TOKENS, catalogEntryCount } = require('./check-sbom-currency');
24
+
25
+ function syncPackageDescription(root = path.join(__dirname, '..')) {
26
+ const pkgPath = path.join(root, 'package.json');
27
+ const dataDir = path.join(root, 'data');
28
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
29
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
30
+
31
+ const before = pkg.description || '';
32
+ let desc = before;
33
+
34
+ const liveSkills = Array.isArray(manifest.skills) ? manifest.skills.length : 0;
35
+ const liveCatalogs = fs.readdirSync(dataDir).filter((f) => f.endsWith('.json')).length;
36
+ let liveJurisdictions = null;
37
+ try {
38
+ const gf = JSON.parse(fs.readFileSync(path.join(dataDir, 'global-frameworks.json'), 'utf8'));
39
+ liveJurisdictions = Object.keys(gf).filter((k) => !k.startsWith('_')).length;
40
+ } catch { /* leave null — skip the jurisdiction token */ }
41
+
42
+ // Replace only the integer in "<N> <label>"; `labelRe` is the same (already
43
+ // regex-escaped) pattern check-sbom-currency matches, and $2 preserves the
44
+ // matched label text verbatim.
45
+ const sub = (n, labelRe) => {
46
+ if (n == null) return;
47
+ desc = desc.replace(new RegExp('(\\d+)(\\s+' + labelRe + '\\b)'), String(n) + '$2');
48
+ };
49
+
50
+ sub(liveSkills, 'skills');
51
+ sub(liveCatalogs, 'catalogs?');
52
+ sub(liveJurisdictions, 'jurisdictions?');
53
+ for (const { file, label } of DESCRIPTION_ENTRY_TOKENS) {
54
+ sub(catalogEntryCount(dataDir, file), label);
55
+ }
56
+
57
+ const changed = desc !== before;
58
+ if (changed) {
59
+ pkg.description = desc;
60
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
61
+ }
62
+ return { changed, description: desc };
63
+ }
64
+
65
+ if (require.main === module) {
66
+ const r = syncPackageDescription();
67
+ process.stdout.write(
68
+ r.changed
69
+ ? `package.json description synced from live counts:\n ${r.description}\n`
70
+ : 'package.json description already in sync with live counts.\n'
71
+ );
72
+ }
73
+
74
+ module.exports = { syncPackageDescription };
@@ -119,9 +119,15 @@ module.exports = {
119
119
  const ROOT = path.resolve(__dirname, "..");
120
120
 
121
121
  function emit(msg) { process.stdout.write(`[verify-shipped-tarball] ${msg}\n`); }
122
+ // Sentinel thrown by fail() so the script body's try/finally still runs its
123
+ // temp-dir cleanup. process.exit() would preempt the finally, leaking the
124
+ // npm-pack temp dir on every run (predeploy gate + `npm test`). Abort by
125
+ // throwing instead and set the exit code via process.exitCode.
126
+ const ABORT = Symbol("verify-shipped-tarball:abort");
122
127
  function fail(msg, code = 1) {
123
128
  process.stderr.write(`[verify-shipped-tarball] FAIL: ${msg}\n`);
124
- process.exit(code);
129
+ process.exitCode = code;
130
+ throw ABORT;
125
131
  }
126
132
 
127
133
  // Gate the script body behind require.main === module so tests can
@@ -374,15 +380,20 @@ try {
374
380
  emit(`tarball verify result: ${pass}/${total} pass, ${fail_count} fail, ${miss} missing`);
375
381
  if (fail_count === 0 && miss === 0 && pass === total) {
376
382
  emit(`PASS — shipped tarball is internally consistent`);
377
- process.exit(0);
383
+ process.exitCode = 0;
384
+ } else {
385
+ for (const f of failures.slice(0, 10)) emit(` - ${f}`);
386
+ if (failures.length > 10) emit(` ... and ${failures.length - 10} more`);
387
+ emit(`FAIL — shipped tarball would be broken on every fresh install. Refusing to publish.`);
388
+ process.exitCode = 1;
378
389
  }
379
- for (const f of failures.slice(0, 10)) emit(` - ${f}`);
380
- if (failures.length > 10) emit(` ... and ${failures.length - 10} more`);
381
- emit(`FAIL shipped tarball would be broken on every fresh install. Refusing to publish.`);
382
- process.exit(1);
390
+ } catch (e) {
391
+ // ABORT is the fail() sentinel cleanup still runs via finally below. Any
392
+ // other error is unexpected: let finally run, then re-propagate it.
393
+ if (e !== ABORT) throw e;
383
394
  } finally {
384
395
  // Best-effort cleanup; leave on failure for diagnostics.
385
- if (process.exitCode === 0) {
396
+ if (process.exitCode === 0 || process.exitCode === undefined) {
386
397
  try { fs.rmSync(tmpRoot, { recursive: true, force: true }); } catch {}
387
398
  } else {
388
399
  emit(`temp dir preserved for inspection: ${tmpRoot}`);
@@ -58,7 +58,7 @@ forward_watch:
58
58
  - AI product age policy enforcement — Character.ai litigation (2024 child-suicide complaint) testing duty-of-care for AI companion apps; ChatGPT / Claude / Gemini under-13 / under-18 enforcement evolving via FTC + state AG actions
59
59
  - France SREN (Securing and Regulating the Digital Space) Act 2024 — ARCOM age-verification referential for adult content services; double-anonymity model under deployment
60
60
  - 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
61
- last_threat_review: "2026-05-11"
61
+ last_threat_review: "2026-06-10"
62
62
  discovery_mode: "standalone" # operator-reached via `exceptd brief age-gates-child-safety` or `exceptd ask`; not chained into any playbook's direct.skill_chain by design
63
63
  ---
64
64
 
@@ -125,13 +125,13 @@ Classical security and privacy frameworks (NIST 800-53 r5, ISO/IEC 27001:2022, S
125
125
 
126
126
  ## TTP Mapping
127
127
 
128
- This skill is primarily a compliance + privacy-engineering skill rather than a technical-exploit skill. There are no ATLAS-catalogued AI-attack TTPs that are child-specific as of v5.6.0, and most relevant attacker activity intersects general ATT&CK techniques rather than child-targeted novel TTPs. The relevant mapping is therefore narrower and explicitly flagged as such — `atlas_refs` is empty by design, not omission.
128
+ This skill is primarily a compliance + privacy-engineering skill rather than a technical-exploit skill. There are no ATLAS-catalogued AI-attack TTPs that are child-specific as of v2026.05, and most relevant attacker activity intersects general ATT&CK techniques rather than child-targeted novel TTPs. The relevant mapping is therefore narrower and explicitly flagged as such — `atlas_refs` is empty by design, not omission.
129
129
 
130
130
  | ID | Source | Technique | Child-Safeguarding Relevance | Gap Flag |
131
131
  |---|---|---|---|---|
132
132
  | T1078 | ATT&CK Enterprise | Valid Accounts | Account takeover targeting child accounts (compromised parental controls; sextortion via stolen accounts; grooming via account hijack) — child accounts are under-protected because MFA roll-out lags adult user populations. | NIST 800-53 AC-2 + COPPA / AADC / Children's Code silent on MFA-for-child requirement; the AC-2 gap entry in `data/framework-control-gaps.json` covers AI-service-principals not child identities. Hand off to `identity-assurance` for AAL2+ on child accounts where vendor terms permit. |
133
133
  | T1567 | ATT&CK Enterprise | Exfiltration Over Web Service | Child PI exfiltrated via AI-tool / SaaS egress — additional liability under COPPA (no behavioral-ad use of under-13 PI), AADC (DPIA failure), GDPR Art. 8 (no lawful basis), DPDPA (default-VPC bypass), CN PIPL Art. 31 (child PI = sensitive PI requiring separate consent). | Hand off to `dlp-gap-analysis` for child-PI as a protected data class; COPPA / AADC / Children's Code do not name DLP technical controls; the SOC2-CC7 anomaly-detection gap entry applies. |
134
- | AI-generated CSAM creation / distribution | Not catalogued in ATLAS or ATT&CK as of v5.6.0 | Generative-AI image / video synthesis depicting children | Direct criminal exposure under 18 U.S.C. §§2251, 2252, 2252A, 2256 (Protect Act / Mash-Up Act framework); mandatory NCMEC reporting per §2258A. Multiple 2024-2025 prosecutions (US v. Anderegg WD-Wis 2024 — first federal AI-CSAM prosecution; UK National Crime Agency campaign 2024-2025). | No formal TTP class. Evidence stream: NCMEC CyberTipline reports + EU IWF reports. Hand off to `ai-attack-surface` for generative-model content-policy red-team and to `incident-response-playbook` for reporting workflow. |
134
+ | AI-generated CSAM creation / distribution | Not catalogued in ATLAS or ATT&CK as of v2026.05 | Generative-AI image / video synthesis depicting children | Direct criminal exposure under 18 U.S.C. §§2251, 2252, 2252A, 2256 (Protect Act / Mash-Up Act framework); mandatory NCMEC reporting per §2258A. Multiple 2024-2025 prosecutions (US v. Anderegg WD-Wis 2024 — first federal AI-CSAM prosecution; UK National Crime Agency campaign 2024-2025). | No formal TTP class. Evidence stream: NCMEC CyberTipline reports + EU IWF reports. Hand off to `ai-attack-surface` for generative-model content-policy red-team and to `incident-response-playbook` for reporting workflow. |
135
135
  | AI chatbot grooming / harmful-content engagement with children | Not catalogued | Long-context AI chatbot interactions with children steering toward harm | Research and litigation evidence: Character.ai litigation 2024 (FL wrongful-death suit alleging companion-chatbot contribution to minor suicide; additional 2024-2025 complaints); UK NCA campaign 2024 documenting grooming attempts via AI chatbots; ESRC / RAND research 2024-2025. | No formal TTP class. EU DSA Art. 28 + UK OSA + AU OSA + KOSA-if-enacted all frame this as a platform duty-of-care obligation. Hand off to `ai-risk-management` for AI-product age policy enforcement. |
136
136
 
137
137
  **Honest scope statement (no fabricated TTP IDs).** This skill does not invent TTP IDs to fill gaps in the ATLAS or ATT&CK matrices. AI-generated CSAM and AI-chatbot-mediated harm to children are real-world threat classes documented through prosecution records, NCMEC / IWF reporting, and litigation — not novel ATLAS techniques. Citation is to the evidence stream, not to a TTP ID.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ai-attack-surface
3
3
  version: "1.0.0"
4
- description: Comprehensive AI/ML attack surface assessment mapped to MITRE ATLAS v5.6.0 with explicit framework gap flags
4
+ description: Comprehensive AI/ML attack surface assessment mapped to MITRE ATLAS v2026.05 with explicit framework gap flags
5
5
  triggers:
6
6
  - ai attack surface
7
7
  - prompt injection
@@ -59,7 +59,7 @@ forward_watch:
59
59
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — Chroma vector DB CWE-190 + CWE-362 chain by haehae; impacts RAG vector store integrity; track patch and downstream RAG advisory
60
60
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — NVIDIA Megatron Bridge overly permissive allowed list by Satoki Tsuji; AI training-stack supply-chain exposure; track patch and SBOM advisory
61
61
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — NVIDIA Megatron Bridge path traversal by haehae; AI training-stack file-system trust boundary; track patch and SBOM advisory
62
- last_threat_review: "2026-05-17"
62
+ last_threat_review: "2026-06-10"
63
63
  ---
64
64
 
65
65
  # AI Attack Surface Assessment
@@ -156,7 +156,7 @@ AI-assisted reconnaissance is observed at 36,000 probes per second per campaign.
156
156
  | SOC 2 | CC6 (Logical and Physical Access) | Access control via IAM, authentication, authorization. Prompt injection is an access control failure that routes around CC6 entirely — the authorized model account takes the action, not the attacker. Audit trails show the model's service account performed the action. |
157
157
  | SOC 2 | CC7 (System Operations) | Anomaly detection for system operations. No guidance for AI API baseline, AI C2 detection, or PROMPTFLUX behavioral patterns. |
158
158
  | PCI DSS 4.0 | 6.4.1 | Web application protection (WAF). WAFs operate on HTTP request/response patterns. They have no semantic understanding of prompt injection embedded in JSON `message` fields. |
159
- | MITRE ATT&CK | Enterprise | Does not include prompt injection as a technique. AI-as-C2 (SesameOp) is not in ATT&CK as of mid-2026. ATLAS v5.6.0 covers these but is not part of SOC detection engineering programs that are ATT&CK-mapped. |
159
+ | MITRE ATT&CK | Enterprise | Does not include prompt injection as a technique. AI-as-C2 (SesameOp) is not in ATT&CK as of mid-2026. ATLAS v2026.05 covers these but is not part of SOC detection engineering programs that are ATT&CK-mapped. |
160
160
  | NIST AI RMF | MEASURE 2.5 | Measure AI risks during operation. Provides a framework for thinking about AI risk but no specific controls for prompt injection, MCP supply chain, or AI-as-C2. |
161
161
  | EU NIS2 | Art. 21(2)(d) (supply-chain security) + Art. 21(2)(e) (security in acquisition, development and maintenance) | "Appropriate and proportionate" supply-chain language. Member-state transpositions (BSI IT-SiG 2.0, ANSSI) do not enumerate MCP servers or LLM API providers as in-scope supply-chain components. An essential entity can meet NIS2 supplier-management obligations with traditional SaaS vendor reviews while having zero coverage of AI-assistant tool ecosystems. |
162
162
  | EU DORA | Art. 8 (ICT asset management) + Art. 28 (ICT third-party register) + Art. 30 (key contractual provisions) | Financial-entity ICT third-party language scoped to traditional ICT providers. LLM API providers acting as data processors for prompt content and developer-environment MCP servers are not enumerated as ICT third-party service providers. ESAs RTS on subcontracting (JC 2024/53) is silent on AI/ML SaaS dependency classes. |
@@ -170,7 +170,7 @@ AI-assisted reconnaissance is observed at 36,000 probes per second per campaign.
170
170
 
171
171
  ---
172
172
 
173
- ## TTP Mapping (MITRE ATLAS v5.6.0)
173
+ ## TTP Mapping (MITRE ATLAS v2026.05)
174
174
 
175
175
  | ATLAS ID | Technique | Framework Coverage | Gap Description | Exploitation Example |
176
176
  |---|---|---|---|---|
@@ -49,7 +49,7 @@ d3fend_refs:
49
49
  - D3-NI
50
50
  - D3-NTA
51
51
  - D3-NTPM
52
- last_threat_review: "2026-05-17"
52
+ last_threat_review: "2026-06-10"
53
53
  ---
54
54
 
55
55
  # AI C2 Detection
@@ -330,13 +330,13 @@ level: medium
330
330
 
331
331
  ---
332
332
 
333
- ## TTP Mapping (MITRE ATLAS v5.6.0 + MITRE ATT&CK)
333
+ ## TTP Mapping (MITRE ATLAS v2026.05 + MITRE ATT&CK)
334
334
 
335
335
  | ID | Source | Technique | C2 Relevance | Gap Flag — Which Detection Control Fails |
336
336
  |---|---|---|---|---|
337
- | AML.T0096 | ATLAS v5.6.0 | LLM API as covert C2 / LLM Integration Abuse | Direct: SesameOp encodes commands and exfiltrated data in prompt and completion fields against api.openai.com, api.anthropic.com, generativelanguage.googleapis.com. AI provider domain is the relay, not the attacker C2 endpoint. | NIST-800-53-SC-7 (Boundary Protection) — AI provider domains are allowlisted in most enterprise egress for legitimate developer and product use, so boundary inspection cannot distinguish benign developer prompts from C2-encoded prompts. See SC-7 entry in `data/framework-control-gaps.json` — real requirement is SDK-level prompt logging with identity binding, anomaly detection on prompt-shape and token-volume, and an allowlist that enumerates the sanctioned business reason per identity. Boundary-only SC-7 evidence is incomplete for any org with AI API access in production. |
338
- | AML.T0017 | ATLAS v5.6.0 | Discover ML Model Ontology — adversary maps the deployed LLM's family, system-prompt structure, guardrail surface via inference-API probing | PROMPTFLUX queries public LLMs to generate per-execution evasion code; PROMPTSTEAL uses LLMs to prioritise exfiltration targets — both depend on first discovering what the target model will answer. The inference API is the discovery surface. | NIST-800-53-SI-3 fails — there is no static signature for code generated per-event by a public LLM. NIST-800-53-SI-4 fails as commonly deployed — no AI-API behavioural baseline per process/identity. |
339
- | AML.T0016 | ATLAS v5.6.0 | Obtain Capabilities: Develop Capabilities — adversary use of inference APIs to generate / refine malware, evasion, phishing payloads | PROMPTFLUX and PROMPTSTEAL both consume public LLMs as a real-time capability-development service. The inference API is doing weaponization work for the adversary. | NIST-800-53-SI-3 fails for the same reason. SC-7 boundary control treats the AI provider as allowlisted SaaS. |
337
+ | AML.T0096 | ATLAS v2026.05 | LLM API as covert C2 / LLM Integration Abuse | Direct: SesameOp encodes commands and exfiltrated data in prompt and completion fields against api.openai.com, api.anthropic.com, generativelanguage.googleapis.com. AI provider domain is the relay, not the attacker C2 endpoint. | NIST-800-53-SC-7 (Boundary Protection) — AI provider domains are allowlisted in most enterprise egress for legitimate developer and product use, so boundary inspection cannot distinguish benign developer prompts from C2-encoded prompts. See SC-7 entry in `data/framework-control-gaps.json` — real requirement is SDK-level prompt logging with identity binding, anomaly detection on prompt-shape and token-volume, and an allowlist that enumerates the sanctioned business reason per identity. Boundary-only SC-7 evidence is incomplete for any org with AI API access in production. |
338
+ | AML.T0017 | ATLAS v2026.05 | Discover ML Model Ontology — adversary maps the deployed LLM's family, system-prompt structure, guardrail surface via inference-API probing | PROMPTFLUX queries public LLMs to generate per-execution evasion code; PROMPTSTEAL uses LLMs to prioritise exfiltration targets — both depend on first discovering what the target model will answer. The inference API is the discovery surface. | NIST-800-53-SI-3 fails — there is no static signature for code generated per-event by a public LLM. NIST-800-53-SI-4 fails as commonly deployed — no AI-API behavioural baseline per process/identity. |
339
+ | AML.T0016 | ATLAS v2026.05 | Obtain Capabilities: Develop Capabilities — adversary use of inference APIs to generate / refine malware, evasion, phishing payloads | PROMPTFLUX and PROMPTSTEAL both consume public LLMs as a real-time capability-development service. The inference API is doing weaponization work for the adversary. | NIST-800-53-SI-3 fails for the same reason. SC-7 boundary control treats the AI provider as allowlisted SaaS. |
340
340
  | T1071 | ATT&CK | Application Layer Protocol (C2) | AI C2 traffic is standard HTTPS REST to api.openai.com or equivalent. Application-protocol C2 detection that looks for DGA, unusual TLS, or beaconing does not fire. | SC-7 boundary control sees only the destination domain (allowlisted) — no protocol anomaly to alert on. Detection requires identity-bound prompt content inspection, which SC-7 as written does not require. |
341
341
  | T1102 | ATT&CK | Web Service (C2 via legitimate web service) | AI API endpoints are exactly the "legitimate web service used as C2" pattern that T1102 describes — but at scale and pre-allowlisted in nearly every enterprise. | SOC 2 CC7 anomaly-detection control: AI API traffic shares the SaaS blind spot — typically not baselined per process or identity. ISO 27001 A.8.16 monitoring activities: no guidance for AI-API-shaped traffic. |
342
342
  | T1568 | ATT&CK | Dynamic Resolution | AI provider responses can carry encoded instructions that dynamically determine the next-hop behaviour for the malware (effectively model-mediated dynamic resolution of the next attacker instruction). | No standard DNS-tunnelling or DGA detection applies — the "resolution" happens inside an HTTPS payload to a trusted endpoint. SC-7 cannot see it without SDK-level prompt + response logging. |
@@ -67,7 +67,7 @@ forward_watch:
67
67
  - NGINX Rift CVE-2026-42945 (disclosed 2026-05-13, source depthfirst) — KEV-watch predicted CISA KEV listing by 2026-05-29; track for active-exploitation confirmation and patch advisory affecting API gateway / reverse-proxy deployments
68
68
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — LiteLLM 3-bug SSRF + Code Injection chain by k3vg3n; LLM-proxy API surface; track upstream patch and CVE assignments
69
69
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — LiteLLM full SSRF + Code Injection by Out Of Bounds (Byung Young Yi); duplicate-class with the k3vg3n entry; track unified patch advisory
70
- last_threat_review: "2026-05-18"
70
+ last_threat_review: "2026-06-10"
71
71
  ---
72
72
 
73
73
  # API Security Assessment
@@ -126,7 +126,7 @@ APIs are now the integration substrate of every non-trivial system. The mid-2026
126
126
 
127
127
  ---
128
128
 
129
- ## TTP Mapping (MITRE ATT&CK Enterprise + ATLAS v5.6.0)
129
+ ## TTP Mapping (MITRE ATT&CK Enterprise + ATLAS v2026.05)
130
130
 
131
131
  | TTP ID | Technique | API Manifestation | CWE Root-Causes | Framework Coverage |
132
132
  |---|---|---|---|---|
@@ -60,7 +60,7 @@ d3fend_refs:
60
60
  - D3-CSPP
61
61
  - D3-EAL
62
62
  - D3-NTA
63
- last_threat_review: "2026-05-11"
63
+ last_threat_review: "2026-06-10"
64
64
  ---
65
65
 
66
66
  # Attack Surface Management + Penetration Testing
@@ -115,17 +115,17 @@ A pen test scoped to layers 1 and (partly) 7 — i.e. "web app + network + nomin
115
115
  | CBEST (Bank of England / PRA / FCA) | Whole framework | UK equivalent to TIBER-EU for systemically important financial firms. Same lag pattern as TIBER-EU. CBEST-certified providers are not required to demonstrate competence in AI-surface attack emulation as of mid-2026. |
116
116
  | Australian ISM (Information Security Manual) + ACSC Essential 8 | ISM controls on penetration testing; Essential 8 Maturity Level 3 testing requirements | Essential 8 mandates regular testing of mitigation strategies (patching, app control, MFA, etc.). The testing requirements do not extend to AI-API egress as C2, MCP trust, or RAG poisoning. ISM control set is network/endpoint centric. |
117
117
  | ISO/IEC 27001:2022 | A.5.34 (Privacy and protection of PII) — note: the actually relevant clause for independent review is **A.5.35 (Independent review of information security)** and **A.8.29 (Security testing in development and acceptance)** | A.5.35 requires independent review of the information security approach at planned intervals or when significant changes occur. The clause is methodology-agnostic — auditors accept a network/web pen test as evidence even when AI surfaces are in production. A.8.29 mandates security testing of new and changed information systems, but does not define what an adequate test of an AI system looks like. |
118
- | MITRE ATT&CK Enterprise (v19.0) | Whole matrix | The enterprise matrix does not contain prompt-injection as a technique. AI-as-C2 (SesameOp pattern) is absent from ATT&CK as of mid-2026. Adversary emulation programs that are ATT&CK-only and not ATLAS-extended will not include the mid-2026 dominant new tradecraft in their playbooks. ATLAS v5.6.0 covers it — but ATLAS is not yet a standard requirement for pen testing certification or scoping. |
118
+ | MITRE ATT&CK Enterprise (v19.1) | Whole matrix | The enterprise matrix does not contain prompt-injection as a technique. AI-as-C2 (SesameOp pattern) is absent from ATT&CK as of mid-2026. Adversary emulation programs that are ATT&CK-only and not ATLAS-extended will not include the mid-2026 dominant new tradecraft in their playbooks. ATLAS v2026.05 covers it — but ATLAS is not yet a standard requirement for pen testing certification or scoping. |
119
119
 
120
120
  > Global coverage note: the above table spans US (NIST 800-115, ATT&CK), EU (NIS2, TIBER-EU under DORA), UK (CBEST), AU (ISM/Essential 8), and ISO 27001:2022. US-only pen test scoping is incomplete.
121
121
 
122
122
  ---
123
123
 
124
- ## TTP Mapping (MITRE ATLAS v5.6.0 + MITRE ATT&CK v19.0)
124
+ ## TTP Mapping (MITRE ATLAS v2026.05 + MITRE ATT&CK v19.1)
125
125
 
126
126
  Pen testers must emulate both classical and AI-class chains. The table below maps the kill-chain phases a mid-2026 adversary emulation engagement must cover.
127
127
 
128
- | Phase | Classical TTP (ATT&CK v19.0) | AI-Class TTP (ATLAS v5.6.0) | Framework Gap Flag |
128
+ | Phase | Classical TTP (ATT&CK v19.1) | AI-Class TTP (ATLAS v2026.05) | Framework Gap Flag |
129
129
  |---|---|---|---|
130
130
  | Reconnaissance | T1595 (Active Scanning) — implied by T1190 setup | AML.TA0002 (Reconnaissance tactic) — model card / dataset / API endpoint discovery, system-prompt probing | NIST 800-115 §3.x recon guidance is network-only |
131
131
  | Initial Access | T1190 (Exploit Public-Facing Application) | AML.T0051 (LLM Prompt Injection) — entered via PR description, support ticket, retrieved doc | OWASP WSTG covers webapp; not prompt-injection as entry vector |
@@ -70,7 +70,7 @@ forward_watch:
70
70
  - AWS Bedrock, Azure OpenAI, GCP Vertex AI shared-responsibility documentation drift — each major CSP refreshes the AI-service responsibility line every 6–12 months; track for control-mapping breakage
71
71
  - eBPF-based runtime detection coverage of confidential-computing enclaves (AWS Nitro Enclaves, Azure Confidential VMs, GCP Confidential Space) — partial visibility is a tracked detection gap
72
72
  - CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories
73
- last_threat_review: "2026-05-11"
73
+ last_threat_review: "2026-06-10"
74
74
  ---
75
75
 
76
76
  # Cloud Security (mid-2026)
@@ -131,8 +131,8 @@ Cloud is where AI runs. Every consequential AI service — OpenAI, Anthropic, Go
131
131
  | Cloud data exfiltration | T1530 — Data from Cloud Storage Object | ATT&CK Enterprise | Public S3 / GCS / Blob storage discovery via Wiz-style external attack-surface scan; legitimate IAM principal exfil via federated workload; cross-tenant boundary failure on SaaS | NIST 800-53 SC-28 (encryption at rest) does not address access-policy errors; CWE-200, CWE-732, CWE-862 |
132
132
  | Cloud-facing application | T1190 — Exploit Public-Facing Application | ATT&CK Enterprise | API Gateway / Load Balancer / managed-WAF-bypass; managed-database exposure (RDS / SQL DB / Cloud SQL public IP); container-registry public image abuse; Lambda / Cloud Functions / Azure Functions endpoint exploit | NIST 800-53 SC-7 perimeter assumption inadequate; CSA CCM AIS-04 and IVS-08 partial; CWE-1188 (Insecure Default Initialization) |
133
133
  | Cloud-credential exposure | T1552 — Unsecured Credentials (incl. T1552.001 Files, T1552.005 Cloud Instance Metadata API, T1552.007 Container API) | ATT&CK Enterprise | IMDSv1 SSRF on EC2 / GCE; static cloud credentials in git / images / env vars; container API and kubeconfig theft; workload-identity-federation trust-policy abuse | CWE-798 (hardcoded credentials), CWE-200; NIST 800-53 IA-5 method-neutral |
134
- | AI model registry / cloud-hosted model | AML.T0010 — ML Supply Chain Compromise | ATLAS v5.6.0 | Bedrock / SageMaker custom model from poisoned upstream; Azure ML model registry tampering; Vertex Model Garden mirror tampering; HF model pulled into Bedrock / SageMaker / Vertex with weights backdoor | CSA CCM CCC-09 (vendor / supply chain) silent on model-supply-chain specifics; SLSA / in-toto / Sigstore for models still maturing |
135
- | Cloud inference API abuse / model extraction | AML.T0017 — Discover ML Model Ontology (inference-API probing for system-prompt, guardrail, model-family signal against cloud-hosted endpoints); AML.T0016 — Obtain Capabilities: Develop Capabilities (downstream weaponization) | ATLAS v5.6.0 | Programmatic query of Bedrock / Azure OpenAI / Vertex endpoint to extract model behaviour, training-data inference, system-prompt leakage | No cloud-specific ATLAS control mapping for inference-API rate-limit / anomaly detection; chain to `ai-attack-surface` |
134
+ | AI model registry / cloud-hosted model | AML.T0010 — ML Supply Chain Compromise | ATLAS v2026.05 | Bedrock / SageMaker custom model from poisoned upstream; Azure ML model registry tampering; Vertex Model Garden mirror tampering; HF model pulled into Bedrock / SageMaker / Vertex with weights backdoor | CSA CCM CCC-09 (vendor / supply chain) silent on model-supply-chain specifics; SLSA / in-toto / Sigstore for models still maturing |
135
+ | Cloud inference API abuse / model extraction | AML.T0017 — Discover ML Model Ontology (inference-API probing for system-prompt, guardrail, model-family signal against cloud-hosted endpoints); AML.T0016 — Obtain Capabilities: Develop Capabilities (downstream weaponization) | ATLAS v2026.05 | Programmatic query of Bedrock / Azure OpenAI / Vertex endpoint to extract model behaviour, training-data inference, system-prompt leakage | No cloud-specific ATLAS control mapping for inference-API rate-limit / anomaly detection; chain to `ai-attack-surface` |
136
136
 
137
137
  **Note on ATT&CK Enterprise cloud-platform sub-techniques.** ATT&CK Enterprise has cloud-platform-specific matrices (IaaS, SaaS, Office 365, Azure AD / Entra ID, Google Workspace). T1078.004 (Cloud Accounts), T1552.005 (Cloud Instance Metadata API), T1552.007 (Container API), T1190 with cloud-service variants, T1530 with managed-storage variants are the most operationally relevant. The frontmatter pins the parent IDs; analysis should descend to the sub-technique appropriate to the cloud(s) in scope.
138
138
 
@@ -21,7 +21,7 @@ framework_gaps:
21
21
  - ALL-PROMPT-INJECTION-ACCESS-CONTROL
22
22
  - FedRAMP-Rev5-Moderate
23
23
  - CMMC-2.0-Level-2
24
- last_threat_review: "2026-05-22"
24
+ last_threat_review: "2026-06-10"
25
25
  ---
26
26
 
27
27
  # Compliance Theater Detection
@@ -78,7 +78,7 @@ The pre-analyzed gaps for these controls live in the framework-gap-analysis skil
78
78
 
79
79
  ---
80
80
 
81
- ## TTP Mapping (MITRE ATLAS v5.6.0 and ATT&CK)
81
+ ## TTP Mapping (MITRE ATLAS v2026.05 and ATT&CK)
82
82
 
83
83
  Each theater pattern below maps to one or more attacker TTPs in `data/atlas-ttps.json` and MITRE ATT&CK Enterprise. The mapping is what distinguishes theater from genuine compliance: a control claimed as compensating must map to a TTP it actually disrupts.
84
84
 
@@ -92,7 +92,7 @@ Each theater pattern below maps to one or more attacker TTPs in `data/atlas-ttps
92
92
  | Vendor/Third-Party Risk Theater — AI APIs (Pattern 6) | AML.T0010 (ML Supply Chain Compromise) | MCP servers and LLM APIs sit outside the vendor-management scope |
93
93
  | Security Awareness Theater — AI Phishing (Pattern 7) | T1566 (Phishing), AML.T0016 (Obtain Capabilities: Develop Capabilities — misuse of public AI APIs for payload crafting) | AI-generated content evades grammar/style heuristics and template-matching detectors |
94
94
 
95
- Source-of-truth TTP catalog: `data/atlas-ttps.json` (pinned to MITRE ATLAS v5.6.0, May 2026). Any theater claim in an assessment must cite at least one TTP ID from that catalog or an ATT&CK Enterprise ID — claims without a mapped TTP are orphaned controls and are rejected.
95
+ Source-of-truth TTP catalog: `data/atlas-ttps.json` (pinned to MITRE ATLAS v2026.05, May 2026). Any theater claim in an assessment must cite at least one TTP ID from that catalog or an ATT&CK Enterprise ID — claims without a mapped TTP are orphaned controls and are rejected.
96
96
 
97
97
  ---
98
98
 
@@ -57,7 +57,7 @@ d3fend_refs:
57
57
  - D3-IOPR
58
58
  forward_watch:
59
59
  - Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — NVIDIA Container Toolkit container escape ($50K award) by chompie / IBM X-Force XOR; high-severity container/hypervisor boundary break; track patch and KEV add post-embargo
60
- last_threat_review: "2026-05-15"
60
+ last_threat_review: "2026-06-10"
61
61
  ---
62
62
 
63
63
  # Container + Kubernetes Runtime Security (mid-2026)
@@ -124,9 +124,9 @@ State of standards baselines:
124
124
  | Container escape to host | T1611 — Escape to Host | ATT&CK Enterprise | Kernel LPE (Copy Fail CVE-2026-31431, Dirty Frag CVE-2026-43284 family); historical runc CVE-2024-21626 LeakyVessels family; cgroup v1 release_agent legacy abuses; abuse of overly permissive capabilities (`CAP_SYS_ADMIN`, `CAP_SYS_MODULE`) | NIST 800-190 predates kernel-LPE-as-container-escape as the dominant vector. Defense requires kernel patching cadence (hand off to `kernel-lpe-triage`) plus seccomp default profile, capability drops, read-only rootfs, and runtime detection. None of these are framework-mandated. |
125
125
  | Privilege escalation within the container | T1068 — Exploitation for Privilege Escalation | ATT&CK Enterprise | In-container kernel LPE (yields host root via T1611 chain); abuse of writable hostPath; abuse of mounted Docker socket | Method-neutral framework controls; the actual control is seccomp + dropped capabilities + read-only rootfs + non-root runAsUser, all enforced by PSS-Restricted profile |
126
126
  | Exploit public-facing K8s component | T1190 — Exploit Public-Facing Application | ATT&CK Enterprise | Exposed kube-apiserver (rare but seen on self-managed clusters); exposed kubelet read-only port (10255) or read/write port (10250) without authentication; exposed Kubernetes Dashboard with no auth; exposed Argo CD or Jenkins on the cluster; ingress controller CVEs (ingress-nginx CVE-2025 family) | NSA/CISA Hardening Guide v1.2 addresses control-plane exposure; managed services close this by default; self-managed clusters in CI/government still expose these |
127
- | Compromised container image at a public/private registry | AML.T0010 — ML Supply Chain Compromise (umbrella) | ATLAS v5.6.0 | Poisoned base image; backdoored model-serving image; typosquatted MCP server in a sidecar; AI-pipeline-specific (KServe / vLLM / Triton image with embedded malicious payload) | ATLAS classifies; no framework mandates signature verification at admission. Hand off the build-side provenance to `supply-chain-integrity`; the container-runtime control is `ClusterImagePolicy` enforcement |
127
+ | Compromised container image at a public/private registry | AML.T0010 — ML Supply Chain Compromise (umbrella) | ATLAS v2026.05 | Poisoned base image; backdoored model-serving image; typosquatted MCP server in a sidecar; AI-pipeline-specific (KServe / vLLM / Triton image with embedded malicious payload) | ATLAS classifies; no framework mandates signature verification at admission. Hand off the build-side provenance to `supply-chain-integrity`; the container-runtime control is `ClusterImagePolicy` enforcement |
128
128
 
129
- ATT&CK Containers matrix (sub-matrix, since 2021) and ATT&CK for Kubernetes (Microsoft's threat matrix, 2020, since absorbed conceptually into ATT&CK Containers) are both relevant prior art. The Enterprise IDs above are canonical in ATLAS v5.6.0 alignment and pass the linter regex `^T\d{4}(\.\d{3})?$`.
129
+ ATT&CK Containers matrix (sub-matrix, since 2021) and ATT&CK for Kubernetes (Microsoft's threat matrix, 2020, since absorbed conceptually into ATT&CK Containers) are both relevant prior art. The Enterprise IDs above are canonical in ATLAS v2026.05 alignment and pass the linter regex `^T\d{4}(\.\d{3})?$`.
130
130
 
131
131
  CWE cross-walk (see `data/cwe-catalog.json`):
132
132