@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.
- package/AGENTS.md +5 -5
- package/ARCHITECTURE.md +3 -3
- package/CHANGELOG.md +28 -0
- package/CONTEXT.md +2 -2
- package/README.md +6 -6
- package/agents/threat-researcher.md +2 -2
- package/bin/exceptd.js +41 -8
- package/data/_indexes/_meta.json +41 -40
- package/data/_indexes/activity-feed.json +240 -240
- package/data/_indexes/catalog-summaries.json +3 -3
- package/data/_indexes/currency.json +64 -64
- package/data/_indexes/jurisdiction-map.json +31 -158
- package/data/_indexes/recipes.json +1 -1
- package/data/_indexes/section-offsets.json +510 -510
- package/data/_indexes/summary-cards.json +33 -33
- package/data/_indexes/token-budget.json +200 -200
- package/data/atlas-ttps.json +7 -7
- package/data/attack-techniques.json +5 -5
- package/data/framework-control-gaps.json +3 -3
- package/lib/auto-discovery.js +15 -9
- package/lib/collectors/library-author.js +26 -9
- package/lib/collectors/secrets.js +8 -1
- package/lib/cvss.js +108 -0
- package/lib/lint-skills.js +6 -1
- package/lib/playbook-runner.js +17 -4
- package/lib/prefetch.js +97 -5
- package/lib/refresh-external.js +25 -13
- package/lib/schemas/manifest.schema.json +1 -1
- package/lib/schemas/skill-frontmatter.schema.json +1 -1
- package/lib/validate-indexes.js +5 -0
- package/lib/version-pins.js +3 -3
- package/manifest-snapshot.json +2 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +124 -124
- package/orchestrator/pipeline.js +16 -4
- package/package.json +1 -1
- package/sbom.cdx.json +170 -140
- package/scripts/build-indexes.js +12 -1
- package/scripts/builders/catalog-summaries.js +1 -1
- package/scripts/builders/recipes.js +1 -1
- package/scripts/check-sbom-currency.js +76 -14
- package/scripts/refresh-sbom.js +1 -1
- package/scripts/run-e2e-scenarios.js +48 -17
- package/scripts/sync-package-description.js +74 -0
- package/scripts/verify-shipped-tarball.js +18 -7
- package/skills/age-gates-child-safety/skill.md +3 -3
- package/skills/ai-attack-surface/skill.md +4 -4
- package/skills/ai-c2-detection/skill.md +5 -5
- package/skills/api-security/skill.md +2 -2
- package/skills/attack-surface-pentest/skill.md +4 -4
- package/skills/cloud-security/skill.md +3 -3
- package/skills/compliance-theater/skill.md +3 -3
- package/skills/container-runtime-security/skill.md +3 -3
- package/skills/coordinated-vuln-disclosure/skill.md +2 -2
- package/skills/defensive-countermeasure-mapping/skill.md +3 -3
- package/skills/dlp-gap-analysis/skill.md +5 -5
- package/skills/exploit-scoring/skill.md +2 -2
- package/skills/framework-gap-analysis/skill.md +4 -4
- package/skills/fuzz-testing-strategy/skill.md +2 -2
- package/skills/incident-response-playbook/skill.md +3 -3
- package/skills/mcp-agent-trust/skill.md +2 -2
- package/skills/mlops-security/skill.md +3 -3
- package/skills/ot-ics-security/skill.md +3 -3
- package/skills/policy-exception-gen/skill.md +3 -3
- package/skills/pqc-first/skill.md +2 -2
- package/skills/rag-pipeline-security/skill.md +4 -4
- package/skills/ransomware-response/skill.md +2 -2
- package/skills/sector-energy/skill.md +2 -2
- package/skills/sector-federal-government/skill.md +2 -2
- package/skills/sector-financial/skill.md +4 -4
- package/skills/sector-healthcare/skill.md +3 -3
- package/skills/security-maturity-tiers/skill.md +1 -1
- package/skills/skill-update-loop/skill.md +6 -6
- package/skills/supply-chain-integrity/skill.md +2 -2
- package/skills/threat-model-currency/skill.md +8 -8
- package/skills/threat-modeling-methodology/skill.md +2 -2
- package/skills/webapp-security/skill.md +2 -2
- package/skills/zeroday-gap-learn/skill.md +3 -3
- package/sources/validators/cve-validator.js +27 -18
package/scripts/build-indexes.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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();
|
package/scripts/refresh-sbom.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
338
|
-
| AML.T0017 | ATLAS
|
|
339
|
-
| AML.T0016 | ATLAS
|
|
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-
|
|
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
|
|
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-
|
|
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.
|
|
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
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
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
|
|