@blamejs/exceptd-skills 0.16.9 → 0.16.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.10 — 2026-06-02
4
+
5
+ RWEP scoring no longer emits a spurious validation warning when a CVE carries the `theoretical` active-exploitation status — a value the catalog vocabulary and the scorer already accept and score. The guided curation questionnaire now prompts for `ai_assisted_weaponization`, a required field it previously skipped, so a curated entry cannot silently omit it. The `prefetch` verb no longer double-counts a global flag, and `lint --strict` is now documented in its own `--help`.
6
+
3
7
  ## 0.16.9 — 2026-06-01
4
8
 
5
9
  The catalog now covers a set of real, vendor-patched protocol-layer flaws it previously did not name, so scans, triage, and reports surface them with RWEP scoring and behavioral indicators:
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "schema_version": "1.1.0",
3
- "generated_at": "2026-06-02T05:46:26.678Z",
3
+ "generated_at": "2026-06-02T13:27:49.467Z",
4
4
  "generator": "scripts/build-indexes.js",
5
5
  "source_count": 54,
6
6
  "source_hashes": {
7
- "manifest.json": "3dab3ebc6c86b2318f956da3e343ad7b470f05dd30e2afbee5a8e3b3a845a926",
7
+ "manifest.json": "b2f7ad163ce22cedc2a990633761b25d3f436012c20dc162739959f92220161f",
8
8
  "data/atlas-ttps.json": "f66b456cf82a3c20575d8479de41f7b11b7ee5693eb1fcf64a67e162ae1b88a2",
9
9
  "data/attack-techniques.json": "c39f28e3402ef13ad9b7076819f63fda67a22f97e3e375cfe01c4a4e0beff7c9",
10
10
  "data/cve-catalog.json": "8264da4534d39c9493cfcd18acf7e38ed47ce2a81be15afd5a3f4baf1d504929",
@@ -177,7 +177,7 @@ function severityWord(score) {
177
177
  // (b) compute `residual_warnings` after an apply.
178
178
  const REQUIRED_SCHEMA_FIELDS = [
179
179
  "name", "type", "cvss_score", "cvss_vector", "cisa_kev",
180
- "poc_available", "ai_discovered", "active_exploitation",
180
+ "poc_available", "ai_discovered", "ai_assisted_weaponization", "active_exploitation",
181
181
  "affected", "affected_versions", "vector",
182
182
  "patch_available", "patch_required_reboot", "live_patch_available",
183
183
  "framework_control_gaps", "atlas_refs", "attack_refs",
@@ -285,13 +285,11 @@ function buildQuestionnaire(cveId, draft) {
285
285
  // object via loadJsonRaw's ENOENT handling, so the ATT&CK candidate
286
286
  // questionnaire branch always returned zero proposals.
287
287
  const attack = loadJson("data/attack-techniques.json");
288
- const cwe = loadJson("data/cwe-catalog.json");
289
288
  const frameworkGaps = loadJson("data/framework-control-gaps.json");
290
289
 
291
290
  const atlasCandidates = pickCandidates(draftDigest, atlas, "ttp_id", "description");
292
291
  const attackCandidates = pickCandidates(draftDigest, attack, "ttp_id", "description");
293
292
  // CWE candidates surface as part of the vector question — not its own field.
294
- void cwe;
295
293
  const frameworkCandidates = pickCandidates(draftDigest, frameworkGaps, "control_id", "description");
296
294
 
297
295
  // Build the editorial-questions list. Each entry names the catalog field,
@@ -479,7 +477,7 @@ function buildQuestionnaire(cveId, draft) {
479
477
  * poc_description: "...",
480
478
  * ai_discovered: false,
481
479
  * ai_assisted_weaponization: false,
482
- * active_exploitation: "confirmed" | "suspected" | "none" | "unknown",
480
+ * active_exploitation: "confirmed" | "suspected" | "theoretical" | "none" | "unknown",
483
481
  * vector: "...",
484
482
  * complexity: "...",
485
483
  * patch_available: true,
@@ -115,7 +115,7 @@ const VERB_FLAG_ALLOWLIST = Object.freeze({
115
115
  'apply', 'dry-run', 'from-cache', 'from-fixture', 'network', 'source',
116
116
  'advisory', 'check-advisories', 'force-stale', 'force-stale-acked', 'air-gap', 'swarm',
117
117
  ],
118
- prefetch: ['source', 'cache-dir', 'max-age', 'force', 'no-network', 'quiet'],
118
+ prefetch: ['source', 'cache-dir', 'max-age', 'force', 'no-network'],
119
119
  });
120
120
 
121
121
  /**
@@ -150,6 +150,7 @@ function printHelp() {
150
150
  'Usage: node lib/lint-skills.js [--skill <name>] [--quiet]\n' +
151
151
  '\n' +
152
152
  ' --skill <name> Lint only the named skill from manifest.json.\n' +
153
+ ' --strict Promote warnings to release-blocking failures.\n' +
153
154
  ' --quiet Suppress per-skill PASS output; show failures only.\n' +
154
155
  ' --help Show this message.\n',
155
156
  );
@@ -1104,7 +1104,7 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}, runOp
1104
1104
  // Aliasing: playbooks ship rwep_factor values `public_poc` and
1105
1105
  // `ai_weaponization` for what F5 calls `poc_available` and `ai_factor`.
1106
1106
  // Both spellings resolve here.
1107
- const _activeExploitationLadder = { confirmed: 1.0, suspected: 0.5, unknown: 0.25, none: 0 };
1107
+ const _activeExploitationLadder = { confirmed: 1.0, suspected: 0.5, unknown: 0.25, theoretical: 0, none: 0 };
1108
1108
  const _factorScale = (factorName, cve, blastScore) => {
1109
1109
  if (!cve) return 0;
1110
1110
  switch (factorName) {
@@ -2595,7 +2595,7 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2595
2595
  // CSAF 2.0 consumers, while preserving the operator's exact label in the
2596
2596
  // free-form `text` field. (CLEAR≡WHITE; AMBER+STRICT carries AMBER's
2597
2597
  // disclosure scope plus a stricter handling note.)
2598
- const CSAF_TLP_LABEL = { CLEAR: 'WHITE', 'AMBER+STRICT': 'AMBER', GREEN: 'GREEN', AMBER: 'AMBER', RED: 'RED' };
2598
+ const CSAF_TLP_LABEL = { CLEAR: 'WHITE', GREEN: 'GREEN', AMBER: 'AMBER', 'AMBER+STRICT': 'AMBER', RED: 'RED' };
2599
2599
  const csafDistribution = (runOpts.tlp && allowedTlp.has(runOpts.tlp))
2600
2600
  ? { tlp: { label: CSAF_TLP_LABEL[runOpts.tlp] }, text: `TLP:${runOpts.tlp}` }
2601
2601
  : null;
package/lib/scoring.js CHANGED
@@ -108,7 +108,7 @@ function score(cveId, catalog) {
108
108
  * - cisa_kev, poc_available, ai_assisted_weapon, ai_discovered,
109
109
  * patch_available, live_patch_available, reboot_required: boolean
110
110
  * (or null, treated as false with a missing-field warning).
111
- * - active_exploitation: 'none' | 'unknown' | 'suspected' | 'confirmed'.
111
+ * - active_exploitation: 'none' | 'unknown' | 'suspected' | 'theoretical' | 'confirmed'.
112
112
  * - blast_radius: integer in [0, 30] (clamped at the weight ceiling but
113
113
  * flagged when out-of-range — out-of-range usually means a unit error).
114
114
  */
@@ -126,7 +126,7 @@ function validateFactors(factors) {
126
126
  warnings.push(`${f}: expected boolean, got ${typeof factors[f]} (${JSON.stringify(factors[f])})`);
127
127
  }
128
128
  }
129
- const aeAllowed = ['none', 'unknown', 'suspected', 'confirmed'];
129
+ const aeAllowed = ['none', 'unknown', 'suspected', 'theoretical', 'confirmed'];
130
130
  if (factors.active_exploitation === undefined || factors.active_exploitation === null) {
131
131
  warnings.push("active_exploitation: missing (treated as 'none')");
132
132
  } else if (!aeAllowed.includes(factors.active_exploitation)) {
@@ -261,7 +261,7 @@ function deriveRwepFromFactors(factors) {
261
261
  if (!factors || typeof factors !== 'object') return 0;
262
262
  const values = Object.values(factors);
263
263
  if (values.length === 0) return 0;
264
- const aeAllowed = new Set(['none', 'unknown', 'suspected', 'confirmed']);
264
+ const aeAllowed = new Set(['none', 'unknown', 'suspected', 'theoretical', 'confirmed']);
265
265
  const hasBooleanOrLadder = values.some(
266
266
  (v) => typeof v === 'boolean' || (typeof v === 'string' && aeAllowed.has(v)),
267
267
  );
@@ -554,7 +554,6 @@ function checkCrossRefs(playbook, ctx, playbookIds) {
554
554
  * `informational: false`.
555
555
  */
556
556
  function checkMutexReciprocity(playbooks) {
557
- const findings = [];
558
557
  const mutexMap = new Map();
559
558
  for (const pb of playbooks) {
560
559
  if (!pb.data || !pb.data._meta || !pb.data._meta.id) continue;
@@ -574,7 +573,6 @@ function checkMutexReciprocity(playbooks) {
574
573
  }
575
574
  }
576
575
  }
577
- findings.push(byPlaybook);
578
576
  return byPlaybook;
579
577
  }
580
578
 
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceptd-security",
3
- "version": "0.16.9",
3
+ "version": "0.16.10",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation",
5
5
  "homepage": "https://exceptd.com",
6
6
  "license": "Apache-2.0",
@@ -53,7 +53,7 @@
53
53
  ],
54
54
  "last_threat_review": "2026-05-15",
55
55
  "signature": "0H+JfyUVmo/pVFEi5rLENATHjlukPVUqnOWmNPEH77wm8svKGK0aNJ46k6QU5GdHb8c9X9pVJKiuhON6AxDjDw==",
56
- "signed_at": "2026-06-02T05:38:21.342Z",
56
+ "signed_at": "2026-06-02T13:19:53.107Z",
57
57
  "cwe_refs": [
58
58
  "CWE-125",
59
59
  "CWE-362",
@@ -123,7 +123,7 @@
123
123
  ],
124
124
  "last_threat_review": "2026-05-17",
125
125
  "signature": "PHwHEsoy7ctBYOtlAfAdCDVfsq2Bpk9+qESSF+5dVkDcez2zp2v9Ihsv2vqMEs3QxMndyQ+t7NVezyt5VamSCg==",
126
- "signed_at": "2026-06-02T05:38:21.345Z",
126
+ "signed_at": "2026-06-02T13:19:53.109Z",
127
127
  "cwe_refs": [
128
128
  "CWE-1039",
129
129
  "CWE-1426",
@@ -196,7 +196,7 @@
196
196
  ],
197
197
  "last_threat_review": "2026-05-17",
198
198
  "signature": "dD4p7lcRtMyfITOncqLkpOeMy6x6gM0V7UlWHgLEdcxqODb1s75ar1cBtTqDWPbMv6ZAzVo2HJLDK1hVjjU2AQ==",
199
- "signed_at": "2026-06-02T05:38:21.345Z",
199
+ "signed_at": "2026-06-02T13:19:53.109Z",
200
200
  "cwe_refs": [
201
201
  "CWE-22",
202
202
  "CWE-345",
@@ -248,7 +248,7 @@
248
248
  "framework_gaps": [],
249
249
  "last_threat_review": "2026-05-22",
250
250
  "signature": "wsw8Mlr/gyw6S7Iaao9BVHdU5LFPWl8WVymW17Lkq9J1Mui0+fCrTg6UbrsaeE3s7EW3TVgzBuK+8EFd1+H5AA==",
251
- "signed_at": "2026-06-02T05:38:21.346Z"
251
+ "signed_at": "2026-06-02T13:19:53.109Z"
252
252
  },
253
253
  {
254
254
  "name": "compliance-theater",
@@ -279,7 +279,7 @@
279
279
  ],
280
280
  "last_threat_review": "2026-05-22",
281
281
  "signature": "uVTc1QRKOKcIVDajBz+q2egjiEAyOQaDNsvVI2ghj5FD0VvquoUBBE5Naca2FkaZa790EHWCsVZ4hhdaSQs2DQ==",
282
- "signed_at": "2026-06-02T05:38:21.347Z"
282
+ "signed_at": "2026-06-02T13:19:53.110Z"
283
283
  },
284
284
  {
285
285
  "name": "exploit-scoring",
@@ -308,7 +308,7 @@
308
308
  ],
309
309
  "last_threat_review": "2026-05-18",
310
310
  "signature": "QuNpwnZ6HkCEAXTPC/jLbXSmMIc1JnBczqZAAIZmZj8OcEMVnw9mJYAnU3CxaEI7rvbcMkN2uS5E8yUCm/NiAg==",
311
- "signed_at": "2026-06-02T05:38:21.347Z"
311
+ "signed_at": "2026-06-02T13:19:53.110Z"
312
312
  },
313
313
  {
314
314
  "name": "rag-pipeline-security",
@@ -345,7 +345,7 @@
345
345
  ],
346
346
  "last_threat_review": "2026-05-22",
347
347
  "signature": "5rw2i39SxY2WphBbDLEP28wufnbPPE9+PWt54hmaGdwHXr9RLiVt5liL/5xp14sehlVgFsfpR/bg9vy//xV0DA==",
348
- "signed_at": "2026-06-02T05:38:21.347Z",
348
+ "signed_at": "2026-06-02T13:19:53.111Z",
349
349
  "cwe_refs": [
350
350
  "CWE-1395",
351
351
  "CWE-1426"
@@ -405,7 +405,7 @@
405
405
  ],
406
406
  "last_threat_review": "2026-05-17",
407
407
  "signature": "Vqu49nzntFWjn9A/QeJzm7q/2xk/cZJ6HFQKtiNi1zgcxzXKm+MlFdkaLgYHWj5/9HJohxyIDyBJQTvcJ20eDQ==",
408
- "signed_at": "2026-06-02T05:38:21.348Z",
408
+ "signed_at": "2026-06-02T13:19:53.111Z",
409
409
  "d3fend_refs": [
410
410
  "D3-CA",
411
411
  "D3-CSPP",
@@ -440,7 +440,7 @@
440
440
  "framework_gaps": [],
441
441
  "last_threat_review": "2026-05-22",
442
442
  "signature": "W87VdyVdAxAdcRI6P/8StaV+MS8ZSPKM9HOCK9n/bBO6BM3ZSE3uImVoyJVpAXQlUpUGN+A3lCJZXv64LuxwDg==",
443
- "signed_at": "2026-06-02T05:38:21.348Z",
443
+ "signed_at": "2026-06-02T13:19:53.112Z",
444
444
  "cwe_refs": [
445
445
  "CWE-1188"
446
446
  ],
@@ -474,7 +474,7 @@
474
474
  "framework_gaps": [],
475
475
  "last_threat_review": "2026-05-18",
476
476
  "signature": "wdVX+edeNekpaIldqkhvtraV6DquLvIsKAjuZVwPQYn3l1vS99HXuFxmNsD7UeMlO3qgC6Dysfsto9EnuH0RBg==",
477
- "signed_at": "2026-06-02T05:38:21.348Z",
477
+ "signed_at": "2026-06-02T13:19:53.112Z",
478
478
  "forward_watch": [
479
479
  "New AI attack classes as ATLAS v6 publishes",
480
480
  "Post-quantum adversary capability timeline",
@@ -513,7 +513,7 @@
513
513
  "framework_gaps": [],
514
514
  "last_threat_review": "2026-05-01",
515
515
  "signature": "b5miTiY0cnxETd2btxorfZBdJKt/fLnQx20sGYUb9zEqGqtm0LMLpghkW68j4/9k48KNyuGMtNWiKTSnodUGBw==",
516
- "signed_at": "2026-06-02T05:38:21.349Z"
516
+ "signed_at": "2026-06-02T13:19:53.112Z"
517
517
  },
518
518
  {
519
519
  "name": "zeroday-gap-learn",
@@ -540,7 +540,7 @@
540
540
  "framework_gaps": [],
541
541
  "last_threat_review": "2026-05-18",
542
542
  "signature": "xbkip0AQtWQKAu+O6r/gYECNjezS6O9k9xkkJsYbMlr+j8CdqH3p5/0l+GZmDidImRC/DL07GCnKrk9HRR/yDQ==",
543
- "signed_at": "2026-06-02T05:38:21.349Z",
543
+ "signed_at": "2026-06-02T13:19:53.113Z",
544
544
  "forward_watch": [
545
545
  "New CISA KEV entries",
546
546
  "New ATLAS TTP additions in each ATLAS release",
@@ -604,7 +604,7 @@
604
604
  ],
605
605
  "last_threat_review": "2026-05-22",
606
606
  "signature": "li2NnC1oeVIr22ComP5QbcQoh5xpWITuaKpza1s2SsUkH6kGnnt4wFfFAzaC1ORmH9x2cr8hN8kaNANG/eIMBQ==",
607
- "signed_at": "2026-06-02T05:38:21.350Z",
607
+ "signed_at": "2026-06-02T13:19:53.113Z",
608
608
  "cwe_refs": [
609
609
  "CWE-327"
610
610
  ],
@@ -652,7 +652,7 @@
652
652
  ],
653
653
  "last_threat_review": "2026-05-22",
654
654
  "signature": "sZHlJ7ueHPdtzVbR+yXQ5+wKgNyjWsa1LKVg9aWTmg/Onl71DvEILMyJiLpPQjseT56Mnr1DMYJE8xOGlffBAw==",
655
- "signed_at": "2026-06-02T05:38:21.350Z"
655
+ "signed_at": "2026-06-02T13:19:53.113Z"
656
656
  },
657
657
  {
658
658
  "name": "security-maturity-tiers",
@@ -689,7 +689,7 @@
689
689
  ],
690
690
  "last_threat_review": "2026-05-01",
691
691
  "signature": "3AwFnEJu6DukPPNep/3SnuPWEuV060fJEQIwThFm7ujmdbFk0/Ii0XwGv1dkvbbK7ymMdOQpp35l4aLONAucDA==",
692
- "signed_at": "2026-06-02T05:38:21.350Z",
692
+ "signed_at": "2026-06-02T13:19:53.114Z",
693
693
  "cwe_refs": [
694
694
  "CWE-1188"
695
695
  ]
@@ -724,7 +724,7 @@
724
724
  "framework_gaps": [],
725
725
  "last_threat_review": "2026-05-11",
726
726
  "signature": "iJWevUBurLvt2v8X+Ch2eHmZkPWpKeAtIpxTIP4MwbUHyco3igDeBywJCyaR2vURYRx8LkzzIMM8DxQM4LAXBQ==",
727
- "signed_at": "2026-06-02T05:38:21.351Z"
727
+ "signed_at": "2026-06-02T13:19:53.114Z"
728
728
  },
729
729
  {
730
730
  "name": "attack-surface-pentest",
@@ -796,7 +796,7 @@
796
796
  "Pwn2Own Berlin 2026 (disclosed 2026-05-14, embargo ends 2026-08-12) — Microsoft Edge 4-bug sandbox escape by Orange Tsai (DEVCORE); forward-watch only (browser sandbox, out of current playbook scope); track Microsoft Edge security advisory and KEV add"
797
797
  ],
798
798
  "signature": "DDMzI+4En4aIkwBUCGW6nj1eEkCyLqHGn2LJ2rnwWfYatjPI1U5HrTZNAN/n9JqWtAzk8F3rmsKehaaz5iNWDA==",
799
- "signed_at": "2026-06-02T05:38:21.351Z"
799
+ "signed_at": "2026-06-02T13:19:53.114Z"
800
800
  },
801
801
  {
802
802
  "name": "fuzz-testing-strategy",
@@ -856,7 +856,7 @@
856
856
  "OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
857
857
  ],
858
858
  "signature": "dJB0iAstIUbyny+udl3OIkaLScEmqS97LNP73yQ8mxt+0bcqxZjpfXaWLzLuIQblGYvUvz75/H6rO2EJuGd4AQ==",
859
- "signed_at": "2026-06-02T05:38:21.351Z"
859
+ "signed_at": "2026-06-02T13:19:53.115Z"
860
860
  },
861
861
  {
862
862
  "name": "dlp-gap-analysis",
@@ -931,7 +931,7 @@
931
931
  "Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
932
932
  ],
933
933
  "signature": "KEAoMji3VcPX/ZXXqVe6OStxSkTssfY9fIRPyPcDYqh50GzOFQ6koNOTBVAiWOvjDjQ38g12xun5srbqgmvRAw==",
934
- "signed_at": "2026-06-02T05:38:21.352Z"
934
+ "signed_at": "2026-06-02T13:19:53.115Z"
935
935
  },
936
936
  {
937
937
  "name": "supply-chain-integrity",
@@ -1010,7 +1010,7 @@
1010
1010
  "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-attestation impact"
1011
1011
  ],
1012
1012
  "signature": "zuW8T0EMbVV83GsUP/W20Use2gBTicBW021T0sY7qsRY/U5qsPWkXYIWp3SdiKLTIKqTEd/0T7LQebjIs2QKCA==",
1013
- "signed_at": "2026-06-02T05:38:21.352Z"
1013
+ "signed_at": "2026-06-02T13:19:53.115Z"
1014
1014
  },
1015
1015
  {
1016
1016
  "name": "defensive-countermeasure-mapping",
@@ -1067,7 +1067,7 @@
1067
1067
  ],
1068
1068
  "last_threat_review": "2026-05-11",
1069
1069
  "signature": "Qe0Hg9BrX3Zm5pj0n2z/oiHbAXWdA2Dq461zc4izkkUjEX2CZ02rODjCI2ELbrVOU3GC7edxqAxA+5U/ObnHDQ==",
1070
- "signed_at": "2026-06-02T05:38:21.352Z"
1070
+ "signed_at": "2026-06-02T13:19:53.116Z"
1071
1071
  },
1072
1072
  {
1073
1073
  "name": "identity-assurance",
@@ -1134,7 +1134,7 @@
1134
1134
  "d3fend_refs": [],
1135
1135
  "last_threat_review": "2026-05-11",
1136
1136
  "signature": "UV3458QXSkEpenzrOmdlTTfPHUD4hNyKMDHoeZDq/kiFb4mAG0ghQGTTgI9Ru8cJbSmYM1++m9N5TFIJ6JJPBg==",
1137
- "signed_at": "2026-06-02T05:38:21.353Z"
1137
+ "signed_at": "2026-06-02T13:19:53.116Z"
1138
1138
  },
1139
1139
  {
1140
1140
  "name": "ot-ics-security",
@@ -1190,7 +1190,7 @@
1190
1190
  "d3fend_refs": [],
1191
1191
  "last_threat_review": "2026-05-11",
1192
1192
  "signature": "kIVzsPsJ72PzzWQwTuvjoHHoVEDCday5I52M9ohjB3/Ak+zlA8oyWLO/BKb/XuYY4fOApjfxTErSWv5uHQ2zDw==",
1193
- "signed_at": "2026-06-02T05:38:21.353Z"
1193
+ "signed_at": "2026-06-02T13:19:53.116Z"
1194
1194
  },
1195
1195
  {
1196
1196
  "name": "coordinated-vuln-disclosure",
@@ -1242,7 +1242,7 @@
1242
1242
  "NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
1243
1243
  ],
1244
1244
  "signature": "bWr27Q1uN9xCe1ib4QulszBa7YIDNkGqo72k5nm2cK98LyPblicD+sO9MnGckAyB22BTN/cIB+FwFMcI5IxvBw==",
1245
- "signed_at": "2026-06-02T05:38:21.353Z"
1245
+ "signed_at": "2026-06-02T13:19:53.117Z"
1246
1246
  },
1247
1247
  {
1248
1248
  "name": "threat-modeling-methodology",
@@ -1292,7 +1292,7 @@
1292
1292
  "PASTA v2 updates incorporating AI/ML application threats"
1293
1293
  ],
1294
1294
  "signature": "Q854yzLqXdOazc6EyQbZzgAlivuq2vGFDVUCrxSldSvx/HX/ZM/uzmJyP7aBG7ZsMHxj6Lmj/H82YQoo1e+NCQ==",
1295
- "signed_at": "2026-06-02T05:38:21.354Z"
1295
+ "signed_at": "2026-06-02T13:19:53.117Z"
1296
1296
  },
1297
1297
  {
1298
1298
  "name": "webapp-security",
@@ -1366,7 +1366,7 @@
1366
1366
  "d3fend_refs": [],
1367
1367
  "last_threat_review": "2026-05-11",
1368
1368
  "signature": "4ccahkJpGJZtwD7EBpnGcN0sEGPMEw8eqV+tvePVS04YAkLgYVWtlkasI/8n0be9xB+77x+Sjj3kIi2j2Lf9CA==",
1369
- "signed_at": "2026-06-02T05:38:21.354Z",
1369
+ "signed_at": "2026-06-02T13:19:53.117Z",
1370
1370
  "forward_watch": [
1371
1371
  "NGINX Rift CVE-2026-42945 (disclosed 2026-05-13, source depthfirst) — KEV-watch predicted CISA KEV listing by 2026-05-29; AI-assisted discovery angle; track for active-exploitation confirmation and patch advisory affecting front-door web app deployments"
1372
1372
  ]
@@ -1419,7 +1419,7 @@
1419
1419
  "d3fend_refs": [],
1420
1420
  "last_threat_review": "2026-05-15",
1421
1421
  "signature": "SBB7c3wNYfIdkyOp4g4nW0WP7xS+YokMzg32aaeJdbf14LTGQRzQUvSqb2TCj2HFUSHESOyKT1JpkAfyHLSQBQ==",
1422
- "signed_at": "2026-06-02T05:38:21.355Z"
1422
+ "signed_at": "2026-06-02T13:19:53.118Z"
1423
1423
  },
1424
1424
  {
1425
1425
  "name": "sector-healthcare",
@@ -1479,7 +1479,7 @@
1479
1479
  "d3fend_refs": [],
1480
1480
  "last_threat_review": "2026-05-11",
1481
1481
  "signature": "U04GNLyRas1VmfEsB8khH4iqFZPwx96sPY0Kw9iVsSPU+KTeEFqwgtWK1X1pzgb+T16Pc7HSrCaXDOpTFvQEDw==",
1482
- "signed_at": "2026-06-02T05:38:21.355Z"
1482
+ "signed_at": "2026-06-02T13:19:53.118Z"
1483
1483
  },
1484
1484
  {
1485
1485
  "name": "sector-financial",
@@ -1560,7 +1560,7 @@
1560
1560
  "TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
1561
1561
  ],
1562
1562
  "signature": "xbylLqNPBuEsFE/MNVeGy/01K6yiJXMxQbzC1F4RWU5aseDGbNy5HrAv2JWI2+Aft05ozreNPjccvu66yJ5EBw==",
1563
- "signed_at": "2026-06-02T05:38:21.355Z"
1563
+ "signed_at": "2026-06-02T13:19:53.119Z"
1564
1564
  },
1565
1565
  {
1566
1566
  "name": "sector-federal-government",
@@ -1629,7 +1629,7 @@
1629
1629
  "Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
1630
1630
  ],
1631
1631
  "signature": "C9c3JuBhUbwcb7uZpDdy+PNT8sYmYIxzD4uRHu421ePW1aSFJ8fkMvuTzSO8vD/F/jOOg5opM4kov/xSAn+qCg==",
1632
- "signed_at": "2026-06-02T05:38:21.356Z"
1632
+ "signed_at": "2026-06-02T13:19:53.119Z"
1633
1633
  },
1634
1634
  {
1635
1635
  "name": "sector-energy",
@@ -1694,7 +1694,7 @@
1694
1694
  "ICS-CERT advisory feed (https://www.cisa.gov/news-events/cybersecurity-advisories/ics-advisories) for vendor CVEs in Siemens, Rockwell, Schneider Electric, ABB, GE Vernova, Hitachi Energy, AVEVA / OSIsoft PI"
1695
1695
  ],
1696
1696
  "signature": "oz8Q5WVaY8au4IjbaZahx/DSaC00Q44ylSL3mDkTerCEpW/EyPUeiLeGxSrWxBCwVFEKSSJvnhJjhvX5lDPcCg==",
1697
- "signed_at": "2026-06-02T05:38:21.356Z"
1697
+ "signed_at": "2026-06-02T13:19:53.120Z"
1698
1698
  },
1699
1699
  {
1700
1700
  "name": "sector-telecom",
@@ -1780,7 +1780,7 @@
1780
1780
  "O-RAN SFG / WG11 security specifications"
1781
1781
  ],
1782
1782
  "signature": "NAtyzfLPXlUuB78Snb9nWmbZalC1CNlIYN9rYhdEmtB/xQGC6vVnThgrEAHlm7v/jMCFuknvEpUHKdscUnUADw==",
1783
- "signed_at": "2026-06-02T05:38:21.357Z"
1783
+ "signed_at": "2026-06-02T13:19:53.120Z"
1784
1784
  },
1785
1785
  {
1786
1786
  "name": "api-security",
@@ -1849,7 +1849,7 @@
1849
1849
  "d3fend_refs": [],
1850
1850
  "last_threat_review": "2026-05-18",
1851
1851
  "signature": "1UTjZNC5Lyrgw93LAizdXVeSmv3jS8YQNT1db5OKsldub50+o1FXmAH4+3MxZozaOGDCX3yXbdDJSJaaSmfuAA==",
1852
- "signed_at": "2026-06-02T05:38:21.357Z",
1852
+ "signed_at": "2026-06-02T13:19:53.120Z",
1853
1853
  "forward_watch": [
1854
1854
  "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",
1855
1855
  "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",
@@ -1935,7 +1935,7 @@
1935
1935
  "CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
1936
1936
  ],
1937
1937
  "signature": "EdsY4xe7YA8X8m+KZUbq49JwoCXgRKEz2eg3m86O37rvBmpm8ppvl9hrsekygvpBh2VmCHL2dEYiOD8OM2n7CA==",
1938
- "signed_at": "2026-06-02T05:38:21.357Z"
1938
+ "signed_at": "2026-06-02T13:19:53.121Z"
1939
1939
  },
1940
1940
  {
1941
1941
  "name": "container-runtime-security",
@@ -1997,7 +1997,7 @@
1997
1997
  "d3fend_refs": [],
1998
1998
  "last_threat_review": "2026-05-15",
1999
1999
  "signature": "fnLKPLkjjRCJ/F9wdmZ1w1lXmqEJvTYkv6Uu+9OTd5vZTWKz3QMuxKOsas+ctCdOvTaeloqPUUprXx+ZZdDpCg==",
2000
- "signed_at": "2026-06-02T05:38:21.358Z",
2000
+ "signed_at": "2026-06-02T13:19:53.121Z",
2001
2001
  "forward_watch": [
2002
2002
  "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"
2003
2003
  ]
@@ -2071,7 +2071,7 @@
2071
2071
  "MITRE ATLAS v5.6.0 (released May 2026) shipped the AML.T0010 sub-technique expansion this forecast tracked plus new techniques (\"Publish Poisoned AI Agent Tool\", \"Escape to Host\"); inventory now 16 tactics, 84 techniques, 56 sub-techniques. Forward watch: subsequent ATLAS minor and major releases — track next-cadence updates to agentic-AI TTPs and MLOps-pipeline-specific techniques"
2072
2072
  ],
2073
2073
  "signature": "t3dkdpTX04zvjitEeOJThpgjurLd1UO9GOut4LXSZgY3ULhfknI4zT7G5+m2RSZZTo7yyeZrwpg+7vEg9K6mAw==",
2074
- "signed_at": "2026-06-02T05:38:21.358Z"
2074
+ "signed_at": "2026-06-02T13:19:53.122Z"
2075
2075
  },
2076
2076
  {
2077
2077
  "name": "incident-response-playbook",
@@ -2133,7 +2133,7 @@
2133
2133
  "NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
2134
2134
  ],
2135
2135
  "signature": "+1kmtA6rAvIyDjjy+cJHK6BcfylyVsa5cUjRFijlFR9GsQfB93JnmkEJOqML50pdlcxtJI3yUodHpL3/YJGtCA==",
2136
- "signed_at": "2026-06-02T05:38:21.359Z"
2136
+ "signed_at": "2026-06-02T13:19:53.122Z"
2137
2137
  },
2138
2138
  {
2139
2139
  "name": "ransomware-response",
@@ -2213,7 +2213,7 @@
2213
2213
  ],
2214
2214
  "last_threat_review": "2026-05-22",
2215
2215
  "signature": "h48ASCz63aBfHzLKxMVDADMuT4atriK0iE6bJeVzZTsx/e8+hyv4fLP7+zYxT9Oe0Gss3v/Xy+t+Wd9uwzV+Aw==",
2216
- "signed_at": "2026-06-02T05:38:21.359Z"
2216
+ "signed_at": "2026-06-02T13:19:53.122Z"
2217
2217
  },
2218
2218
  {
2219
2219
  "name": "email-security-anti-phishing",
@@ -2266,7 +2266,7 @@
2266
2266
  "d3fend_refs": [],
2267
2267
  "last_threat_review": "2026-05-18",
2268
2268
  "signature": "FVBn4ex2qPIo9SHMVJ6tntoz4tVwjbIq3m6wDjjZyv2JODlS+90GBYCOkNamxxkmw/6de6SMs0YHQiF/xjo/DQ==",
2269
- "signed_at": "2026-06-02T05:38:21.359Z"
2269
+ "signed_at": "2026-06-02T13:19:53.123Z"
2270
2270
  },
2271
2271
  {
2272
2272
  "name": "age-gates-child-safety",
@@ -2334,7 +2334,7 @@
2334
2334
  "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"
2335
2335
  ],
2336
2336
  "signature": "ZHVdGWCcfG98tSVB0b9mwrsYwv71V3uUEl+6ss7omSQhmNvqV5s6MAZM5YladBt9MK/8T/zBrTYN4gAonOP+BQ==",
2337
- "signed_at": "2026-06-02T05:38:21.360Z"
2337
+ "signed_at": "2026-06-02T13:19:53.123Z"
2338
2338
  },
2339
2339
  {
2340
2340
  "name": "cloud-iam-incident",
@@ -2414,7 +2414,7 @@
2414
2414
  ],
2415
2415
  "last_threat_review": "2026-05-15",
2416
2416
  "signature": "r9ii4nb3HJELdtKCGF5qy9PHOiot3GC24yfxfGAKlLENHkdRvRkvvL99eV/6RXyfUaMyrnc2Te8tPQcNu5bsDg==",
2417
- "signed_at": "2026-06-02T05:38:21.360Z",
2417
+ "signed_at": "2026-06-02T13:19:53.123Z",
2418
2418
  "forward_watch": [
2419
2419
  "AWS IAM Identity Center session-policy refresh and step-up-on-admin enforcement (anticipated 2026-H2 release)",
2420
2420
  "GCP Workload Identity Federation principal-set attribute mapping tightening (post-2026 Q3 Federation hardening guide)",
@@ -2508,7 +2508,7 @@
2508
2508
  ],
2509
2509
  "last_threat_review": "2026-05-15",
2510
2510
  "signature": "9mfDtMApMAg9V/lmwpniNxo/6gNZoOEoYDfyFvyWvKrPMtc7H9F8uz06FVoARe/J49saAKTVXOurNE1D/KtpCQ==",
2511
- "signed_at": "2026-06-02T05:38:21.360Z",
2511
+ "signed_at": "2026-06-02T13:19:53.124Z",
2512
2512
  "forward_watch": [
2513
2513
  "Entra ID conditional access evolution post-Midnight Blizzard — Microsoft's 2025-2026 commitments on legacy-tenant MFA enforcement and OAuth-app consent gating",
2514
2514
  "Okta IPSIE (Interoperability Profile for Secure Identity in the Enterprise) OpenID Foundation working-group output and adoption timeline",
@@ -2526,6 +2526,6 @@
2526
2526
  ],
2527
2527
  "manifest_signature": {
2528
2528
  "algorithm": "Ed25519",
2529
- "signature_base64": "hW45T3BHJnmyURDqY+OF6bc2AnaT/ECyyNZA31VQEKyqg0QsEtTY2Cb1znzf2Bjolx47T0dUptAamW36T0ZDCg=="
2529
+ "signature_base64": "Zt8w2qUTGhNEKc89TDxD35uHvQGwjSzPDCVx1PwUa7i5ur/2bKGXI47z3d8+Wuw3382RTy8yN0d/gYtrionwDA=="
2530
2530
  }
2531
2531
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.16.9",
3
+ "version": "0.16.10",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (427 CVEs / 173 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
5
5
  "keywords": [
6
6
  "ai-security",
package/sbom.cdx.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:7cbe47c5-3d4f-4160-a176-1967c9b23f78",
4
+ "serialNumber": "urn:uuid:8bb2d8cc-10a2-40a8-a3e7-67aba4a5fe06",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2092-04-26T17:49:57.000Z",
7
+ "timestamp": "2100-04-09T18:45:32.000Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "blamejs",
11
11
  "name": "scripts/refresh-sbom.js",
12
- "version": "0.16.9"
12
+ "version": "0.16.10"
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.9",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.16.10",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.16.9",
19
+ "version": "0.16.10",
20
20
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 42 skills, 11 catalogs (427 CVEs / 173 CWEs / 805 ATT&CK + ICS / 170 ATLAS / 468 D3FEND / 8888 RFCs), 35 jurisdictions, 10-class catalog gap detector + budget gate, real XML parser + canonical-form diff + content-pattern regression detection, Ed25519-signed.",
21
21
  "licenses": [
22
22
  {
@@ -25,17 +25,17 @@
25
25
  }
26
26
  }
27
27
  ],
28
- "purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.9",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.16.10",
29
29
  "hashes": [
30
30
  {
31
31
  "alg": "SHA-256",
32
- "content": "a878765de519ea80b2e3045ac8934e38b30d87bbc11eb7b3ad1f63bfdf6b9b74"
32
+ "content": "6645aae260375bdfd45fef3a154848cb7b71634e0cb8a5063beacce54ed78dc6"
33
33
  }
34
34
  ],
35
35
  "externalReferences": [
36
36
  {
37
37
  "type": "distribution",
38
- "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.9"
38
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.16.10"
39
39
  },
40
40
  {
41
41
  "type": "vcs",
@@ -116,11 +116,11 @@
116
116
  "hashes": [
117
117
  {
118
118
  "alg": "SHA-256",
119
- "content": "4e6bb9d2ecea4da80bb73e0d5f3540dbe1d5eb6f7fa4e3671d04080930b55b0e"
119
+ "content": "00756e446fca3d66af2838dee91d30f591e5f8a179c97683eaebe4b3495a27d6"
120
120
  },
121
121
  {
122
122
  "alg": "SHA3-512",
123
- "content": "da628d1b4e0df048c16efdd0fc3c6be5699c1a425bfa03be4fa00d0483850398270d47f991757c77ac1971dc9677d1c37141a372adb3bd0f58f28bc27fed478a"
123
+ "content": "ebe3f08786809489c762ec4cc294b9f78124013d44ce82e77c2fb501cd49b0c1692c380881829e4e301a2c786fcbf2edf871d480fd006ef3dc72b0f1f3bfe66a"
124
124
  }
125
125
  ]
126
126
  },
@@ -1166,11 +1166,11 @@
1166
1166
  "hashes": [
1167
1167
  {
1168
1168
  "alg": "SHA-256",
1169
- "content": "3af0fa9ef45578fb8eb63f448c20ef5a6f347fbbee948e812de8e03960ef0efa"
1169
+ "content": "069a323205aa0ee280661b9bd1b85bb4d35ea547f7f5dfdd24ea545d14ef492f"
1170
1170
  },
1171
1171
  {
1172
1172
  "alg": "SHA3-512",
1173
- "content": "51304eff2510afd251adbf0a16b6ef049d9df7d9e8670a6412443b083fb68559ed63666df8486a1e1c044c00ace4735237d227ab0c4a264fe6edcc1467462e44"
1173
+ "content": "ff9b24c9197e80129933c1f2b89e820ebdbd401ef50b9e255723de04c959f81b0abef396bca24f0e8236b9048ff7fd516219a12f753d8297120d7ebe1af1c9de"
1174
1174
  }
1175
1175
  ]
1176
1176
  },
@@ -1226,11 +1226,11 @@
1226
1226
  "hashes": [
1227
1227
  {
1228
1228
  "alg": "SHA-256",
1229
- "content": "601eedde3731699669bd3b99f53ea1c06bf50c43f09ce49f68da86339d1eaae3"
1229
+ "content": "eb1a3820bba3203967c6057f12cd594e18f3044d966e1c7c1ba17757d902559f"
1230
1230
  },
1231
1231
  {
1232
1232
  "alg": "SHA3-512",
1233
- "content": "5e2ec6e21f88650c45a802486027091c95e4f4f9a1b8c43af15eaf2ac3b38fe632e7f9fcafde417705693c58587e17228fcec606459596792b168191fca2401a"
1233
+ "content": "1a0acc79ebea7cb9d80cde43503c14f14342ccffda834752c2f29215aacfba46a9d120e78c544cd389e6023ccaa519b829dc82eb4f4de6ed1d4ca2ce2ee06e97"
1234
1234
  }
1235
1235
  ]
1236
1236
  },
@@ -1301,11 +1301,11 @@
1301
1301
  "hashes": [
1302
1302
  {
1303
1303
  "alg": "SHA-256",
1304
- "content": "aa2c68cf9479becaaf9583f8a36c257f0d7550a49a8ecb9d1ddcdfede76d21af"
1304
+ "content": "0ce41dee14acf7998866f5adb63502b614b5501acf8f88c37cd277c96b91a87c"
1305
1305
  },
1306
1306
  {
1307
1307
  "alg": "SHA3-512",
1308
- "content": "1f3c09e2469da54138c896a25807a3ee1ca74813670d0e14b7adb151466fc3e391227a535779379d59c681225747685081f3a38a9a9a64d021c8ac961d2d16d0"
1308
+ "content": "58f6d75ff97f64633937bef489fdfefd6cf331115109cab30b2064cf3aebfaaddde6c973fa71b31bb44f06ecb485751ee9137e11f528a61b329b4699d53a4041"
1309
1309
  }
1310
1310
  ]
1311
1311
  },
@@ -1316,11 +1316,11 @@
1316
1316
  "hashes": [
1317
1317
  {
1318
1318
  "alg": "SHA-256",
1319
- "content": "279c195af8a5c5304e4e3b99a06deb8c96b6de037b7df218f1426a3a3dd31328"
1319
+ "content": "68bc62828f28047b5f09da9e5fa3c2cc3ed98d965c1a43dbf51b9fa7e45ec1a1"
1320
1320
  },
1321
1321
  {
1322
1322
  "alg": "SHA3-512",
1323
- "content": "62c93cead7f0749b9ec8462735a16ebef7ba85f8bd1a88e583e68a78486d96baa1372aabc1f958d5fb3237a9d82d05d05ba449ea94110c540f36fbee8c720a4e"
1323
+ "content": "c05800e93846fb755ded51be74047d84d032eeff9f5a0b818b1abc16f76c7032181ded4d5cf054d0387c3d7f2d71095f55a7a42c7f4c2764ec0115300ce75c85"
1324
1324
  }
1325
1325
  ]
1326
1326
  },
@@ -1451,11 +1451,11 @@
1451
1451
  "hashes": [
1452
1452
  {
1453
1453
  "alg": "SHA-256",
1454
- "content": "ed49d69da4ef5acb4fd827deaaaf280bd6317a5defa96f6fa5696dc96c2929b4"
1454
+ "content": "70fda3a2033d461f3ef1ac7e4bf259c9fe78090f1a1f5c09eb72dbf927e59955"
1455
1455
  },
1456
1456
  {
1457
1457
  "alg": "SHA3-512",
1458
- "content": "cee40245e9bb979c399942241faf038e95adb38ad66b6f3e70ba6034b78f774241310369773bd9ab36ceb0af1fe92e8f8b7e9b9965b3f629ab2847152f892530"
1458
+ "content": "8547b087e1881cf58a55fd0aab186441f508ad226be0ab4aa3efd13a0e864af776f29576e25df9b4324ec7a4712bb3476faac5ed63a226a6ff71d225c297c969"
1459
1459
  }
1460
1460
  ]
1461
1461
  },
@@ -1631,11 +1631,11 @@
1631
1631
  "hashes": [
1632
1632
  {
1633
1633
  "alg": "SHA-256",
1634
- "content": "b6a9cc3871cd272304fe55c7b979b8efe4d458217f7faadd750b3235a23cd986"
1634
+ "content": "fe42cfd0758cb2709dc7dad91c4e47b495d6ebf984b00ace2422de34e744d35f"
1635
1635
  },
1636
1636
  {
1637
1637
  "alg": "SHA3-512",
1638
- "content": "a0e8c965e7cfbfa5d6db032579de719d0aec55536f9c9fa734c0deef5367298b67b89f809fe34d9049bf3dda03834212865d95df4c1aeda23c1653483d67046e"
1638
+ "content": "a06a61b0f278a5973c0e4d6398d1688a574dec3442ca9823d0b4c5f37d1cb1cab47e8575580051b56e34829f8e22195ded1f4f57bdc3925ca32edae3e4c8839f"
1639
1639
  }
1640
1640
  ]
1641
1641
  },
@@ -1751,11 +1751,11 @@
1751
1751
  "hashes": [
1752
1752
  {
1753
1753
  "alg": "SHA-256",
1754
- "content": "3dab3ebc6c86b2318f956da3e343ad7b470f05dd30e2afbee5a8e3b3a845a926"
1754
+ "content": "b2f7ad163ce22cedc2a990633761b25d3f436012c20dc162739959f92220161f"
1755
1755
  },
1756
1756
  {
1757
1757
  "alg": "SHA3-512",
1758
- "content": "21f67b36206d0202a8c25045ded464d5cc36d7d97e5e4146ccce28ca08e31609fd579a4c65cbec27b10a03b61a84e80172c20886e1890052e92b80d306126f4b"
1758
+ "content": "342cee9e227424456314cbbdf150ee9a4077ea00d2ae87d58e5301d80492af5cf7f38f1d0aa7f66751c77e7345ddefb40bb5f2ba58921d665985654dce7cfa0c"
1759
1759
  }
1760
1760
  ]
1761
1761
  },
@@ -2201,11 +2201,11 @@
2201
2201
  "hashes": [
2202
2202
  {
2203
2203
  "alg": "SHA-256",
2204
- "content": "7cb063cd9a7179fa0e279273766be15011ea977e9b6601dd3cd4ca696d27abe1"
2204
+ "content": "7ef5ed5d9a6dd68b2d43419d7fa2d0408eb8b57b69588e51db136aa0f29e82a6"
2205
2205
  },
2206
2206
  {
2207
2207
  "alg": "SHA3-512",
2208
- "content": "d96a6d849df087061fde018299c2c1c96f3c92738a8f650c53bdef1148e10a92f77be29568a0145f4eaa522489843ac84a43d96bd48e4b66a5ab02d8d3c3c5f7"
2208
+ "content": "b96dad01e1385bc17bb1fa54d2f0d86558063856c12401fca1d96a01869de984b87cf5c54ded025a5f93762c109385ef6aed5552abff36070e281810ae063db8"
2209
2209
  }
2210
2210
  ]
2211
2211
  },
@@ -2216,11 +2216,11 @@
2216
2216
  "hashes": [
2217
2217
  {
2218
2218
  "alg": "SHA-256",
2219
- "content": "576480c2ad29550a06e5a3e48b7dae04aadc1b505d76e3a5b1db9179d61263c4"
2219
+ "content": "bd449efbfde0263c17aa1d2cf4ff57f39f8221e78a7f8eb62392b522acc7f3b8"
2220
2220
  },
2221
2221
  {
2222
2222
  "alg": "SHA3-512",
2223
- "content": "e3370698400f8b807680cebda861e73acedbf1602c9a7c2a8372c2285b2f88650a9d6e2931ebdb2cb229ad23e7c592787dea15a91adea5b4a854c6fc6842d38a"
2223
+ "content": "1a7ecda566d16f89d15d721ca491962344fe9f011c27126f48cde5fb887405d5be5a28dac1892add97fd7316ab2204aa69020ede2bc1c43db4d988b45c1f1dea"
2224
2224
  }
2225
2225
  ]
2226
2226
  },
@@ -2486,11 +2486,11 @@
2486
2486
  "hashes": [
2487
2487
  {
2488
2488
  "alg": "SHA-256",
2489
- "content": "f199cc754f202fa994c1769af5e02a4ef995ee02e87221422b00f945b03e99db"
2489
+ "content": "1e140b2a035043d72cfb1a46e57751bbb2d4b3d39ecc73b48c28625b260e17f3"
2490
2490
  },
2491
2491
  {
2492
2492
  "alg": "SHA3-512",
2493
- "content": "d504c65cda545acda811997487ca8b6fef3ee5b5447b537f502295d5f6f4266cb2ecb743389b04aa2b6118b021af6f2ce9c6f00e44cf60bd7c06b72456a500f8"
2493
+ "content": "f027c768c24cd0c5a4aff871afe82482a7ee655a56a69ab0d95662609a95fb28a31a3b4aa1108f879bdbf59ce097a6536e931c128105a7d93436e6eed59bfd82"
2494
2494
  }
2495
2495
  ]
2496
2496
  },
@@ -30,7 +30,7 @@ const ROOT = path.resolve(__dirname, "..");
30
30
  // Every key here was classified (adopted / already-owned / helper-dependent /
31
31
  // out-of-scope). A class appearing upstream but absent here is NEW and wants
32
32
  // triage. Refresh this list (and re-triage the delta) when this check fires.
33
- const UPSTREAM_TRIAGED = Object.freeze([
33
+ const UPSTREAM_TRIAGED = Object.freeze([ // keep-sorted
34
34
  "ai-disclosure-on-request-without-requested-gate",
35
35
  "archive-gz-without-safedecompress",
36
36
  "archive-wrap-partial-recipient",
@@ -59,7 +59,6 @@ const UPSTREAM_TRIAGED = Object.freeze([
59
59
  "math-random-noncrypto",
60
60
  "nfinity",
61
61
  "no-number-money-arithmetic",
62
- "numeric-opt-no-bounds-check",
63
62
  "primitive-unreachable",
64
63
  "process-exit",
65
64
  "raw-byte-literal",
@@ -19,6 +19,11 @@
19
19
  * VALID_ALLOW_CLASSES, or is missing the `— <reason>` tail. A typo'd
20
20
  * marker suppresses nothing, so the underlying violation would ship
21
21
  * unflagged — this meta-guard keeps the marker mechanism trustworthy.
22
+ * - unsorted-marked-array : a flat string array tagged `// keep-sorted` that
23
+ * drifted out of alphabetical order. Opt-in — only marked arrays are
24
+ * checked, so a one-time allowlist sort becomes a standing guarantee.
25
+ * - misaligned-marked-run : a `// keep-aligned` const/weight table whose
26
+ * `=`/`:` assignment columns are not all equal. Opt-in, same shape.
22
27
  *
23
28
  * Exceptions live at the violation site, not in this file:
24
29
  * - file-level, in the first 50 lines: // codebase-patterns:allow-file <class> — <reason>
@@ -42,6 +47,8 @@ const VALID_ALLOW_CLASSES = Object.freeze({
42
47
  "process-exit-after-stdout-write": true,
43
48
  "dynamic-regex": true,
44
49
  "bidi-codepoint-literal": true,
50
+ "unsorted-marked-array": true,
51
+ "misaligned-marked-run": true,
45
52
  });
46
53
 
47
54
  const EXCLUDE_DIRS = new Set([
@@ -272,6 +279,91 @@ function detectOrphanAllowClass(files) {
272
279
  return hits;
273
280
  }
274
281
 
282
+ // ---- opt-in readability detectors (preventative) -------------------------
283
+ // These fire ONLY on sites that explicitly opt in via a marker, so unmarked
284
+ // code is never flagged. They turn a one-time cleanup (sorting an allowlist,
285
+ // aligning a const table) into a standing guarantee: mark the cleaned site and
286
+ // the gate keeps it clean.
287
+
288
+ // `// keep-sorted` marks a flat string-literal array that must stay
289
+ // alphabetically sorted (e.g. an allowlist). Only arrays whose opening line
290
+ // carries the marker are checked; arrays containing object/nested elements are
291
+ // skipped (not a flat string list).
292
+ function scanUnsortedMarkedArray(rel, lines) {
293
+ const hits = [];
294
+ for (let i = 0; i < lines.length; i++) {
295
+ if (!/\/\/\s*keep-sorted\b/.test(lines[i])) continue;
296
+ const openIdx = lines[i].indexOf("[");
297
+ if (openIdx === -1) continue;
298
+ let depth = 0, started = false, body = "";
299
+ for (let j = i; j < lines.length; j++) {
300
+ const seg = (j === i) ? lines[j].slice(openIdx) : lines[j];
301
+ for (const ch of seg) {
302
+ if (ch === "[") { depth++; started = true; }
303
+ else if (ch === "]") { depth--; }
304
+ }
305
+ body += " " + seg;
306
+ if (started && depth <= 0) break;
307
+ }
308
+ if (/[{]/.test(body)) continue; // object/nested elements — not a flat string array
309
+ const strs = [];
310
+ const re = /(['"])((?:\\.|(?!\1).)*)\1/g;
311
+ let m;
312
+ while ((m = re.exec(body)) !== null) strs.push(m[2]);
313
+ if (strs.length < 2) continue;
314
+ const sorted = [...strs].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
315
+ if (strs.join("") !== sorted.join("")) {
316
+ const k = strs.findIndex((s, idx) => idx > 0 && strs[idx - 1] > s);
317
+ hits.push({ file: rel, line: i + 1, content: lines[i].trim(), why: `marked // keep-sorted but "${strs[k]}" is out of alphabetical order` });
318
+ }
319
+ }
320
+ return hits;
321
+ }
322
+ function detectUnsortedMarkedArray(files) {
323
+ const hits = [];
324
+ for (const rel of (files || filesUnder(["bin/exceptd.js", "lib", "orchestrator", "scripts"]))) {
325
+ if (rel === "scripts/check-codebase-patterns.js") continue; // holds the detector + its own marker prose
326
+ hits.push(...scanUnsortedMarkedArray(rel, readLines(rel)));
327
+ }
328
+ return hits;
329
+ }
330
+
331
+ // `// keep-aligned` marks a contiguous run of `IDENT = value` / `IDENT: value`
332
+ // lines (a const/weight table) whose assignment columns must all line up. The
333
+ // run is the lines immediately after the marker, until a blank or non-assignment
334
+ // line. Opt-in, so only deliberately-aligned tables are enforced.
335
+ function scanMisalignedMarkedRun(rel, lines) {
336
+ const hits = [];
337
+ for (let i = 0; i < lines.length; i++) {
338
+ if (!/\/\/\s*keep-aligned\b/.test(lines[i])) continue;
339
+ const run = [];
340
+ for (let j = i + 1; j < lines.length; j++) {
341
+ if (/^\s*$/.test(lines[j])) break;
342
+ const code = stripLineComment(lines[j]).replace(/\s+$/, "");
343
+ const m = code.match(/^(\s*[A-Za-z_$][\w$.'"-]*\s*)([:=])\s/);
344
+ if (!m) break;
345
+ run.push({ lineNo: j + 1, col: m[1].length, op: m[2], content: lines[j].trim() });
346
+ }
347
+ if (run.length < 2) continue;
348
+ const op = run[0].op;
349
+ const cols = run.filter((r) => r.op === op).map((r) => r.col);
350
+ const target = Math.max(...cols);
351
+ const bad = run.find((r) => r.op === op && r.col !== target);
352
+ if (bad) {
353
+ hits.push({ file: rel, line: bad.lineNo, content: bad.content, why: `marked // keep-aligned but the '${op}' columns are not all equal` });
354
+ }
355
+ }
356
+ return hits;
357
+ }
358
+ function detectMisalignedMarkedRun(files) {
359
+ const hits = [];
360
+ for (const rel of (files || filesUnder(["bin/exceptd.js", "lib", "orchestrator", "scripts"]))) {
361
+ if (rel === "scripts/check-codebase-patterns.js") continue;
362
+ hits.push(...scanMisalignedMarkedRun(rel, readLines(rel)));
363
+ }
364
+ return hits;
365
+ }
366
+
275
367
  const CLASSES = [
276
368
  {
277
369
  id: "process-exit-after-stdout-write",
@@ -282,9 +374,21 @@ const CLASSES = [
282
374
  {
283
375
  id: "dynamic-regex",
284
376
  run: detectDynamicRegex,
285
- warnOnly: true, // flip to false next release once the known sites carry markers
377
+ warnOnly: false,
286
378
  hint: "RegExp from operator input is a ReDoS sink — anchor + length-cap, or `// allow:dynamic-regex — <reason>` when the pattern is a trusted bundled schema",
287
379
  },
380
+ {
381
+ id: "unsorted-marked-array",
382
+ run: detectUnsortedMarkedArray,
383
+ warnOnly: false,
384
+ hint: "a flat string array tagged `// keep-sorted` drifted out of alphabetical order — re-sort it, or drop the marker if the order is intentional",
385
+ },
386
+ {
387
+ id: "misaligned-marked-run",
388
+ run: detectMisalignedMarkedRun,
389
+ warnOnly: false,
390
+ hint: "a `// keep-aligned` const/weight table has uneven assignment columns — realign the `=`/`:` columns, or drop the marker",
391
+ },
288
392
  {
289
393
  id: "bidi-codepoint-literal",
290
394
  run: detectBidiCodepointLiteral,
@@ -331,6 +435,10 @@ module.exports = {
331
435
  detectDynamicRegex,
332
436
  detectBidiCodepointLiteral,
333
437
  detectOrphanAllowClass,
438
+ detectUnsortedMarkedArray,
439
+ detectMisalignedMarkedRun,
440
+ scanUnsortedMarkedArray,
441
+ scanMisalignedMarkedRun,
334
442
  filesUnder,
335
443
  };
336
444
 
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * Usage:
13
13
  * node scripts/release.js prepare [--minor] # bump + sign + indexes + snapshot + sbom + baseline
14
- * node scripts/release.js gates # npm test + 18-gate predeploy
14
+ * node scripts/release.js gates # npm test + 20-gate predeploy
15
15
  * node scripts/release.js commit # release branch + signed commit
16
16
  * node scripts/release.js push # push branch + open PR
17
17
  * node scripts/release.js watch # CI watch + flag unresolved review threads
@@ -576,7 +576,7 @@ function cmdHelp() {
576
576
  console.log("");
577
577
  console.log("Usage:");
578
578
  console.log(" node scripts/release.js prepare [--minor] # bump + sign + indexes + snapshot + sbom + baseline");
579
- console.log(" node scripts/release.js gates # npm test + 18-gate predeploy");
579
+ console.log(" node scripts/release.js gates # npm test + 20-gate predeploy");
580
580
  console.log(" node scripts/release.js commit # release branch + signed commit");
581
581
  console.log(" node scripts/release.js push # push branch + open PR");
582
582
  console.log(" node scripts/release.js watch # CI watch + flag unresolved review threads");