@blamejs/exceptd-skills 0.21.13 → 0.21.15

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 (33) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/data/_indexes/_meta.json +23 -23
  3. package/data/_indexes/activity-feed.json +94 -94
  4. package/data/_indexes/catalog-summaries.json +6 -6
  5. package/data/_indexes/chains.json +2 -2
  6. package/data/_indexes/currency.json +28 -28
  7. package/data/_indexes/section-offsets.json +200 -200
  8. package/data/_indexes/summary-cards.json +14 -14
  9. package/data/_indexes/token-budget.json +99 -99
  10. package/data/attack-techniques.json +3 -1
  11. package/data/cve-catalog.json +107 -1
  12. package/data/cwe-catalog.json +2 -1
  13. package/data/framework-control-gaps.json +7 -1
  14. package/data/zeroday-lessons.json +99 -2
  15. package/manifest.json +81 -81
  16. package/package.json +2 -2
  17. package/sbom.cdx.json +69 -54
  18. package/scripts/check-skill-catalog-facts.js +304 -0
  19. package/scripts/predeploy.js +8 -0
  20. package/skills/ai-attack-surface/skill.md +2 -2
  21. package/skills/cloud-iam-incident/skill.md +6 -6
  22. package/skills/compliance-theater/skill.md +6 -6
  23. package/skills/container-runtime-security/skill.md +2 -2
  24. package/skills/exploit-scoring/skill.md +16 -14
  25. package/skills/framework-gap-analysis/skill.md +5 -5
  26. package/skills/idp-incident-response/skill.md +4 -4
  27. package/skills/kernel-lpe-triage/skill.md +15 -15
  28. package/skills/policy-exception-gen/skill.md +4 -4
  29. package/skills/sector-federal-government/skill.md +3 -3
  30. package/skills/security-maturity-tiers/skill.md +5 -5
  31. package/skills/supply-chain-integrity/skill.md +3 -3
  32. package/skills/threat-model-currency/skill.md +6 -6
  33. package/skills/zeroday-gap-learn/skill.md +8 -8
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ /**
5
+ * Skill catalog-facts gate. Exit 0 consistent, 1 when a skill states a CVE fact
6
+ * that differs from data/cve-catalog.json.
7
+ *
8
+ * A markdown table row is compared when the first cell that names a CVE names
9
+ * exactly one catalog CVE. Its cells are compared by column header: CVSS, RWEP, a KEV column (Yes/No, and the listing date when the
10
+ * cell gives one), a public-exploit column (Yes/No), and an AI column. An
11
+ * "AI-Discovered" column is compared with ai_discovered; an AI column that also
12
+ * covers acceleration, enablement or weaponization is compared with
13
+ * ai_discovered OR ai_assisted_weaponization. A prose line that names exactly one
14
+ * catalog CVE is compared on "RWEP <n>", "CVSS <n.n>", "KEV-listed <date>" and
15
+ * "not KEV-listed". On any line, the parentheses that follow a CVE id are read
16
+ * as that CVE's values, so a list such as "CVE-A (90), CVE-B (Dirty Frag, 38,
17
+ * CVSS 7.8)" is compared CVE by CVE. A cell or line that states no comparable value is skipped,
18
+ * and so is a CVE the catalog does not hold, a row whose CVE cell only points
19
+ * to one ("No vendor CVE; see CVE-..."), and a value the text marks as superseded.
20
+ */
21
+
22
+ const fs = require("node:fs");
23
+ const path = require("node:path");
24
+
25
+ const ROOT = path.resolve(__dirname, "..");
26
+ const CVE = /CVE-\d{4}-\d{4,}/g;
27
+ const DATE = /\d{4}-\d{2}-\d{2}/;
28
+
29
+ const splitRow = (line) => line.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
30
+ const isDivider = (cells) => cells.every((c) => /^:?-{2,}:?$/.test(c));
31
+ const uniqueCves = (text) => [...new Set(text.match(CVE) || [])];
32
+
33
+ /** Leading Yes or No of a cell; anything else ("Partial", "varies") is not compared. */
34
+ function yesNo(cell) {
35
+ if (/^yes\b/i.test(cell)) return true;
36
+ if (/^no\b/i.test(cell)) return false;
37
+ return null;
38
+ }
39
+
40
+ /**
41
+ * An AI cell's claim: a leading Yes/No, a declarative form the column counts as
42
+ * yes ("AI-discovered", "AI-weaponized"), or "Human-discovered" as no.
43
+ */
44
+ function aiValue(cell, yesForm) {
45
+ const v = yesNo(cell);
46
+ if (v !== null) return v;
47
+ if (yesForm.test(cell)) return true;
48
+ if (/^human[- ](?:discovered|found)\b/i.test(cell)) return false;
49
+ return null;
50
+ }
51
+
52
+ /** The CVSS major version a text names ("CVSS 4.0", "CVSS v3.1"), or null. */
53
+ function cvssVersion(vector) {
54
+ const m = /^CVSS:(\d)/.exec(vector || "");
55
+ return m ? m[1] : null;
56
+ }
57
+
58
+ function columnKind(header) {
59
+ const h = header.toLowerCase().replace(/[`*?]/g, "").trim();
60
+ if (/^cvss\b/.test(h)) return "cvss";
61
+ if (/^rwep\b/.test(h)) return "rwep";
62
+ if (/\bkev\b/.test(h)) return "kev";
63
+ if (/\bpoc\b|public exploit/.test(h)) return "poc";
64
+ if (/^ai[- ]discovered$/.test(h)) return "ai_discovered";
65
+ if (/^ai\b/.test(h)) return "ai_any";
66
+ return null;
67
+ }
68
+
69
+ /** A cell's text with Markdown emphasis and code marks removed. */
70
+ const plain = (cell) => cell.replace(/[*_`]/g, "").trim();
71
+
72
+ function compareCell(kind, raw, e, say) {
73
+ const cell = plain(raw);
74
+ if (kind === "cvss") {
75
+ const m = /^(\d{1,2}(?:\.\d)?)\b/.exec(cell);
76
+ if (m && Number(m[1]) !== e.cvss_score) say(`CVSS ${m[1]}, catalog ${e.cvss_score}`);
77
+ } else if (kind === "rwep") {
78
+ const m = /^(\d{1,3})(?![\d+–-]|\.\d)/.exec(cell);
79
+ if (m && Number(m[1]) !== e.rwep_score) say(`RWEP ${m[1]}, catalog ${e.rwep_score}`);
80
+ } else if (kind === "kev") {
81
+ const v = yesNo(cell);
82
+ if (v === null) return;
83
+ if (v !== Boolean(e.cisa_kev)) say(`KEV "${cell.slice(0, 40)}", catalog ${e.cisa_kev ? `listed ${e.cisa_kev_date}` : "not listed"}`);
84
+ else if (v) {
85
+ const d = DATE.exec(cell);
86
+ if (d && e.cisa_kev_date && d[0] !== e.cisa_kev_date) say(`KEV date ${d[0]}, catalog ${e.cisa_kev_date}`);
87
+ }
88
+ } else if (kind === "poc") {
89
+ // "Partial" is the catalog's own granular value for a conceptual or chain-only
90
+ // exploit, which it scores as poc_available true.
91
+ const v = /^partial\b/i.test(cell) ? true : yesNo(cell);
92
+ if (v !== null && v !== Boolean(e.poc_available)) say(`public exploit "${cell.slice(0, 40)}", catalog poc_available ${e.poc_available}`);
93
+ } else if (kind === "ai_discovered") {
94
+ const v = aiValue(cell, /^ai[- ](?:discovered|assisted)\b/i);
95
+ if (v !== null && v !== Boolean(e.ai_discovered)) say(`AI-discovered "${cell.slice(0, 40)}", catalog ai_discovered ${e.ai_discovered}`);
96
+ } else if (kind === "ai_any") {
97
+ const v = aiValue(cell, /^ai[- ](?:discovered|assisted|accelerated|enabled|weaponi[sz]ed)\b/i);
98
+ const expected = Boolean(e.ai_discovered) || Boolean(e.ai_assisted_weaponization);
99
+ if (v !== null && v !== expected) say(`AI "${cell.slice(0, 40)}", catalog ai_discovered ${e.ai_discovered} / ai_assisted_weaponization ${e.ai_assisted_weaponization}`);
100
+ }
101
+ }
102
+
103
+ /** A value the text marks as superseded ("the initial CVSS 9.8 was withdrawn"). */
104
+ function historical(line, m) {
105
+ const before = line.slice(Math.max(0, m.index - 24), m.index);
106
+ const after = line.slice(m.index + m[0].length, m.index + m[0].length + 24);
107
+ return /\b(?:initial|original|previous(?:ly)?|earlier|former|prior)\s*$/i.test(before) || /^\s*(?:was|were|is)\s+(?:withdrawn|superseded|corrected|replaced)\b/i.test(after);
108
+ }
109
+
110
+ function compareProse(text, e, say) {
111
+ const line = plain(text);
112
+ for (const m of line.matchAll(/RWEP(?: score)?(?:\s*(?:of|is|=|:))?\s*(\d{1,3})(?![\d+–-]|\.\d)/gi)) {
113
+ if (historical(line, m)) continue;
114
+ if (Number(m[1]) !== e.rwep_score) say(`RWEP ${m[1]}, catalog ${e.rwep_score}`);
115
+ }
116
+ for (const m of line.matchAll(/CVSS(?:\s*v?(\d)(?:\.\d)?\b)?(?:\s*(?:score|base score))?(?:\s*(?:of|is|=|:))?\s*(\d{1,2}\.\d)\b/gi)) {
117
+ const version = m[1] || null;
118
+ if (version && version !== cvssVersion(e.cvss_vector)) continue;
119
+ if (historical(line, m)) continue;
120
+ if (Number(m[2]) !== e.cvss_score) say(`CVSS ${m[2]}, catalog ${e.cvss_score}`);
121
+ }
122
+ for (const m of line.matchAll(/\bKEV[- ]listed(?: on)? (\d{4}-\d{2}-\d{2})/g)) {
123
+ if (e.cisa_kev_date && m[1] !== e.cisa_kev_date) say(`KEV date ${m[1]}, catalog ${e.cisa_kev_date}`);
124
+ if (!e.cisa_kev) say(`says KEV-listed ${m[1]}, catalog not listed`);
125
+ }
126
+ if (/\bnot (?:yet )?(?:CISA )?KEV[- ]listed\b|\bnot (?:yet )?(?:on|in) (?:the )?(?:CISA )?KEV\b|\bKEV(?: listing)?:? pending\b|\bpending (?:CISA )?KEV\b/i.test(line) && e.cisa_kev) {
127
+ say(`says not KEV-listed, catalog listed ${e.cisa_kev_date}`);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Values in the parentheses that follow a CVE id, or that open with one, belong
133
+ * to that CVE: "CVE-X (Copy Fail, RWEP 90, CVSS 7.8)", "CVE-X (Dirty Frag, 38,
134
+ * CVSS 7.8)" or "(CVE-X, CVSS 7.8 / AV:L)". A bare 1-3 digit item is read as
135
+ * RWEP when the line mentions RWEP.
136
+ */
137
+ const PER_CVE = /(CVE-\d{4}-\d{4,})\s*\(([^()]*)\)|\((CVE-\d{4}-\d{4,})[,;:]\s*([^()]*)\)/g;
138
+
139
+ const UNATTRIBUTED = 'states a score for several CVEs outside per-CVE parentheses; write each as "CVE-X (name, RWEP n, CVSS n.n)"';
140
+
141
+ /** A score statement: "RWEP 53", "CVSS 8.8", "**RWEP:** 53/100". */
142
+ const SCORE = /RWEP(?: score)?\*{0,2}(?:\s*(?:of|is|=|:))?\*{0,2}\s*\d{1,3}(?![\d+–-]|\.\d)|CVSS(?:\s*v?\d(?:\.\d)?\b)?(?:\s*(?:score|base score))?\*{0,2}(?:\s*(?:of|is|=|:))?\*{0,2}\s*\d{1,2}\.\d\b|\b\d{1,3}\/100\b/i;
143
+
144
+ function compareParentheticals(line, catalog, say) {
145
+ const rwepContext = /\bRWEP\b/i.test(line);
146
+ let found = 0;
147
+ for (const raw of line.matchAll(PER_CVE)) {
148
+ const m = raw[1] ? [raw[0], raw[1], raw[2]] : [raw[0], raw[3], raw[4]];
149
+ const e = catalog[m[1]];
150
+ if (!e) continue;
151
+ found++;
152
+ const inner = m[2];
153
+ const tell = (msg) => say(m[1], msg);
154
+ compareProse(inner, e, tell);
155
+ if (rwepContext && !/\bRWEP\b/i.test(inner)) {
156
+ for (const item of inner.split(/[,;]/).map((s) => s.trim())) {
157
+ if (/^\d{1,3}$/.test(item) && Number(item) !== e.rwep_score) tell(`RWEP ${item}, catalog ${e.rwep_score}`);
158
+ }
159
+ }
160
+ }
161
+ return found;
162
+ }
163
+
164
+ /** Factor-table row labels and the rwep_factors key each one scores. */
165
+ const FACTOR_KEYS = [
166
+ [/^cisa kev$/, "cisa_kev"],
167
+ [/^poc public$|^public poc$/, "poc_available"],
168
+ [/^ai[- ](?:assisted|discovered|factor)$/, "ai_factor"],
169
+ [/^active exploitation$/, "active_exploitation"],
170
+ [/^blast radius$/, "blast_radius"],
171
+ [/^patch available$/, "patch_available"],
172
+ [/^live patch available$/, "live_patch_available"],
173
+ [/^reboot required$/, "reboot_required"],
174
+ ];
175
+
176
+ /**
177
+ * A "Factor | Value | Points" row under a heading that names one catalog CVE:
178
+ * the points must equal that CVE's rwep_factors entry, and the RWEP row must
179
+ * equal its rwep_score.
180
+ */
181
+ /** Factor rows whose Value cell states a catalog boolean, with how to read it. */
182
+ const FACTOR_VALUES = [
183
+ [/^cisa kev$/, (e) => Boolean(e.cisa_kev), "cisa_kev"],
184
+ [/^poc public$|^public poc$/, (e) => Boolean(e.poc_available), "poc_available"],
185
+ [/^ai[- ]discovered$/, (e) => Boolean(e.ai_discovered), "ai_discovered"],
186
+ [/^ai[- ](?:assisted|factor)$/, (e) => Boolean(e.ai_discovered) || Boolean(e.ai_assisted_weaponization), "ai_discovered / ai_assisted_weaponization"],
187
+ [/^patch available$/, (e) => Boolean(e.patch_available), "patch_available"],
188
+ [/^live patch available$/, (e) => Boolean(e.live_patch_available), "live_patch_available"],
189
+ [/^reboot required$/, (e) => Boolean(e.patch_required_reboot), "patch_required_reboot"],
190
+ ];
191
+
192
+ function compareFactorRow(cells, rawHeader, e, say) {
193
+ const pointsCol = rawHeader.findIndex((h) => /^\s*points\s*$/i.test(h));
194
+ if (pointsCol < 1) return false;
195
+ const label = (cells[0] || "").replace(/\*/g, "").trim().toLowerCase();
196
+ const valueCol = rawHeader.findIndex((h) => /^\s*value\s*$/i.test(h));
197
+ const valueRule = FACTOR_VALUES.find(([re]) => re.test(label));
198
+ if (valueCol > 0 && valueRule) {
199
+ const cell = plain(cells[valueCol] || "");
200
+ const v = /^partial\b/i.test(cell) && valueRule[2] === "poc_available" ? true : yesNo(cell);
201
+ if (v !== null && v !== valueRule[1](e)) say(`factor table ${cells[0].replace(/\*/g, "").trim()} value "${cell.slice(0, 40)}", catalog ${valueRule[2]} ${valueRule[1](e)}`);
202
+ }
203
+ const m = /^\*{0,2}\s*([+\-−]?\d{1,3})\b/.exec(cells[pointsCol] || "");
204
+ if (!m) return false;
205
+ const points = Number(m[1].replace("−", "-"));
206
+ if (/^rwep(?: total)?$/.test(label)) {
207
+ if (points !== e.rwep_score) say(`factor table RWEP ${points}, catalog ${e.rwep_score}`);
208
+ return true;
209
+ }
210
+ const hit = FACTOR_KEYS.find(([re]) => re.test(label));
211
+ if (!hit || !e.rwep_factors || typeof e.rwep_factors[hit[1]] !== "number") return false;
212
+ if (points !== e.rwep_factors[hit[1]]) say(`factor table ${cells[0].replace(/\*/g, "").trim()} ${points}, catalog rwep_factors.${hit[1]} ${e.rwep_factors[hit[1]]}`);
213
+ return true;
214
+ }
215
+
216
+ function checkSkill(file, catalog) {
217
+ const seen = new Set();
218
+ const failures = [];
219
+ const push = (msg) => { if (!seen.has(msg)) { seen.add(msg); failures.push(msg); } };
220
+ let compared = 0;
221
+ let header = null;
222
+ let rawHeader = null;
223
+ let sectionCve = null;
224
+ const lines = fs.readFileSync(file, "utf8").split(/\r?\n/);
225
+ lines.forEach((line, i) => {
226
+ const where = `${path.relative(ROOT, file).replace(/\\/g, "/")}:${i + 1}`;
227
+ if (/^#{1,6}\s/.test(line)) {
228
+ const headingIds = uniqueCves(line);
229
+ sectionCve = headingIds.length === 1 && catalog[headingIds[0]] ? headingIds[0] : null;
230
+ }
231
+ compared += compareParentheticals(line, catalog, (id, msg) => push(`${where} ${id}: ${msg}`));
232
+ if (/^\s*\|/.test(line)) {
233
+ const cells = splitRow(line);
234
+ if (header === null) { rawHeader = cells; header = cells.map(columnKind); return; }
235
+ if (isDivider(cells)) return;
236
+ if (sectionCve && uniqueCves(line).length === 0) {
237
+ if (compareFactorRow(cells, rawHeader, catalog[sectionCve], (msg) => push(`${where} ${sectionCve}: ${msg}`))) compared++;
238
+ return;
239
+ }
240
+ const idCell = cells.find((c) => uniqueCves(c).length > 0) || "";
241
+ if (/^(?:no|n\/a)\b/i.test(idCell) || /\bsee\s+CVE-/i.test(idCell)) return;
242
+ const ids = uniqueCves(idCell);
243
+ if (ids.length > 1) {
244
+ const known = ids.filter((id) => catalog[id]);
245
+ const scored = header.some((kind, k) => (kind === "cvss" || kind === "rwep") && /^\d/.test(plain(cells[k] || "")));
246
+ if (known.length && scored) push(`${where} ${ids.join(", ")}: ${UNATTRIBUTED}`);
247
+ return;
248
+ }
249
+ if (ids.length !== 1 || !catalog[ids[0]]) return;
250
+ const e = catalog[ids[0]];
251
+ compared++;
252
+ header.forEach((kind, k) => {
253
+ if (kind && cells[k] !== undefined) compareCell(kind, cells[k], e, (msg) => push(`${where} ${ids[0]}: ${msg}`));
254
+ });
255
+ return;
256
+ }
257
+ header = null;
258
+ const ids = uniqueCves(line);
259
+ if (ids.length > 1) {
260
+ const rest = plain(line.replace(PER_CVE, " "));
261
+ const m = SCORE.exec(rest);
262
+ if (ids.some((id) => catalog[id]) && m && !historical(rest, m)) push(`${where} ${ids.join(", ")}: ${UNATTRIBUTED}`);
263
+ return;
264
+ }
265
+ if (ids.length !== 1 || !catalog[ids[0]]) return;
266
+ compared++;
267
+ compareProse(line, catalog[ids[0]], (msg) => push(`${where} ${ids[0]}: ${msg}`));
268
+ });
269
+ return { failures, compared };
270
+ }
271
+
272
+ function check(skillsDir, catalog) {
273
+ const failures = [];
274
+ let compared = 0;
275
+ let files = 0;
276
+ for (const name of fs.readdirSync(skillsDir).sort()) {
277
+ const file = path.join(skillsDir, name, "skill.md");
278
+ if (!fs.existsSync(file)) continue;
279
+ files++;
280
+ const r = checkSkill(file, catalog);
281
+ failures.push(...r.failures);
282
+ compared += r.compared;
283
+ }
284
+ return { failures, compared, files };
285
+ }
286
+
287
+ function main() {
288
+ const skillsDir = process.argv[2] || path.join(ROOT, "skills");
289
+ const catalogFile = process.argv[3] || path.join(ROOT, "data", "cve-catalog.json");
290
+ const catalog = JSON.parse(fs.readFileSync(catalogFile, "utf8"));
291
+ const { failures, compared, files } = check(skillsDir, catalog);
292
+ if (failures.length) {
293
+ console.error("Skill catalog facts: FAIL");
294
+ for (const f of failures) console.error(` ${f}`);
295
+ console.error(`\n${failures.length} skill ${failures.length === 1 ? "statement differs" : "statements differ"} from data/cve-catalog.json. Update the skill text to the catalog value.`);
296
+ process.exitCode = 1;
297
+ return;
298
+ }
299
+ console.log(`Skill catalog facts: PASS — ${compared} CVE rows and lines across ${files} skills match data/cve-catalog.json`);
300
+ }
301
+
302
+ if (require.main === module) main();
303
+
304
+ module.exports = { check, checkSkill, columnKind, yesNo };
@@ -218,6 +218,14 @@ const GATES = [
218
218
  args: [path.join(ROOT, "scripts", "check-ism-control-references.js")],
219
219
  ciJobName: "Data integrity (catalog + manifest snapshot)",
220
220
  },
221
+ {
222
+ // A skill that states a CVE's CVSS, RWEP, KEV status, public-exploit or
223
+ // AI-discovery value must agree with data/cve-catalog.json.
224
+ name: "Skill catalog facts (skill CVE statements vs. the catalog)",
225
+ command: process.execPath,
226
+ args: [path.join(ROOT, "scripts", "check-skill-catalog-facts.js")],
227
+ ciJobName: "Data integrity (catalog + manifest snapshot)",
228
+ },
221
229
  ];
222
230
 
223
231
  function runGate(gate) {
@@ -60,7 +60,7 @@ forward_watch:
60
60
  - 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
61
61
  - 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
62
62
  - 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
63
- last_threat_review: "2026-09-18"
63
+ last_threat_review: "2026-09-28"
64
64
  ---
65
65
 
66
66
  # AI Attack Surface Assessment
@@ -141,7 +141,7 @@ AI-assisted reconnaissance is observed at 36,000 probes per second per campaign.
141
141
 
142
142
  ### 11. AI-Discovered + AI-Weaponized Supply-Chain Worms
143
143
 
144
- **CVE-2026-45321** — Mini Shai-Hulud TanStack npm worm (CVSS 9.6, ~150M weekly downloads across 42 @tanstack/* packages, CISA KEV pending). Disclosed 2026-05-11. The attack chain — Pwn-Request via `pull_request_target` on TanStack's bundle-size workflow, pnpm-store cache poisoning under the `actions/cache` key, and OIDC-token theft on the next main push — is engineering-grade and weaponizes three independently-benign primitives. While attribution (TeamPCP) records no AI-assisted exploit development for this specific instance, the worm pattern is exactly what AML.T0017-class capability development now produces at AI cadence: chained CI/CD primitives that no individual component owner recognises as exploitable. Treat the @tanstack/* surface as an exemplar of the broader AML.T0010 (AI Supply Chain Compromise) threat applied to JS toolchains that the AI assistant ecosystem depends on.
144
+ **CVE-2026-45321** — Mini Shai-Hulud TanStack npm worm (CVSS 9.6, ~150M weekly downloads across 42 @tanstack/* packages, KEV-listed 2026-05-27). Disclosed 2026-05-11. The attack chain — Pwn-Request via `pull_request_target` on TanStack's bundle-size workflow, pnpm-store cache poisoning under the `actions/cache` key, and OIDC-token theft on the next main push — is engineering-grade and weaponizes three independently-benign primitives. While attribution (TeamPCP) records no AI-assisted exploit development for this specific instance, the worm pattern is exactly what AML.T0017-class capability development now produces at AI cadence: chained CI/CD primitives that no individual component owner recognises as exploitable. Treat the @tanstack/* surface as an exemplar of the broader AML.T0010 (AI Supply Chain Compromise) threat applied to JS toolchains that the AI assistant ecosystem depends on.
145
145
 
146
146
  ---
147
147
 
@@ -73,7 +73,7 @@ forward_watch:
73
73
  - AU ISM update enumerating cloud non-human-principal credential hygiene with explicit bearer-token TTL ceilings
74
74
  - DORA TLPT (JC 2024/40 RTS) first-cycle aggregate findings on cloud-account-compromise scenarios
75
75
  - AWS, GCP, and Azure shared-fate / shared-responsibility recalibration for federated-trust hygiene
76
- last_threat_review: "2026-05-15"
76
+ last_threat_review: "2026-09-28"
77
77
  ---
78
78
 
79
79
  # Cloud IAM Incident Response (mid-2026)
@@ -164,11 +164,11 @@ The playbook `cloud-iam-incident` operationalises this skill into a seven-phase
164
164
 
165
165
  | Surface / CVE Class | CVSS | RWEP | CISA KEV | PoC Public | AI-Discovered | Active Exploitation | Patch / Mitigation Available | Sector-Aware Detection |
166
166
  |---|---|---|---|---|---|---|---|---|
167
- | CVE-2024-1709 ScreenConnect auth bypass (cloud-runtime adjacent) | 10.0 | 95 | Yes | Yes | n/a | Confirmed mass exploitation | Vendor patch | EDR + identity-protection |
168
- | CVE-2024-21626 runc container escape | 8.6 | 88 | Yes | Yes | n/a | Confirmed | Vendor patch | Container-runtime telemetry |
169
- | CVE-2024-3094 xz-utils supply-chain backdoor | 10.0 | 70 | Yes | Yes | No | Suspected | Vendor patch | SBOM-driven detection |
170
- | CVE-2026-20182 Cisco SD-WAN cloud-edge | 9.1 | 90 | Yes | Yes | Yes | Confirmed | Vendor patch | Network telemetry |
171
- | CVE-2026-30623 Anthropic MCP STDIO (cloud-hosted MCP servers) | 8.4 | 87 | Pending | Yes | Yes | Suspected | Vendor patch + config hardening | MCP-aware telemetry |
167
+ | CVE-2024-1709 ScreenConnect auth bypass (cloud-runtime adjacent) | 10.0 | 75 | Yes (2024-02-22) | Yes | No | Confirmed mass exploitation | Vendor patch | EDR + identity-protection |
168
+ | CVE-2024-21626 runc container escape | 8.6 | 35 | No | Yes | No | None recorded | Vendor patch | Container-runtime telemetry |
169
+ | CVE-2024-3094 xz-utils supply-chain backdoor | 10.0 | 45 | No | Yes | No | Suspected | Vendor patch | SBOM-driven detection |
170
+ | CVE-2026-20182 Cisco SD-WAN cloud-edge | 10.0 | 65 | Yes (2026-05-14) | No | No | Confirmed | Vendor patch | Network telemetry |
171
+ | CVE-2026-30623 Anthropic MCP STDIO (cloud-hosted MCP servers) | 8.8 | 30 | No | Yes | No | Suspected | Vendor patch + config hardening | MCP-aware telemetry |
172
172
  | Leaked access key in public repo (Snowflake-AA24-class) | n/a (config) | high (exploited within minutes) | n/a | Documented at scale | n/a | Confirmed mass exploitation | Configuration hardening + key rotation + scraper-bot countermeasures | GitGuardian / Trufflehog firehose; behavioural detection on CreateAccessKey + key-use anomaly |
173
173
  | IMDSv1 SSRF / metadata exfil | n/a (design class) | high | n/a | Documented at scale | n/a | Confirmed ongoing | IMDSv2 enforcement | VPC Flow Logs + CloudTrail Insights |
174
174
  | OIDC trust-policy wildcard subject claim (fork-PR-to-takeover) | n/a (config class) | high | n/a | Demonstrated 2024-2026 | n/a | Suspected ongoing | Trust-policy tightening + per-environment OIDC trust | Trust-policy static analysis + assume-role telemetry |
@@ -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-09-18"
24
+ last_threat_review: "2026-09-28"
25
25
  ---
26
26
 
27
27
  # Compliance Theater Detection
@@ -102,16 +102,16 @@ The theater patterns most acutely under attack today are those backed by high-RW
102
102
 
103
103
  | Theater pattern | Evidence CVE | CVSS | RWEP tier | KEV | Public PoC | AI-accelerated | Live-patchable | Active exploitation |
104
104
  |---|---|---|---|---|---|---|---|---|
105
- | Patch Management Theater | CVE-2026-31431 (Copy Fail) | High | Critical | Yes | Yes (732 bytes, deterministic) | Yes (AI-discovered) | Yes (kpatch/livepatch) | Confirmed |
106
- | Vendor Management Theater (AI APIs / MCP) | CVE-2026-30615 (Windsurf MCP local-vector RCE) | 8.0 | 35 | No | Partial | No | Yes (IDE update) | Suspected |
105
+ | Patch Management Theater | CVE-2026-31431 (Copy Fail) | 7.8 | 90 | Yes (2026-05-01) | Yes (732 bytes, deterministic) | Yes (AI-discovered) | Yes (kpatch/livepatch) | Confirmed |
106
+ | Vendor Management Theater (AI APIs / MCP) | CVE-2026-30615 (Windsurf MCP local-vector RCE) | 8.0 | 35 | No | Yes | No | Yes (IDE update) | Suspected |
107
107
  | Access Control Theater (AI agents) | CVE-2025-53773 (Copilot YOLO-mode RCE) | 7.8 | 30 | No | Yes (demonstrated) | Yes (AI tooling enables) | Yes (SaaS push / IDE update) | Suspected |
108
- | Network Segmentation Theater (IPsec) | CVE-2026-43284 (Dirty Frag) | High | High | Pending | Partial | No | Limited (subsystem-dependent) | Suspected |
109
- | Patch Management Theater (Bug-Family Sequel) | CVE-2026-46300 (Fragnesia) | 7.8 | 20 (today) / 55+ on KEV | No (candidate) | Yes (one-liner vs /usr/bin/su) | No | Yes (kpatch / canonical-livepatch / KernelCare) | None observed |
108
+ | Network Segmentation Theater (IPsec) | CVE-2026-43284 (Dirty Frag) | 8.8 | 53 | No | Yes (chain component) | Yes (AI-assisted discovery) | Limited (kpatch RHEL-only) | Suspected |
109
+ | Patch Management Theater (Bug-Family Sequel) | CVE-2026-46300 (Fragnesia) | 7.8 | 35 (60 if KEV-listed) | No (candidate) | Yes (one-liner vs /usr/bin/su) | Yes (AI-assisted discovery) | Yes (kpatch / canonical-livepatch / KernelCare) | None observed |
110
110
  | Incident Response Theater (AI pipeline) | SesameOp campaign + AML.T0096 | N/A | High | N/A | ATLAS-documented | Yes | N/A | Confirmed campaign |
111
111
  | Change Management Theater (AI models) | Continuous provider updates | N/A | Medium | N/A | N/A | N/A | N/A | Ongoing (uncontrolled) |
112
112
  | Security Awareness Theater (AI phishing) | AI-generated phishing baseline (82.6%) | N/A | High | N/A | Operational | Yes | N/A | Confirmed (industry-wide) |
113
113
 
114
- The first three rows (Critical / Critical / High RWEP with public PoC or active exploitation) are the most dangerous theater patterns to leave un-remediated. Pull authoritative scoring from `data/exploit-availability.json` and `data/cve-catalog.json` before producing the output.
114
+ Rows with a high RWEP and a public PoC or confirmed exploitation, led by Copy Fail at RWEP 90, are the most dangerous theater patterns to leave un-remediated. Pull authoritative scoring from `data/exploit-availability.json` and `data/cve-catalog.json` before producing the output.
115
115
 
116
116
  ---
117
117
 
@@ -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-09-18"
60
+ last_threat_review: "2026-09-28"
61
61
  ---
62
62
 
63
63
  # Container + Kubernetes Runtime Security (mid-2026)
@@ -145,7 +145,7 @@ CWE cross-walk (see `data/cwe-catalog.json`):
145
145
  | Class / CVE | CVSS | RWEP | CISA KEV | PoC Public | AI-Discovered | Active Exploitation | Patch / Mitigation | Admission-Detectable | Runtime-Detectable (Falco/Tetragon) |
146
146
  |---|---|---|---|---|---|---|---|---|---|
147
147
  | Host-kernel LPE as container escape — Copy Fail (CVE-2026-31431) | 7.8 | 90 (see `cve-catalog.json`) | Yes (2026-05-01, due 2026-05-15) | Yes — 732-byte script | Yes | Confirmed | Kernel patch + live-patch (kpatch/livepatch/kGraft) on supported distros; reboot rolling fleet on others | No (admission doesn't see kernel ops) | Yes — Falco/Tetragon catches the post-escape host operations; the in-kernel write itself is invisible to eBPF |
148
- | Container-runtime CVE class — runc CVE-2024-21626 ("LeakyVessels") family | 8.6 | varies (historical reference) | Yes (at time of disclosure) | Yes | No (manual disclosure) | Patched in modern fleets; brownfield self-managed clusters lag | runc / containerd / CRI-O upgrade | Partial — admission can require minimum runtime versions via node-feature labels | Yes — Tetragon can enforce SIGKILL on the abuse syscall sequence |
148
+ | Container-runtime CVE class — runc CVE-2024-21626 ("LeakyVessels") family | 8.6 | 35 (see `cve-catalog.json`) | No | Yes | No (manual disclosure) | Patched in modern fleets; brownfield self-managed clusters lag | runc / containerd / CRI-O upgrade | Partial — admission can require minimum runtime versions via node-feature labels | Yes — Tetragon can enforce SIGKILL on the abuse syscall sequence |
149
149
  | Misconfigured PSS — `pod-security.kubernetes.io/enforce: privileged` on a workload namespace | n/a (class) | n/a | n/a | Trivial — `kubectl run --privileged` | Operator misconfig + AI-coding-assistant template drift | Routinely observed in incident response 2024–2026 | Set namespace label to `restricted`; remediate the workload | Yes — Kyverno PolicyReport + Kubescape surface this; PSA itself enforces on admit if label set |
150
150
  | Misconfigured RBAC — ServiceAccount with `create/patch` on `pods` or `secrets` cluster-wide | n/a (class) | n/a | n/a | Trivial — `can-i --list --as=system:serviceaccount:...` | Operator misconfig | Routinely observed | Replace wildcard ClusterRoles with scoped Roles; deny `automountServiceAccountToken: true` by default | Yes — Kyverno + OPA policies; kube-bench check | Yes — Falco detects token use from unexpected ServiceAccount |
151
151
  | Exposed kubelet 10250 / 10255 | n/a (class) | n/a | n/a | Trivial — Shodan / Censys queries; Peirates | n/a | Confirmed in self-managed / CI / lab clusters | Network policy default-deny; kubelet authentication + authorization; firewall | No (network-layer control) | Yes — Cilium L7 policy + Falco network rule |
@@ -21,7 +21,7 @@ attack_refs: []
21
21
  framework_gaps:
22
22
  - CWE-Top-25-2024-meta
23
23
  - CIS-Controls-v8-Control7
24
- last_threat_review: "2026-09-18"
24
+ last_threat_review: "2026-09-28"
25
25
  ---
26
26
 
27
27
  # Real-World Exploit Priority (RWEP) Scoring
@@ -91,11 +91,11 @@ How each RWEP factor maps to a real CVE in `data/cve-catalog.json`:
91
91
  | CVE | KEV | PoC | AI-Discovered | Active Exploitation | Blast Radius | Patch | Live Patch | RWEP | CVSS |
92
92
  |---|---|---|---|---|---|---|---|---|---|
93
93
  | CVE-2026-31431 (Copy Fail) | Yes | Yes (732-byte) | Yes | Confirmed | All Linux ≥ 4.14 (30) | Yes | Yes (kpatch/livepatch/kGraft) | 90 | 7.8 |
94
- | CVE-2026-43284 (Dirty Frag ESP/IPsec) | No | Yes (chain) | No | Suspected | IPsec-using systems (18) | Yes | RHEL-only kpatch | 38 | 7.8 |
95
- | CVE-2026-43500 (Dirty Frag RxRPC) | No | Yes (chain) | No | Suspected | RxRPC-loaded systems | Yes | Partial | 32 | 7.6 |
96
- | CVE-2026-46300 (Fragnesia) | No (likely candidate) | Yes (one-liner vs /usr/bin/su) | No | None observed | Linux >= 5.10 with esp4/esp6/rxrpc loaded (25) | Yes | Yes (kpatch / canonical-livepatch / KernelCare) | 20 | 7.8 |
97
- | CVE-2025-53773 (Copilot YOLO-mode RCE) | No | Yes (demonstrated) | Yes (AI tooling enables) | Suspected | GitHub Copilot users (15) | Yes (SaaS) | Yes (SaaS push) | 30 | 7.8 |
98
- | CVE-2026-30615 (Windsurf MCP local-vector RCE) | No | Partial | No | Suspected (supply-chain) | 150M+ downloads, local-vector + supply-chain prereq | Yes | Yes (IDE update) | 35 | 8.0 |
94
+ | CVE-2026-43284 (Dirty Frag ESP/IPsec) | No | Yes (chain) | Yes (AI-assisted) | Suspected | IPsec-using systems (18) | Yes | RHEL-only kpatch | 53 | 8.8 |
95
+ | CVE-2026-43500 (Dirty Frag RxRPC) | No | Yes (chain) | Yes (AI-assisted) | Suspected | RxRPC-loaded systems (12) | Yes | No | 47 | 7.6 |
96
+ | CVE-2026-46300 (Fragnesia) | No (likely candidate) | Yes (one-liner vs /usr/bin/su) | Yes (AI-assisted) | None observed | Linux >= 5.10 with esp4/esp6/rxrpc loaded (25) | Yes | Yes (kpatch / canonical-livepatch / KernelCare) | 35 | 7.8 |
97
+ | CVE-2025-53773 (Copilot YOLO-mode RCE) | No | Yes (demonstrated) | No (AI-assisted weaponization) | Suspected | GitHub Copilot users (10) | Yes (SaaS) | Yes (SaaS push) | 30 | 7.8 |
98
+ | CVE-2026-30615 (Windsurf MCP local-vector RCE) | No | Yes | No | Suspected (supply-chain) | 150M+ downloads, local-vector + supply-chain prereq (30) | Yes | Yes (IDE update) | 35 | 8.0 |
99
99
 
100
100
  Key reads: Copy Fail (RWEP 90, CVSS 7.8) and Windsurf MCP (RWEP 35, CVSS 8.0) sit at opposite ends of the exploitation-evidence axis — Copy Fail is the canonical case of CVSS under-prioritization (KEV + deterministic public PoC + AI-discovered + broad blast-radius dominate); Windsurf MCP is the canonical case of CVSS-vector blindness (the AV:L local-attack vector plus the supply-chain prerequisite throttle real exploitation rate even after CVSS was corrected from 9.8 to 8.0).
101
101
 
@@ -184,15 +184,15 @@ RWEP = min(100, max(0,
184
184
  |---|---|---|
185
185
  | CISA KEV | No | 0 |
186
186
  | PoC Public | Yes (chain component) | +20 |
187
- | AI-Assisted | No | 0 |
187
+ | AI-Assisted | Yes (discovered with AI assistance, per Sysdig) | +15 |
188
188
  | Active Exploitation | Suspected | +10 (partial) |
189
189
  | Blast Radius | IPsec-using systems, kernel 5.x | +18 |
190
190
  | Patch Available | Yes | -15 |
191
191
  | Live Patch Available | No (kpatch RHEL-only — not broadly available) | 0 |
192
192
  | Reboot Required | Yes | +5 |
193
- | **RWEP** | | **38** |
193
+ | **RWEP** | | **53** |
194
194
 
195
- **Interpretation:** No CISA KEV, suspected (not confirmed) exploitation, and no broad live-patch availability. RWEP 38 vs. CVSS-equivalent 78 — RWEP shows this is less urgent than CVSS suggests absent confirmed exploitation at scale. Critical contextual risk: any host using IPsec for compliance controls (SC-8/SC-28) cannot claim those controls as compensating controls while this CVE is unpatched — the exploit runs through the IPsec implementation.
195
+ **Interpretation:** No CISA KEV, suspected (not confirmed) exploitation, and no broad live-patch availability. RWEP 53 vs. CVSS-equivalent 88 — RWEP shows this is less urgent than CVSS suggests absent confirmed exploitation at scale. Critical contextual risk: any host using IPsec for compliance controls (SC-8/SC-28) cannot claim those controls as compensating controls while this CVE is unpatched — the exploit runs through the IPsec implementation.
196
196
 
197
197
  ---
198
198
 
@@ -202,15 +202,17 @@ RWEP = min(100, max(0,
202
202
  |---|---|---|
203
203
  | CISA KEV | No (likely candidate within days of disclosure) | 0 |
204
204
  | PoC Public | Yes (one-line root shell vs /usr/bin/su) | +20 |
205
- | AI-Assisted | No (human-discovered by V12 security team) | 0 |
205
+ | AI-Assisted | Yes (found by William Bowling of Zellic with the company's AI-agentic auditing tool) | +15 |
206
206
  | Active Exploitation | None observed | 0 |
207
207
  | Blast Radius | Linux >= 5.10 with esp4/esp6/rxrpc loaded | +25 |
208
208
  | Patch Available | Yes (testing on AlmaLinux / CloudLinux; upstream on netdev) | -15 |
209
209
  | Live Patch Available | Yes (kpatch / canonical-livepatch / KernelCare) | -10 |
210
210
  | Reboot Required | No (module-unload mitigation is non-reboot) | 0 |
211
- | **RWEP** | | **20** |
211
+ | **RWEP** | | **35** |
212
+
213
+ **Interpretation:** RWEP 35 today places Fragnesia in the standard-30-day patch band. Two factors will move this fast: (a) a CISA KEV listing adds +25 (RWEP 60 → 72-hour band), and (b) confirmed active exploitation adds another +20 (RWEP 80 → 24-hour band). Operators tracking RWEP should pre-stage the response now while the score is low so the KEV-listing event triggers an already-rehearsed playbook rather than a from-scratch decision. RWEP 35 vs. RWEP 90 for Copy Fail is the canonical "same CVSS band, different urgency" pair for 2026.
212
214
 
213
- **Interpretation:** RWEP 20 today places Fragnesia in the standard-30-day patch band. Two factors will move this fast: (a) a CISA KEV listing adds +25 (RWEP 45 → 7-day band), and (b) confirmed active exploitation adds another +20 (RWEP 65 → 72-hour band). Operators tracking RWEP should pre-stage the response now while the score is low so the KEV-listing event triggers an already-rehearsed playbook rather than a from-scratch decision. Critical operational insight: Fragnesia is the sibling bug introduced by the Dirty Frag patch, and the module-unload mitigation set (`blacklist esp4 / esp6 / rxrpc`) is identical to Dirty Frag's — operators already mitigated for CVE-2026-43284 / CVE-2026-43500 are already mitigated for Fragnesia at zero additional operational cost. RWEP 20 vs. RWEP 90 for Copy Fail is the canonical "same CVSS band, different urgency" pair for 2026.
215
+ Critical operational insight: Fragnesia is the sibling bug introduced by the Dirty Frag patch, and the module-unload mitigation set (`blacklist esp4 / esp6 / rxrpc`) is identical to Dirty Frag's — operators already mitigated for CVE-2026-43284 / CVE-2026-43500 are already mitigated for Fragnesia at zero additional operational cost.
214
216
 
215
217
  ---
216
218
 
@@ -237,10 +239,10 @@ RWEP = min(100, max(0,
237
239
  | Factor | Value | Points |
238
240
  |---|---|---|
239
241
  | CISA KEV | No | 0 |
240
- | PoC Public | Partial | +10 |
242
+ | PoC Public | Yes | +20 |
241
243
  | AI-Assisted | No | 0 |
242
244
  | Active Exploitation | Suspected (supply chain targeting) | +10 |
243
- | Blast Radius | 150M+ MCP-capable assistant downloads, local-vector + supply-chain prerequisite | +20 |
245
+ | Blast Radius | 150M+ MCP-capable assistant downloads, local-vector + supply-chain prerequisite | +30 |
244
246
  | Patch Available | Yes | -15 |
245
247
  | Live Patch Available | Yes (IDE update) | -10 |
246
248
  | Reboot Required | No | 0 |
@@ -20,7 +20,7 @@ data_deps:
20
20
  atlas_refs: []
21
21
  attack_refs: []
22
22
  framework_gaps: []
23
- last_threat_review: "2026-09-18"
23
+ last_threat_review: "2026-09-28"
24
24
  ---
25
25
 
26
26
  # Framework Gap Analysis
@@ -96,11 +96,11 @@ This skill consumes the matrix produced upstream by the exploit-scoring skill. T
96
96
 
97
97
  | CVE | CVSS | RWEP | KEV | Public PoC | AI-accelerated | Live-patchable | Active exploitation |
98
98
  |---|---|---|---|---|---|---|---|
99
- | CVE-2026-31431 (Copy Fail) | High | Critical | Yes | Yes (732 bytes, deterministic) | Yes (AI-discovered) | Yes (kpatch/livepatch) | Confirmed |
100
- | CVE-2026-43284 (Dirty Frag) | High | High | Pending | Partial | No | Limited (subsystem-dependent) | Suspected |
101
- | CVE-2026-46300 (Fragnesia) | 7.8 | 20 (today) / 55+ on KEV | No (candidate) | Yes (one-liner vs /usr/bin/su) | No | Yes (kpatch / canonical-livepatch / KernelCare) | None observed |
99
+ | CVE-2026-31431 (Copy Fail) | 7.8 | 90 | Yes (2026-05-01) | Yes (732 bytes, deterministic) | Yes (AI-discovered) | Yes (kpatch/livepatch) | Confirmed |
100
+ | CVE-2026-43284 (Dirty Frag) | 8.8 | 53 | No | Yes (chain component) | Yes (AI-assisted discovery) | Limited (kpatch RHEL-only) | Suspected |
101
+ | CVE-2026-46300 (Fragnesia) | 7.8 | 35 (60 if KEV-listed) | No (candidate) | Yes (one-liner vs /usr/bin/su) | Yes (AI-assisted discovery) | Yes (kpatch / canonical-livepatch / KernelCare) | None observed |
102
102
  | CVE-2025-53773 (Copilot YOLO-mode RCE) | 7.8 | 30 | No | Yes (demonstrated) | Yes (AI tooling enables) | Yes (SaaS push / IDE update) | Suspected |
103
- | CVE-2026-30615 (Windsurf MCP local-vector RCE) | 8.0 | 35 | No | Partial | No | Yes (IDE update) | Suspected |
103
+ | CVE-2026-30615 (Windsurf MCP local-vector RCE) | 8.0 | 35 | No | Yes | No | Yes (IDE update) | Suspected |
104
104
 
105
105
  When a gap analysis cites a CVE not in this matrix, the analyst must populate the row from `data/cve-catalog.json` before producing the declaration. A declaration without an evidence row is incomplete.
106
106
 
@@ -76,7 +76,7 @@ forward_watch:
76
76
  - NIS2 implementing-act revision enumerating federated-identity control-plane indicators
77
77
  - Cross-tenant access settings evolution at Entra ID — partner-tenant attestation cadence and revocation latency
78
78
  - PSD3 / PSR final text on agent-initiated payments and the IdP-mediated agent-attestation surface
79
- last_threat_review: "2026-05-15"
79
+ last_threat_review: "2026-09-28"
80
80
  ---
81
81
 
82
82
  # Identity-Provider Incident Response (mid-2026)
@@ -149,9 +149,9 @@ Agentic AI is the emerging structural problem on top. AI agents operating on beh
149
149
  | Help-desk social engineering (Scattered Spider) | n/a (social engineering) | high (MGM USD 100M, Caesars USD 15M as public reference) | n/a | Demonstrated at scale | n/a (AI-augmented reconnaissance) | Confirmed ongoing 2022-2026 | Mitigation only — out-of-band identity verification, video-callback to previously-registered number, knowledge-based + government-ID verification, never phone-only | n/a | Help-desk-system audit; vendor-fragmented |
150
150
  | SAML token forgery (Golden SAML / NobleSAML class) | n/a (design class) | high (state-actor priority) | n/a | Public research + active campaigns | n/a | Suspected ongoing against hybrid-identity tenants | Mitigation — signing-key rotation + signing-state attestation + audit-log alerting on signing-cert modification | n/a | Tenant audit log if signing-state modification alerts configured |
151
151
  | ScreenConnect identity-class CVE | 7.5 (CVSS) | high (CISA KEV) | Yes (2024-02) | Yes — public PoC | No | Confirmed exploitation against IdP-adjacent MSP surface | Yes — vendor patch | Patch-class | EDR + tenant audit if integration logged |
152
- | CVE-2024-1709 ScreenConnect authentication bypass | 10.0 | 95 | Yes | Yes | No | Confirmed exploitation 2024 | Yes | Limited — appliance reboot window | Vendor-side patching + IdP-side conditional-access tightening |
153
- | CVE-2023-3519 Citrix NetScaler RCE | 9.8 | 92 | Yes | Yes | No | Confirmed exploitation 2023-2024 (financial-sector and federal targets) | Yes | Limited — appliance reboot | Network telemetry + IdP-tenant access-pattern alerting |
154
- | CVE-2026-30615 Windsurf MCP — adjacent identity surface | 8.6 | 88 | Forward-watched | Yes | Yes | Suspected | Mitigation + vendor patch | n/a | MCP-tool-trust telemetry |
152
+ | CVE-2024-1709 ScreenConnect authentication bypass | 10.0 | 75 | Yes (2024-02-22) | Yes | No | Confirmed exploitation 2024 | Yes | No | Vendor-side patching + IdP-side conditional-access tightening |
153
+ | CVE-2023-3519 Citrix NetScaler RCE | 9.8 | 80 | Yes (2023-07-19) | Yes | No | Confirmed exploitation 2023-2024 (financial-sector and federal targets) | Yes | No | Network telemetry + IdP-tenant access-pattern alerting |
154
+ | CVE-2026-30615 Windsurf MCP — adjacent identity surface | 8.0 | 35 | No | Yes | No | Suspected | Mitigation + vendor patch | Yes (IDE update) | MCP-tool-trust telemetry |
155
155
 
156
156
  **Honest gap statement.** IdP-specific CVEs (Okta Auth0 Workforce CVEs, Entra ID Graph CVEs, Auth0 platform CVEs, Ping platform CVEs, OneLogin platform CVEs) are not exhaustively inventoried in `data/cve-catalog.json`. Authoritative sources: vendor advisories (Okta Security, Microsoft MSRC + Security Update Guide, Auth0 Security Advisories, Ping Identity Security Notices, One Identity Customer Advisory), CISA KEV for cross-sector exposure, CISA AA24 series for federal-targeting advisories, and sector intel feeds. Forward-watched.
157
157