@blamejs/exceptd-skills 0.18.3 → 0.18.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.18.3",
3
+ "version": "0.18.5",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (459 CVEs / 181 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:d2bad09b-5970-48a7-87df-4a4313ccbaca",
4
+ "serialNumber": "urn:uuid:eb1ee0a4-0742-4f4b-a0fe-81744d380236",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2138-01-13T15:47:39.000Z",
7
+ "timestamp": "2151-01-01T21:29:08.000Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "blamejs",
11
11
  "name": "scripts/refresh-sbom.js",
12
- "version": "0.18.3"
12
+ "version": "0.18.5"
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.3",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.5",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.18.3",
19
+ "version": "0.18.5",
20
20
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 51 skills, 11 catalogs (459 CVEs / 181 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.18.3",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.18.5",
29
29
  "hashes": [
30
30
  {
31
31
  "alg": "SHA-256",
32
- "content": "85527924576e3ea8a9342642693d8e2c561a0992a623338c30998ba5fc8ccfad"
32
+ "content": "969194699d1e9d763d9be4e1a15ac51abdf9542799dceb4e4d5f252d58d15f96"
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.18.3"
38
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.18.5"
39
39
  },
40
40
  {
41
41
  "type": "vcs",
@@ -116,11 +116,11 @@
116
116
  "hashes": [
117
117
  {
118
118
  "alg": "SHA-256",
119
- "content": "19ed87890304e9384dd1dbed4d9e659d1239ced5e8901a8529d7af76c81ed9d7"
119
+ "content": "7b291ac3ee6977e02aa614587ad3fa48b0c01a98fad35e93ebe20847e92255cf"
120
120
  },
121
121
  {
122
122
  "alg": "SHA3-512",
123
- "content": "4a4d31c667079f161c4d980ff80a2f8e214b748e709177bd9942bd3da2e7dd6a96d0fd0d15d374c55455054b531ebb095ff6a2149a8861c1464bb3f19131204c"
123
+ "content": "a189a036e9197f779e04f1c0fe293e599d070b1625bcad3f1f24d20fb34216812ef040be23a69623b49ecce6a6bc40c1317fb1c1db20b881a4be8c6b73b22d3b"
124
124
  }
125
125
  ]
126
126
  },
@@ -281,11 +281,11 @@
281
281
  "hashes": [
282
282
  {
283
283
  "alg": "SHA-256",
284
- "content": "379a16da41f9d9ff66c4e1d19453d6a92d71da5f4464f94440d5185e1e14b4de"
284
+ "content": "0d599c924d9ac9e30a21ee25cf1a9f7bed60205c56074857505ae3b358890692"
285
285
  },
286
286
  {
287
287
  "alg": "SHA3-512",
288
- "content": "803a6475512aea6fb1eb2f64a20ae3122398cee7408a00a0ca309ed8d41d6d70fb442603bfbdfa43b043b198c3662f2e141f73ae7729f47e4ed37750111d6dbd"
288
+ "content": "02154658c71159036f94d0bcd2116d39783e26a4a0199d82540a5e73b7bca154bbfadece13453af3e701fcebbe2a3d31845a803e33f5a65d5ec1c5013dc4b39e"
289
289
  }
290
290
  ]
291
291
  },
@@ -356,11 +356,11 @@
356
356
  "hashes": [
357
357
  {
358
358
  "alg": "SHA-256",
359
- "content": "9a54bccb9f24f84b32024216cc3f53819a053721ac8ab43c326859e68fc0ffaf"
359
+ "content": "349d14b2777342d38e5f8b0149a9ebd7703ee59f67a9efee200d1e19313088ac"
360
360
  },
361
361
  {
362
362
  "alg": "SHA3-512",
363
- "content": "30e0c721b086eabb1445b99a945d25b0ace33b2c03aa0a4fe7c2cc9d87247a5344b12b9fc33a52ffcc805712f27273f125989439fbe8fba1f35d1946f14c968b"
363
+ "content": "f57d9a8d43d8f5d953e0bcf0de0a2f57fae4f4345dfc87d0ee6c26b03da53b4978645f82f40b68b45ad1dd272b1217debe83bf33dd65206ed21104859f502209"
364
364
  }
365
365
  ]
366
366
  },
@@ -926,11 +926,11 @@
926
926
  "hashes": [
927
927
  {
928
928
  "alg": "SHA-256",
929
- "content": "b21d03b948c41bc8a854e2f057948ecf844bd8c105848aeb141d1eadf8192c31"
929
+ "content": "5db2f7006a1d7f2b8642a3e393213394e92798604576df973d0a0b8550f4b5ac"
930
930
  },
931
931
  {
932
932
  "alg": "SHA3-512",
933
- "content": "19e242bf80e0d2984e0e807c3c4b236afc59561cc63a29efcc1f34488adc71dd0ba21eb8b6162eae83b87ca872d2d5073c1efc7aff05761b16b76631acfaaba4"
933
+ "content": "93ee274979c14a6869359e434677396b3157470460bd9c3ef3091573d5c60e208f4600a526ff1404e224844bc2fc734f3db67217d57c8ec8ce728ea7c5240027"
934
934
  }
935
935
  ]
936
936
  },
@@ -1451,11 +1451,11 @@
1451
1451
  "hashes": [
1452
1452
  {
1453
1453
  "alg": "SHA-256",
1454
- "content": "2a488a8dcbb25e0e46dd04e6c8294406f3f75833ff3b2e59fdc9ba3e589df5c8"
1454
+ "content": "e8abd49962816b5d4aa1ef5d37072f467cff47ffc241c963fb6c0811fc0a7e60"
1455
1455
  },
1456
1456
  {
1457
1457
  "alg": "SHA3-512",
1458
- "content": "39ec71547e6c9c9823749ae0248f886af234358e6937da59001ae5cd3bb846a7134b0cbf15f5a086f80b2eec1d0a9ebee96d01568559930a5ac560235b19a246"
1458
+ "content": "7e0d3ecc78cc37247d2a3c917258a3d35ac1a1aadfe3fae058239bc11791b50f6bc9cc8075728413ec99fa5d22e4b8158f5afe0a75a49ca63ebd949d8d8c3158"
1459
1459
  }
1460
1460
  ]
1461
1461
  },
@@ -1871,11 +1871,11 @@
1871
1871
  "hashes": [
1872
1872
  {
1873
1873
  "alg": "SHA-256",
1874
- "content": "b4e322034ba1ebafa3e706772a9cada8131a52adc42197ecda1705bb13d2b131"
1874
+ "content": "3298b773823c834dde46c771fc14baa0ec84f6c7059b52ad568d519305fde144"
1875
1875
  },
1876
1876
  {
1877
1877
  "alg": "SHA3-512",
1878
- "content": "71f2409f9bcc761fd22129a42da2351366cfcddf484388680e12c29ec5a2aa71ed0f37e4e2036a7ac3b639725bb193d252de50c34db9361299cfe751fc9a3a31"
1878
+ "content": "dd01e63ba81e97db83f3ff82c21d3642f1c71f8004c41b32b4d1d00b25a7edee405c38be12e7a8262764f343484ce8776bb0052627594de320cc6905df641b5a"
1879
1879
  }
1880
1880
  ]
1881
1881
  },
@@ -1886,11 +1886,11 @@
1886
1886
  "hashes": [
1887
1887
  {
1888
1888
  "alg": "SHA-256",
1889
- "content": "186d214a360f4e334c45e28cfc4fa9ff246c3aac02c519d238151be579f9b6a2"
1889
+ "content": "d479f64741c9de91df610b874331ea07ebe442b43337dd8c9919fb9c43a78dfe"
1890
1890
  },
1891
1891
  {
1892
1892
  "alg": "SHA3-512",
1893
- "content": "1e4891e5d789e696a11ee584c17a18d2e6633226a66f1eaac9c9c88fe1265a7e0e059df2bd2a937c8f51d312ab2bdb72d507666f63292e98908bf136961b2a62"
1893
+ "content": "9bf29cc45e0e1b4af8a829bdcda30a8af57758abc88951a17ec687d713af18dd24c8b30f543096fbb1d6090737ea8ccfe509c6c892077f449dc21a654f7ca740"
1894
1894
  }
1895
1895
  ]
1896
1896
  },
@@ -1901,11 +1901,11 @@
1901
1901
  "hashes": [
1902
1902
  {
1903
1903
  "alg": "SHA-256",
1904
- "content": "58e3b2b80b32fb30a31db9857fcbd356ddd6c61da6dbc3a762c85a4d212dc787"
1904
+ "content": "d6486b4209c765883b69c32a3f52df0956c2e9cb770cc0d603fafb5b15aeb865"
1905
1905
  },
1906
1906
  {
1907
1907
  "alg": "SHA3-512",
1908
- "content": "59ef7ad1198a2a969e8b5e3fe8b07f65441898a481f6f23f4860dcb7a4a48bae0242c03046a4fb79d31c0d2509a096c3346bb2bc9078ce11b9a6aee9c20ce3ba"
1908
+ "content": "d0b227b3cc046f601ab3ae34c687c88b78e02bfb40ea54638916d55da0c1d3b208176e7a3f58a88585ad9c1bb210c9a7121f5086777b22b88048d8c87699f3a6"
1909
1909
  }
1910
1910
  ]
1911
1911
  },
@@ -2096,11 +2096,11 @@
2096
2096
  "hashes": [
2097
2097
  {
2098
2098
  "alg": "SHA-256",
2099
- "content": "9f54c09e25593d82ba36bffcf5ee6b8137eab9e1f5408b7cf6cb681a69806d4c"
2099
+ "content": "b13367b33e18784be5f0afb08348f83d392943c008cab9b3ca95410331f41281"
2100
2100
  },
2101
2101
  {
2102
2102
  "alg": "SHA3-512",
2103
- "content": "9711d20338cd19da03cb50d089aa42ce5c8f45513d92523a84572494337d88654a29bedc991009ba4522346d3b04506621974707a537c1e24ee1d985017d113f"
2103
+ "content": "b4b493cc6fc6fd2f60cb67e0667c3ba2885e04d8a018d22e82a636a14c33ebcc720dfc3ad0680bf7717e7fb223dbbf8c4307f7e74f934142d483188cdad344cb"
2104
2104
  }
2105
2105
  ]
2106
2106
  },
@@ -2681,11 +2681,11 @@
2681
2681
  "hashes": [
2682
2682
  {
2683
2683
  "alg": "SHA-256",
2684
- "content": "fff6e7b8d8d50d30fa8839bf529fb7551f4b78541d7dddc2613064bff4a0cc8b"
2684
+ "content": "06d9c0b3b342b3b96d5c17675ec060cc0273e793b19d004af0fde2b3669ce1b0"
2685
2685
  },
2686
2686
  {
2687
2687
  "alg": "SHA3-512",
2688
- "content": "b1adf63200cf14cbd6757c963e3f4c67342ed3db9ee33ae73dcaf5e34a00c862d2d40f5be26af0acd274eb7855481c4ab6ee7638253b7e94af9c92572e1110d8"
2688
+ "content": "12fdee49bc8fa5f90a503439b778c6203f75a8fee4ef6a7e6df9690a4216070cea5ee25979ba75b522a0f4b00df3ece0b77bc627db6ce59c23b5282b047a877e"
2689
2689
  }
2690
2690
  ]
2691
2691
  },
@@ -235,7 +235,7 @@ const OUTPUTS = [
235
235
  build: (ctx) => {
236
236
  const codes = Object.keys(ctx.globalFrameworks).filter((k) => !k.startsWith("_") && k !== "GLOBAL");
237
237
  const NAME_TO_CODE = {
238
- "GDPR": "EU", "NIS2": "EU", "DORA": "EU", "EU AI Act": "EU", "ENISA": "EU",
238
+ "GDPR": "EU", "NIS2": "EU", "DORA": "EU", "EU AI Act": "EU", "ENISA": "EU_ENISA",
239
239
  "NCSC": "UK", "Children's Code": "UK", "Online Safety Act": "UK",
240
240
  "ISM": "AU", "Essential 8": "AU", "APRA": "AU", "eSafety": "AU",
241
241
  "MAS TRM": "SG", "CSA Singapore": "SG",
@@ -336,8 +336,10 @@ const OUTPUTS = [
336
336
  entry_types: ["CVE", "CWE"],
337
337
  },
338
338
  };
339
- // CVE half
340
- for (const cveId of Object.keys(ctx.cveCatalog).filter((k) => !k.startsWith("_"))) {
339
+ // CVE half. Skip draft (_draft) CVEs so this derivation agrees with the
340
+ // reverse-ref index (refresh-reverse-refs.js excludes drafts) — both must
341
+ // reflect the same operator-queryable, curated truth.
342
+ for (const cveId of Object.keys(ctx.cveCatalog).filter((k) => !k.startsWith("_") && ctx.cveCatalog[k] && ctx.cveCatalog[k]._draft !== true)) {
341
343
  const cve = ctx.cveCatalog[cveId];
342
344
  const referencingSkills = ctx.skills
343
345
  .filter((s) => {
@@ -692,7 +694,11 @@ function writeMeta(ctx, results) {
692
694
  trigger_table_entries: Object.keys(trigger).length,
693
695
  chains_cve_entries: cveChainCount,
694
696
  chains_cwe_entries: cweChainCount,
695
- jurisdictions_indexed: Object.keys(jurisdictionClocks.by_jurisdiction || {}).length || Object.keys(readBack("jurisdiction-map") || {}).length,
697
+ // The jurisdiction-map indexes EVERY jurisdiction code (including those
698
+ // with no breach/patch clocks), so it — not the clocks table — is the
699
+ // count of jurisdictions actually indexed. (`jurisdiction_clocks` below
700
+ // reports the clocks-only subset.)
701
+ jurisdictions_indexed: Object.keys(readBack("jurisdiction-map") || {}).length || Object.keys(jurisdictionClocks.by_jurisdiction || {}).length,
696
702
  handoff_dag_nodes: handoff.nodes?.length || 0,
697
703
  summary_cards: Object.keys(summaryCards.skills || {}).length,
698
704
  section_offsets_skills: Object.keys(sectionOffsets.skills || {}).length,
@@ -3,18 +3,29 @@
3
3
  /**
4
4
  * scripts/sync-manifest-metadata.js
5
5
  *
6
- * The per-skill `forward_watch` and `last_threat_review` fields in
7
- * manifest.json are a cache of the authoritative values in each skill's
8
- * frontmatter. There was no step that refreshed that cache, so editing a
9
- * skill's frontmatter (e.g. bumping last_threat_review on a threat-review
10
- * pass, or rewording a forward_watch item) left the manifest copy stale.
11
- * Over time the two diverged on dozens of skills.
6
+ * Several per-skill fields in manifest.json are a cache of the authoritative
7
+ * values in each skill's frontmatter (the linter and the staleness gate read
8
+ * frontmatter, so frontmatter is the single source of truth). Nothing kept
9
+ * the cache in sync, so editing frontmatter left the manifest copy stale.
12
10
  *
13
- * This script rewrites the manifest's `forward_watch` and
14
- * `last_threat_review` for every skill from that skill's frontmatter, which
15
- * is the single source of truth (the linter and the staleness gate both read
16
- * frontmatter). Run it whenever skill frontmatter changes, then re-run the
17
- * sign-all step so the refreshed manifest is signed.
11
+ * Two sync disciplines, because the fields differ in kind:
12
+ *
13
+ * - MIRROR (exact): `last_threat_review` (scalar) and `forward_watch`
14
+ * (array) are an exact copy of frontmatter. Synced by replace.
15
+ *
16
+ * - COVER (union): the cross-reference arrays (`data_deps`,
17
+ * `framework_gaps`, `atlas_refs`, `attack_refs`, `rfc_refs`, `cwe_refs`,
18
+ * `d3fend_refs`) are an ENRICHED SUPERSET — the manifest may carry extra
19
+ * curated refs that the derived indexes (build-indexes) and the
20
+ * reverse-ref refresh (refresh-reverse-refs) read. The invariant is that
21
+ * every frontmatter-declared ref MUST appear in the manifest, or it
22
+ * silently vanishes from those surfaces. Synced by UNION (append the
23
+ * missing frontmatter refs, preserve the manifest's order + enrichment) —
24
+ * never by replace, which would drop curated refs.
25
+ *
26
+ * Run it whenever skill frontmatter changes, then re-run sign-all (and, when
27
+ * cross-ref arrays changed, refresh-reverse-refs + build-indexes) so the
28
+ * refreshed manifest is signed and the derived surfaces pick up the refs.
18
29
  *
19
30
  * tests/manifest-frontmatter-sync.test.js fails the suite if the cache ever
20
31
  * drifts again, so a missed run is caught before release rather than shipping
@@ -34,6 +45,10 @@ const MANIFEST = path.join(ROOT, "manifest.json");
34
45
  // The frontmatter fields the manifest caches and must mirror verbatim.
35
46
  const MIRRORED_SCALAR = ["last_threat_review"];
36
47
  const MIRRORED_ARRAY = ["forward_watch"];
48
+ // Cross-reference arrays: the manifest is an enriched superset, so sync by
49
+ // UNION (cover) — append frontmatter refs the manifest is missing, keep the
50
+ // manifest's own curated refs. See the header for why replace would regress.
51
+ const MIRRORED_COVER = ["data_deps", "framework_gaps", "atlas_refs", "attack_refs", "rfc_refs", "cwe_refs", "d3fend_refs"];
37
52
 
38
53
  function skillFrontmatter(id) {
39
54
  const p = path.join(ROOT, "skills", id, "skill.md");
@@ -73,6 +88,17 @@ function sync() {
73
88
  changed++;
74
89
  }
75
90
  }
91
+ for (const key of MIRRORED_COVER) {
92
+ const want = Array.isArray(fm[key]) ? fm[key] : [];
93
+ if (!want.length) continue;
94
+ const have = Array.isArray(entry[key]) ? entry[key] : [];
95
+ const haveSet = new Set(have);
96
+ const missing = want.filter((x) => !haveSet.has(x));
97
+ if (missing.length) {
98
+ entry[key] = [...have, ...missing];
99
+ changed += missing.length;
100
+ }
101
+ }
76
102
  }
77
103
  if (errors.length) {
78
104
  for (const e of errors) process.stderr.write(`[sync-manifest-metadata] ${e}\n`);