@blamejs/exceptd-skills 0.12.26 → 0.12.27

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,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.27 — 2026-05-15
4
+
5
+ **Patch: opt-in `--bundle-deterministic` mode for reproducible CSAF + OpenVEX + close-envelope bytes. Closes cycle 6 III P2-E + cycle 7 CCC bundle-non-determinism finding.**
6
+
7
+ ### New flags
8
+
9
+ - **`--bundle-deterministic`** (boolean, off by default) — when set, the bundle-emit path produces byte-stable output for the same inputs. CSAF `tracking.initial_release_date` / `current_release_date` / `generator.date` / `revision_history[0].date`, OpenVEX top-level `timestamp` + per-statement `timestamp`, close-envelope `acceptance_date` + `regression_schedule.next_run` + `generated_at` all freeze to a single epoch. Auto-generated session IDs derive deterministically from `sha256(playbook_id ∥ evidence_hash ∥ engine_version)` rather than `crypto.randomBytes`. CSAF `vulnerabilities[]` + OpenVEX `statements[]` arrays sort by primary id.
10
+ - **`--bundle-epoch <ISO-8601>`** (value-bearing, optional) — operator-supplied freeze epoch. When omitted, the deterministic mode falls back to `playbook._meta.last_threat_review` (the canonical "this catalog was last reviewed at" timestamp). Honored only when `--bundle-deterministic` is set.
11
+
12
+ Both flags wired for `run`, `ci`, `run-all`, `ai-run`, `ingest`. Per-verb help blocks document them.
13
+
14
+ ### Why
15
+
16
+ - **CI bundle diffing**: `git diff` over `evidence_package.bundle_body` against a baseline becomes signal-bearing only when drift is signal, not noise. Pre-v0.12.27 the same evidence produced ~640 bytes of timestamp drift across CSAF + OpenVEX + close-envelope per run.
17
+ - **Auditor evidence reuse**: ISO 27001 / SOC 2 audits expect re-emit against the same submission to produce byte-equal evidence.
18
+ - **SLSA / Sigstore alignment**: reproducible build evidence requires deterministic outputs the verifier can hash and compare.
19
+
20
+ CSAF 2.0 §3.1.11.2-5 permits identical `initial_release_date` / `current_release_date` for never-revised advisories; freezing to a catalog epoch is spec-compliant. The strict-validator pass (BSI CSAF Validator) accepts the deterministic-mode output unchanged.
21
+
22
+ ### Default-mode regression guard
23
+
24
+ When neither flag is set, bundle output is byte-identical to v0.12.26 — no existing operator sees a behavioral change. A regression test pins this: two consecutive runs in default mode produce different CSAF `tracking.initial_release_date` values, asserting the determinism is opt-in and cannot accidentally activate.
25
+
26
+ ### Test coverage
27
+
28
+ `tests/bundle-determinism.test.js` (new, 7 exact-code tests):
29
+ 1. Two runs same inputs + same epoch → byte-identical CSAF/OpenVEX/summary
30
+ 2. Different `--bundle-epoch` → bundles differ only in timestamp fields
31
+ 3. Different evidence → bundles differ in `vulnerabilities[]` length; timestamps frozen
32
+ 4. Default mode → regression-guard timestamp drift
33
+ 5. `--bundle-epoch invalid-iso` → exit 1 + structured error
34
+ 6. `--bundle-deterministic` without `--bundle-epoch` falls back to `playbook._meta.last_threat_review`
35
+ 7. Array sort: random-order CVE evidence → `vulnerabilities[]` always ascending by `cve_id`
36
+
37
+ Existing CSAF + OpenVEX + CLI test suites pass unchanged (53/53 + 30/30; no default-mode regression).
38
+
39
+ Test count: 1058 pass (5 skipped). Predeploy gates: 14/14. Skills: 39/39 signed.
40
+
3
41
  ## 0.12.26 — 2026-05-15
4
42
 
5
43
  **Patch: sector-telecom skill ships, with supporting framework-gap and ATLAS catalog scaffolding. Closes the cycle 8 LLL P1 finding that the unmodeled RWEP signal from Salt Typhoon-class campaigns was the highest gap in the catalog.**
package/bin/exceptd.js CHANGED
@@ -909,6 +909,11 @@ function dispatchPlaybook(cmd, argv) {
909
909
  "force-overwrite", "no-stream", "block-on-jurisdiction-clock",
910
910
  "force-replay",
911
911
  "json-stdout-only", "fix", "human", "json", "strict-preconditions",
912
+ // v0.12.27: --bundle-deterministic opts the bundle build into
913
+ // byte-stable output (frozen timestamps, deterministic session_id
914
+ // fallback, sorted vulnerabilities[] / statements[]). Pairs with
915
+ // --bundle-epoch <ISO> for the frozen timestamp value.
916
+ "bundle-deterministic",
912
917
  // v0.12.9: doctor --shipped-tarball runs the verify-shipped-tarball
913
918
  // gate alongside --signatures. doctor --registry-check + --signatures
914
919
  // were already accepted; explicit registration removes the silent
@@ -1266,6 +1271,55 @@ function dispatchPlaybook(cmd, argv) {
1266
1271
  runOpts.csafStatus = cs;
1267
1272
  }
1268
1273
 
1274
+ // --bundle-deterministic + --bundle-epoch (v0.12.27): opt-in deterministic
1275
+ // bundle emit. When set, CSAF / OpenVEX / close-envelope timestamps freeze
1276
+ // to the supplied epoch (or the playbook's last_threat_review fallback),
1277
+ // the auto-generated session_id derives from sha256(playbook + evidence_hash
1278
+ // + engine_version) when the operator did not pass --session-id, and
1279
+ // vulnerabilities[] / statements[] sort deterministically. Opt-in so the
1280
+ // default emit path stays byte-identical to pre-v0.12.27 output.
1281
+ if (args["bundle-deterministic"] !== undefined && args["bundle-deterministic"] !== false) {
1282
+ if (!BUNDLE_FLAG_RELEVANT_VERBS.has(cmd)) {
1283
+ return emitError(
1284
+ `${cmd}: --bundle-deterministic is irrelevant on this verb (no bundle is assembled). --bundle-deterministic only applies to verbs that drive phases 5-7: ${[...BUNDLE_FLAG_RELEVANT_VERBS].sort().join(", ")}.`,
1285
+ { verb: cmd, flag: "bundle-deterministic", error_class: "irrelevant-flag", accepted_verbs: [...BUNDLE_FLAG_RELEVANT_VERBS].sort() },
1286
+ pretty
1287
+ );
1288
+ }
1289
+ runOpts.bundleDeterministic = true;
1290
+ }
1291
+ if (args["bundle-epoch"] !== undefined) {
1292
+ if (!BUNDLE_FLAG_RELEVANT_VERBS.has(cmd)) {
1293
+ return emitError(
1294
+ `${cmd}: --bundle-epoch is irrelevant on this verb (no bundle is assembled). --bundle-epoch only applies to verbs that drive phases 5-7: ${[...BUNDLE_FLAG_RELEVANT_VERBS].sort().join(", ")}.`,
1295
+ { verb: cmd, flag: "bundle-epoch", error_class: "irrelevant-flag", accepted_verbs: [...BUNDLE_FLAG_RELEVANT_VERBS].sort() },
1296
+ pretty
1297
+ );
1298
+ }
1299
+ const epoch = args["bundle-epoch"];
1300
+ if (typeof epoch !== "string") {
1301
+ return emitError(
1302
+ `${cmd}: --bundle-epoch must be a string ISO-8601 timestamp.`,
1303
+ { verb: cmd, flag: "bundle-epoch", provided: typeof epoch },
1304
+ pretty
1305
+ );
1306
+ }
1307
+ // Reuse validateIsoSince — the same calendar-shape gate used for --since.
1308
+ const isoErr = validateIsoSince(epoch);
1309
+ if (isoErr) {
1310
+ return emitError(
1311
+ `${cmd}: --bundle-epoch must be a parseable ISO-8601 calendar timestamp (e.g. 2026-01-01T00:00:00Z). Got: ${JSON.stringify(epoch).slice(0, 80)}`,
1312
+ { verb: cmd, flag: "bundle-epoch", provided: epoch.slice(0, 80) },
1313
+ pretty
1314
+ );
1315
+ }
1316
+ // Normalise to a full ISO timestamp so downstream consumers don't have
1317
+ // to handle the date-only shape. Date-only inputs render as
1318
+ // YYYY-MM-DDT00:00:00.000Z; full timestamps round-trip unchanged modulo
1319
+ // ms precision (Date.prototype.toISOString always emits ms).
1320
+ runOpts.bundleEpoch = new Date(epoch).toISOString();
1321
+ }
1322
+
1269
1323
  // --ack: operator acknowledges the jurisdiction obligations surfaced by
1270
1324
  // govern. Captured in attestation; downstream tooling can check whether
1271
1325
  // consent was explicit vs. implicit. AGENTS.md says the AI should surface
@@ -1512,6 +1566,16 @@ Flags:
1512
1566
  publisher trust anchor — i.e. the operator's
1513
1567
  organisation, NOT the tooling vendor. Must be an
1514
1568
  http://… or https://… URL, ≤256 chars.
1569
+ --bundle-deterministic Emit byte-stable CSAF / OpenVEX / close envelope.
1570
+ Freezes tracking + timestamp fields to a single
1571
+ epoch, derives session_id from evidence hash when
1572
+ not supplied via --session-id, and sorts
1573
+ vulnerabilities[] / statements[] ascending.
1574
+ Off by default; opt-in for reproducible-build
1575
+ pipelines + diff-friendly attestation review.
1576
+ --bundle-epoch <ISO> Frozen epoch for --bundle-deterministic. ISO-8601
1577
+ calendar timestamp (date or date+time). Falls back
1578
+ to the playbook's last_threat_review when omitted.
1515
1579
  --diff-from-latest Compare evidence_hash against the most recent prior
1516
1580
  attestation for the same playbook in
1517
1581
  .exceptd/attestations/. Emits status: unchanged | drifted.
@@ -1589,6 +1653,8 @@ Flags:
1589
1653
  CSAF document.publisher.namespace (§3.1.7.4). The
1590
1654
  operator's organisation URL, NOT the tooling vendor.
1591
1655
  Must be an http://… or https://… URL, ≤256 chars.
1656
+ --bundle-deterministic Emit byte-stable bundles (frozen timestamps).
1657
+ --bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.
1592
1658
  --pretty Indented JSON output.
1593
1659
 
1594
1660
  Exit codes: 0 PASS, 1 framework, 4 blocked, 7 SESSION_ID_COLLISION,
@@ -1716,6 +1782,8 @@ Flags:
1716
1782
  CSAF document.publisher.namespace (§3.1.7.4). The
1717
1783
  operator's organisation URL, NOT the tooling vendor.
1718
1784
  Must be an http://… or https://… URL, ≤256 chars.
1785
+ --bundle-deterministic Emit byte-stable bundles for reproducible pipelines.
1786
+ --bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.
1719
1787
  --evidence <file|-> Single-shot mode: pre-supplied submission JSON.
1720
1788
  --operator <name> Bind the attestation to a specific identity.
1721
1789
  --ack Mark explicit operator consent (jurisdiction clock).
@@ -1794,6 +1862,8 @@ Flags:
1794
1862
  --publisher-namespace <url>
1795
1863
  CSAF document.publisher.namespace (§3.1.7.4). The
1796
1864
  operator's organisation URL, NOT the tooling vendor.
1865
+ --bundle-deterministic Emit byte-stable bundles across per-playbook runs.
1866
+ --bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.
1797
1867
  --json Force single-line JSON (overrides any TTY heuristics).
1798
1868
  --pretty Indented JSON output (implies --json).
1799
1869
 
@@ -1874,7 +1944,9 @@ Flags (selected — see \`exceptd run --help\` for the full list):
1874
1944
  --publisher-namespace <url>
1875
1945
  CSAF document.publisher.namespace (§3.1.7.4). The
1876
1946
  operator's organisation URL, NOT the tooling vendor.
1877
- Must be an http://… or https://… URL, ≤256 chars.`,
1947
+ Must be an http://… or https://… URL, ≤256 chars.
1948
+ --bundle-deterministic Emit byte-stable bundles across the multi-run set.
1949
+ --bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.`,
1878
1950
  };
1879
1951
  process.stdout.write((cmds[verb] || `${verb} — no per-verb help available; see \`exceptd help\` for the full list.`) + "\n");
1880
1952
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "schema_version": "1.1.0",
3
- "generated_at": "2026-05-15T22:17:17.497Z",
3
+ "generated_at": "2026-05-15T22:38:59.213Z",
4
4
  "generator": "scripts/build-indexes.js",
5
5
  "source_count": 51,
6
6
  "source_hashes": {
7
- "manifest.json": "492f8548ab8dc7d8edba1666d07c401d77e9459e356e3c30283888c73be6f005",
7
+ "manifest.json": "9862095ee05729021d2dd51765c370ed24377e73c536b237817ef7ce4a7ba437",
8
8
  "data/atlas-ttps.json": "db52a797f6ba7c9a61fd7b1225ebbc268ddf21abe29a106c4246c2ed2e617b86",
9
9
  "data/attack-techniques.json": "6b45448aa42cc6664376c93da73356624708e935c12589ee8c776a10215bce3a",
10
10
  "data/cve-catalog.json": "a2acad16f5e3856b07019fa00110e9dcb38ec5cc71b318d0e164bfcba7f4f644",
@@ -79,21 +79,25 @@ const VERB_FLAG_ALLOWLIST = Object.freeze({
79
79
  'mode', 'air-gap', 'force-stale', 'operator', 'ack', 'csaf-status',
80
80
  'publisher-namespace', 'vex', 'all', 'scope', 'required', 'format',
81
81
  'strict-preconditions', 'block-on-jurisdiction-clock', 'tlp',
82
+ 'bundle-deterministic', 'bundle-epoch',
82
83
  ],
83
84
  'run-all': [
84
85
  'evidence', 'evidence-dir', 'session-id', 'force-overwrite', 'attestation-root',
85
86
  'mode', 'air-gap', 'force-stale', 'operator', 'ack', 'csaf-status',
86
87
  'publisher-namespace', 'vex', 'scope', 'strict-preconditions', 'tlp',
88
+ 'bundle-deterministic', 'bundle-epoch',
87
89
  ],
88
90
  'ai-run': [
89
91
  'evidence', 'no-stream', 'session-id', 'force-overwrite', 'attestation-root',
90
92
  'operator', 'ack', 'csaf-status', 'publisher-namespace', 'air-gap',
91
93
  'mode', 'force-stale', 'tlp',
94
+ 'bundle-deterministic', 'bundle-epoch',
92
95
  ],
93
96
  ingest: [
94
97
  'evidence', 'session-id', 'force-overwrite', 'attestation-root', 'operator',
95
98
  'ack', 'csaf-status', 'publisher-namespace', 'air-gap', 'force-stale',
96
99
  'strict-preconditions',
100
+ 'bundle-deterministic', 'bundle-epoch',
97
101
  ],
98
102
  brief: ['all', 'scope', 'directives', 'flat', 'phase'],
99
103
  discover: ['scan-only', 'scope'],
@@ -1515,6 +1515,13 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1515
1515
  // bypass run() (e.g. unit tests).
1516
1516
  const sessionId = runOpts.session_id || crypto.randomBytes(8).toString('hex');
1517
1517
 
1518
+ // v0.12.27: when opt-in deterministic bundle mode is set, resolve the
1519
+ // single frozen epoch used by every timestamp surface below. Cached for
1520
+ // the whole close() call so notification_actions, regression_schedule,
1521
+ // and the bundle emitter all agree on the same Date.
1522
+ const deterministic = runOpts.bundleDeterministic === true;
1523
+ const frozenEpoch = deterministic ? resolveFrozenEpoch(runOpts, playbook) : null;
1524
+
1518
1525
  // notification_actions — compute ISO deadlines from clock_starts events.
1519
1526
  // v0.11.12 (#123): enrich each entry with the matched obligation's
1520
1527
  // jurisdiction/regulation/window_hours/evidence_required fields. The
@@ -1605,7 +1612,10 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1605
1612
  framework_id: playbook.domain.frameworks_in_scope[0] || 'unspecified',
1606
1613
  control_id: analyzeResult.framework_gap_mapping?.[0]?.claimed_control || 'unspecified',
1607
1614
  ciso_name: agentSignals.ciso_name || '<CISO NAME>',
1608
- acceptance_date: new Date().toISOString().slice(0, 10),
1615
+ // v0.12.27: deterministic mode roots acceptance_date in the
1616
+ // frozen epoch so two runs against the same evidence emit the
1617
+ // same auditor-facing date.
1618
+ acceptance_date: (deterministic ? frozenEpoch : new Date().toISOString()).slice(0, 10),
1609
1619
  duration_expiry: agentSignals.duration_expiry || 'until vendor patch'
1610
1620
  })
1611
1621
  };
@@ -1628,7 +1638,11 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1628
1638
  // spurious millisecond drift on tracking.initial_release_date /
1629
1639
  // timestamp / current_release_date.
1630
1640
  const evidencePackage = c.evidence_package ? (() => {
1631
- const issuedAt = new Date().toISOString();
1641
+ // v0.12.27: deterministic mode pins issuedAt to the frozen epoch so
1642
+ // CSAF tracking.{initial_release_date,current_release_date,
1643
+ // generator.date,revision_history[0].date} and OpenVEX timestamp +
1644
+ // statements[].timestamp all collapse to a single, byte-stable value.
1645
+ const issuedAt = deterministic ? frozenEpoch : new Date().toISOString();
1632
1646
  const builtFormats = new Map();
1633
1647
  const buildOnce = (format) => {
1634
1648
  if (!builtFormats.has(format)) {
@@ -1680,11 +1694,27 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1680
1694
  } : { enabled: false };
1681
1695
 
1682
1696
  // regression_schedule
1683
- const regressionSchedule = c.regression_schedule ? {
1684
- next_run: validateResult.regression_next_run,
1685
- trigger: c.regression_schedule.trigger,
1686
- notify_on_skip: c.regression_schedule.notify_on_skip !== false
1687
- } : null;
1697
+ //
1698
+ // v0.12.27: deterministic mode re-derives next_run from the frozen epoch
1699
+ // rather than wall-clock-now-at-validate-time. Without this, two runs
1700
+ // against the same evidence diverge on next_run by the interval between
1701
+ // the two `validate()` invocations. Frozen base + the same interval set
1702
+ // = byte-identical schedule.
1703
+ const regressionSchedule = c.regression_schedule ? (() => {
1704
+ let nextRun = validateResult.regression_next_run;
1705
+ if (deterministic) {
1706
+ // Re-derive against the validate phase's trigger set (not the
1707
+ // close phase's regression_schedule subtree — close has no triggers
1708
+ // of its own, just the canonical interval declared upstream).
1709
+ const v = resolvedPhase(playbook, directiveId, 'validate');
1710
+ nextRun = frozenRegressionNextRun(v.regression_trigger || [], new Date(frozenEpoch));
1711
+ }
1712
+ return {
1713
+ next_run: nextRun,
1714
+ trigger: c.regression_schedule.trigger,
1715
+ notify_on_skip: c.regression_schedule.notify_on_skip !== false
1716
+ };
1717
+ })() : null;
1688
1718
 
1689
1719
  // feeds_into chaining — full analyze result is exposed so conditions can
1690
1720
  // reference `analyze.compliance_theater_check.verdict` etc.
@@ -1996,6 +2026,40 @@ function getEngineVersion() {
1996
2026
  return _CACHED_PKG_VERSION;
1997
2027
  }
1998
2028
 
2029
+ // v0.12.27: deterministic-bundle epoch resolution. Priority:
2030
+ // 1. runOpts.bundleEpoch (operator-supplied --bundle-epoch <ISO>)
2031
+ // 2. playbook._meta.last_threat_review (the freshness anchor that already
2032
+ // gates every shipped playbook — stable across re-runs of the same
2033
+ // catalog version)
2034
+ // 3. '1970-01-01T00:00:00Z' fallback (effectively impossible in practice
2035
+ // because every shipped playbook carries last_threat_review, but
2036
+ // guarantees the deterministic path never crashes on a malformed
2037
+ // playbook).
2038
+ // Returns a full ISO-8601 timestamp (date-only inputs are normalised).
2039
+ function resolveFrozenEpoch(runOpts, playbook) {
2040
+ const raw = runOpts && runOpts.bundleEpoch
2041
+ ? runOpts.bundleEpoch
2042
+ : (playbook && playbook._meta && playbook._meta.last_threat_review)
2043
+ || '1970-01-01T00:00:00Z';
2044
+ try { return new Date(raw).toISOString(); }
2045
+ catch { return '1970-01-01T00:00:00Z'; }
2046
+ }
2047
+
2048
+ // Recompute regression_schedule.next_run against a frozen `now` so two
2049
+ // deterministic-mode runs of the same playbook produce byte-identical
2050
+ // schedules. Mirrors computeRegressionNextRun but with an injected base
2051
+ // date. Returns the soonest ISO timestamp or null when no interval-based
2052
+ // trigger fired.
2053
+ function frozenRegressionNextRun(triggers, frozenNow) {
2054
+ let soonest = null;
2055
+ for (const t of (triggers || [])) {
2056
+ const parsed = parseInterval(t.interval, frozenNow);
2057
+ if (!parsed || !parsed.date) continue;
2058
+ if (!soonest || parsed.date < soonest) soonest = parsed.date;
2059
+ }
2060
+ return soonest ? soonest.toISOString() : null;
2061
+ }
2062
+
1999
2063
  // Operator-supplied identity strings (--operator) and publisher namespace
2000
2064
  // URLs (--publisher-namespace) flow into operator-facing CSAF surfaces.
2001
2065
  // Strip ASCII control characters as defence in depth — bin/exceptd.js
@@ -2410,7 +2474,19 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2410
2474
  if (branches.length > 0) tree.branches = branches;
2411
2475
  return tree;
2412
2476
  })(),
2413
- vulnerabilities: [...cveVulns, ...indicatorVulns],
2477
+ vulnerabilities: (function () {
2478
+ // v0.12.27: deterministic mode sorts vulnerabilities[] by their
2479
+ // primary identifier (cve_id for CVE entries, ids[0].text otherwise)
2480
+ // ascending. Default mode preserves insertion order so existing
2481
+ // operators see byte-identical output to pre-v0.12.27.
2482
+ const all = [...cveVulns, ...indicatorVulns];
2483
+ if (runOpts && runOpts.bundleDeterministic === true) {
2484
+ const keyOf = (v) => (typeof v.cve === 'string' && v.cve)
2485
+ || (Array.isArray(v.ids) && v.ids[0] && typeof v.ids[0].text === 'string' ? v.ids[0].text : '');
2486
+ return all.slice().sort((a, b) => keyOf(a).localeCompare(keyOf(b)));
2487
+ }
2488
+ return all;
2489
+ })(),
2414
2490
  exceptd_extension: {
2415
2491
  classification: analyze._detect_classification,
2416
2492
  rwep: analyze.rwep,
@@ -2642,7 +2718,17 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2642
2718
  author: 'exceptd',
2643
2719
  timestamp: issued,
2644
2720
  version: 1,
2645
- statements: [...cveStatements, ...indicatorStatements],
2721
+ statements: (function () {
2722
+ // v0.12.27: deterministic mode sorts statements[] by
2723
+ // vulnerability['@id'] ascending. Insertion order otherwise.
2724
+ const all = [...cveStatements, ...indicatorStatements];
2725
+ if (runOpts && runOpts.bundleDeterministic === true) {
2726
+ const keyOf = (s) => (s && s.vulnerability && typeof s.vulnerability['@id'] === 'string')
2727
+ ? s.vulnerability['@id'] : '';
2728
+ return all.slice().sort((a, b) => keyOf(a).localeCompare(keyOf(b)));
2729
+ }
2730
+ return all;
2731
+ })(),
2646
2732
  };
2647
2733
  }
2648
2734
 
@@ -2948,7 +3034,28 @@ function run(playbookId, directiveId, agentSubmission = {}, runOpts = {}) {
2948
3034
  // Without the single-source-of-truth, close() would mint its own id
2949
3035
  // and operators correlating attestation files to embedded bundle URNs
2950
3036
  // would see mismatches.
2951
- const sessionId = runOpts.session_id || crypto.randomBytes(8).toString('hex');
3037
+ //
3038
+ // v0.12.27: when runOpts.bundleDeterministic is set AND the operator did
3039
+ // not pass --session-id, derive the session_id from the submission shape
3040
+ // so two runs against identical evidence produce the same id (and
3041
+ // therefore the same CSAF tracking.id / OpenVEX @id / attestation file
3042
+ // name). Mirrors the evidence_hash path further down but is computed
3043
+ // here so close() can thread it through. Operator-supplied --session-id
3044
+ // still wins on collision.
3045
+ let sessionId;
3046
+ if (runOpts.session_id) {
3047
+ sessionId = runOpts.session_id;
3048
+ } else if (runOpts.bundleDeterministic) {
3049
+ const submissionDigest = crypto.createHash('sha256')
3050
+ .update(canonicalStringify(extractSubmissionForHash(agentSubmission)))
3051
+ .digest('hex');
3052
+ sessionId = crypto.createHash('sha256')
3053
+ .update(`${playbookId}\0${submissionDigest}\0${getEngineVersion()}`)
3054
+ .digest('hex')
3055
+ .slice(0, 16);
3056
+ } else {
3057
+ sessionId = crypto.randomBytes(8).toString('hex');
3058
+ }
2952
3059
  const cachedRunOpts = { ...runOpts, _playbookCache: playbook, session_id: sessionId };
2953
3060
  // Run-time error accumulator for evalCondition regex failures and other
2954
3061
  // non-fatal anomalies surfaced into analyze.runtime_errors[].
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_comment": "Auto-generated by scripts/refresh-manifest-snapshot.js — do not hand-edit. Public skill surface used by check-manifest-snapshot.js to detect breaking removals.",
3
- "_generated_at": "2026-05-15T22:10:24.906Z",
3
+ "_generated_at": "2026-05-15T22:38:13.114Z",
4
4
  "atlas_version": "5.1.0",
5
5
  "skill_count": 39,
6
6
  "skills": [
@@ -1570,7 +1570,14 @@
1570
1570
  "uk tsa 2021",
1571
1571
  "volt typhoon"
1572
1572
  ],
1573
- "data_deps": [],
1573
+ "data_deps": [
1574
+ "atlas-ttps.json",
1575
+ "cve-catalog.json",
1576
+ "cwe-catalog.json",
1577
+ "d3fend-catalog.json",
1578
+ "framework-control-gaps.json",
1579
+ "global-frameworks.json"
1580
+ ],
1574
1581
  "atlas_refs": [
1575
1582
  "AML.T0040"
1576
1583
  ],
@@ -1 +1 @@
1
- 3b2e3c3a40554d760ee71eded57828b0dcd3237ed0c4499ee123606d041bf1dc manifest-snapshot.json
1
+ 259bbbc7ec375bfb21c2e8fe4c397cca265b33b357e19282d34acff932752237 manifest-snapshot.json
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceptd-security",
3
- "version": "0.12.26",
3
+ "version": "0.12.27",
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",
@@ -52,7 +52,7 @@
52
52
  ],
53
53
  "last_threat_review": "2026-05-01",
54
54
  "signature": "N6H4u/u1fCFE6f/3QVkAr2cumZvLNE+xYBC91CCxKoeaSKm5zqbwzb2mvFDk9XKUegUy5W6npLFGi75yxNMIAg==",
55
- "signed_at": "2026-05-15T22:15:26.972Z",
55
+ "signed_at": "2026-05-15T22:38:12.653Z",
56
56
  "cwe_refs": [
57
57
  "CWE-125",
58
58
  "CWE-362",
@@ -116,7 +116,7 @@
116
116
  ],
117
117
  "last_threat_review": "2026-05-01",
118
118
  "signature": "Xen6ojQGzT4AZUN/WtuQon+gT2UrJyX50nrZwEdxLw5aiz8gDaeMkWo/Bic+h4NFEF7MRd7uDTm0dvKgWnlRBA==",
119
- "signed_at": "2026-05-15T22:15:26.974Z",
119
+ "signed_at": "2026-05-15T22:38:12.655Z",
120
120
  "cwe_refs": [
121
121
  "CWE-1039",
122
122
  "CWE-1426",
@@ -179,7 +179,7 @@
179
179
  ],
180
180
  "last_threat_review": "2026-05-01",
181
181
  "signature": "IDhdamTvyWfnz7SvIMrVMz2cwLuiP2/Iw2iYHFNbI1O302XnrGyIVsJcoKZa5QFClBYPiABVt+yI5HEuLxMCBw==",
182
- "signed_at": "2026-05-15T22:15:26.975Z",
182
+ "signed_at": "2026-05-15T22:38:12.655Z",
183
183
  "cwe_refs": [
184
184
  "CWE-22",
185
185
  "CWE-345",
@@ -225,7 +225,7 @@
225
225
  "framework_gaps": [],
226
226
  "last_threat_review": "2026-05-01",
227
227
  "signature": "cPRRTsNQT1MYR3cE5O3KdC4MB037EMc0fsMIbOyfOv16sR+DkiXmAhQOjlIC47HngHz3vhLI+rbqItN91VWpBg==",
228
- "signed_at": "2026-05-15T22:15:26.975Z"
228
+ "signed_at": "2026-05-15T22:38:12.656Z"
229
229
  },
230
230
  {
231
231
  "name": "compliance-theater",
@@ -256,7 +256,7 @@
256
256
  ],
257
257
  "last_threat_review": "2026-05-01",
258
258
  "signature": "79NrFMRsqGsipWeE5ETQSVICGO4BjTJYgyir+PSaNVFpkLqLcwZd8Dr1V7iwX0H0fXFL3WpPz35gtrYCEG32BQ==",
259
- "signed_at": "2026-05-15T22:15:26.976Z"
259
+ "signed_at": "2026-05-15T22:38:12.656Z"
260
260
  },
261
261
  {
262
262
  "name": "exploit-scoring",
@@ -285,7 +285,7 @@
285
285
  ],
286
286
  "last_threat_review": "2026-05-01",
287
287
  "signature": "O7YIzAOQtSCFD0pyUdF0otYy9xwksrRGCLnSw5aMMGOs0SYeYA1JsMX5XLxNOQJC8tURC21HgQc/yx22jLtvAw==",
288
- "signed_at": "2026-05-15T22:15:26.976Z"
288
+ "signed_at": "2026-05-15T22:38:12.656Z"
289
289
  },
290
290
  {
291
291
  "name": "rag-pipeline-security",
@@ -322,7 +322,7 @@
322
322
  ],
323
323
  "last_threat_review": "2026-05-01",
324
324
  "signature": "ai6ebp9pz7dBigm2rQvQ0SklhDZtHqP3exKtolbEBiN0shQScypJfDBaQN2J3aoOC4dZjjTgIZvGfWLmBxrxBA==",
325
- "signed_at": "2026-05-15T22:15:26.976Z",
325
+ "signed_at": "2026-05-15T22:38:12.657Z",
326
326
  "cwe_refs": [
327
327
  "CWE-1395",
328
328
  "CWE-1426"
@@ -379,7 +379,7 @@
379
379
  ],
380
380
  "last_threat_review": "2026-05-01",
381
381
  "signature": "BkDjyCF53MAATVfzERmIhEhi474eWloxD0qyw9Gvw+VFE8aH3pOi+yeCpc0kq0vHAVmAEszwxBKEcuLkJbmSBg==",
382
- "signed_at": "2026-05-15T22:15:26.977Z",
382
+ "signed_at": "2026-05-15T22:38:12.657Z",
383
383
  "d3fend_refs": [
384
384
  "D3-CA",
385
385
  "D3-CSPP",
@@ -414,7 +414,7 @@
414
414
  "framework_gaps": [],
415
415
  "last_threat_review": "2026-05-01",
416
416
  "signature": "DxfXhSyoAGUo1emHh0uIIcg324ZreBYxmFdBDVAKOOuPmMlfN4RqNc/JGDSfVmMv5CjgYCUcSmkcYB0A5lk0Cg==",
417
- "signed_at": "2026-05-15T22:15:26.977Z",
417
+ "signed_at": "2026-05-15T22:38:12.657Z",
418
418
  "cwe_refs": [
419
419
  "CWE-1188"
420
420
  ]
@@ -442,7 +442,7 @@
442
442
  "framework_gaps": [],
443
443
  "last_threat_review": "2026-05-01",
444
444
  "signature": "rFQ82v+1oAHWixWcGwokKhjZHfXUf6N7EfSgldhQ5Jrbiy3kv5CIbnOCsI6zPWyErSnpKVeBFTabJXnzLrzDCQ==",
445
- "signed_at": "2026-05-15T22:15:26.978Z"
445
+ "signed_at": "2026-05-15T22:38:12.658Z"
446
446
  },
447
447
  {
448
448
  "name": "global-grc",
@@ -474,7 +474,7 @@
474
474
  "framework_gaps": [],
475
475
  "last_threat_review": "2026-05-01",
476
476
  "signature": "7yVjZkanFMKDQqXdX4B/7oLc2Rz72xHC1zscYd8F/+e5UAbR7ikK8Bn5EKZt3aBEOhHPAviSQNCMxpZD9U00CA==",
477
- "signed_at": "2026-05-15T22:15:26.978Z"
477
+ "signed_at": "2026-05-15T22:38:12.658Z"
478
478
  },
479
479
  {
480
480
  "name": "zeroday-gap-learn",
@@ -501,7 +501,7 @@
501
501
  "framework_gaps": [],
502
502
  "last_threat_review": "2026-05-01",
503
503
  "signature": "VlPR7yY39hEwDJYYKPgHAOeax9LU0X7eIrR8L7zMFJWS0SdKTalOXXJtD9GppByftnkYAAdryZ8tHQ/KWLtaBQ==",
504
- "signed_at": "2026-05-15T22:15:26.979Z"
504
+ "signed_at": "2026-05-15T22:38:12.659Z"
505
505
  },
506
506
  {
507
507
  "name": "pqc-first",
@@ -553,7 +553,7 @@
553
553
  ],
554
554
  "last_threat_review": "2026-05-01",
555
555
  "signature": "V+qn5FqUlETfsEjvvi6jZGuQdqLFtFejfgPA6KSYxSlBXBTbOBXP3BGk5S+ba9akIzgbKh1j9VGB1MqsIt56DA==",
556
- "signed_at": "2026-05-15T22:15:26.979Z",
556
+ "signed_at": "2026-05-15T22:38:12.659Z",
557
557
  "cwe_refs": [
558
558
  "CWE-327"
559
559
  ],
@@ -600,7 +600,7 @@
600
600
  ],
601
601
  "last_threat_review": "2026-05-01",
602
602
  "signature": "WCNz19186cER1eEhCophTIbnL3ltS3FC98I1rfv463aRnuVxPB3sUlD9xHTbxTM2rUABhqKijhdkWiMh2uKxCQ==",
603
- "signed_at": "2026-05-15T22:15:26.979Z"
603
+ "signed_at": "2026-05-15T22:38:12.659Z"
604
604
  },
605
605
  {
606
606
  "name": "security-maturity-tiers",
@@ -637,7 +637,7 @@
637
637
  ],
638
638
  "last_threat_review": "2026-05-01",
639
639
  "signature": "zjq6ACAHD46xvhvQJKlrCPh5xDCuBuIWBI+QJB8RxcudpC7p7I1pqv+BY8DZdsAgU4tquCU8KC+xlduMIk3/DQ==",
640
- "signed_at": "2026-05-15T22:15:26.980Z",
640
+ "signed_at": "2026-05-15T22:38:12.660Z",
641
641
  "cwe_refs": [
642
642
  "CWE-1188"
643
643
  ]
@@ -672,7 +672,7 @@
672
672
  "framework_gaps": [],
673
673
  "last_threat_review": "2026-05-11",
674
674
  "signature": "/lGgWehCMQUXjI6w4FUa+5wrbyRnct+txvVcXA+D2/ZEkoJKh+J/psO3j5HPf7Hpv+Y5SmkH71CoO+9qilyVDQ==",
675
- "signed_at": "2026-05-15T22:15:26.980Z"
675
+ "signed_at": "2026-05-15T22:38:12.660Z"
676
676
  },
677
677
  {
678
678
  "name": "attack-surface-pentest",
@@ -743,7 +743,7 @@
743
743
  "PTES revision incorporating AI-surface enumeration"
744
744
  ],
745
745
  "signature": "RqQMwOKK7xjG9e/Ls4986NOrDwKz/nQmpw1DwNJwV2nlOztyo7MgxUG3kTuLbuW3qCrrkO+CbpBA5nGS1cmKBQ==",
746
- "signed_at": "2026-05-15T22:15:26.980Z"
746
+ "signed_at": "2026-05-15T22:38:12.660Z"
747
747
  },
748
748
  {
749
749
  "name": "fuzz-testing-strategy",
@@ -803,7 +803,7 @@
803
803
  "OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
804
804
  ],
805
805
  "signature": "+ELdD+1AY5DymBitH7wU65CS60NY1nDoLowJAFn7cE5Gr/5jy9BTkyxsm7PEXaSlXWMOkTf/HQ+uyzyxUVD/Bw==",
806
- "signed_at": "2026-05-15T22:15:26.981Z"
806
+ "signed_at": "2026-05-15T22:38:12.661Z"
807
807
  },
808
808
  {
809
809
  "name": "dlp-gap-analysis",
@@ -878,7 +878,7 @@
878
878
  "Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
879
879
  ],
880
880
  "signature": "vL5XeQOk7vwX0sLMuKghj6XLnXsqKcGpCUNMui9HwqnWyNQwgSRGu+JFqP7ZqpP3SUYRZHcVlWhXeTJHyOtiAA==",
881
- "signed_at": "2026-05-15T22:15:26.981Z"
881
+ "signed_at": "2026-05-15T22:38:12.661Z"
882
882
  },
883
883
  {
884
884
  "name": "supply-chain-integrity",
@@ -955,7 +955,7 @@
955
955
  "OpenSSF model-signing — emerging Sigstore-based signing standard for ML model weights; track for production adoption"
956
956
  ],
957
957
  "signature": "jp3MxKukV7zW47eX3VcAIMG5WxypMDcfHqwYDodI9YQgTxEojCrRcMSApaoZHTdD3yTQC1JtkXeKxU6K3C5NCg==",
958
- "signed_at": "2026-05-15T22:15:26.981Z"
958
+ "signed_at": "2026-05-15T22:38:12.661Z"
959
959
  },
960
960
  {
961
961
  "name": "defensive-countermeasure-mapping",
@@ -1012,7 +1012,7 @@
1012
1012
  ],
1013
1013
  "last_threat_review": "2026-05-11",
1014
1014
  "signature": "XZigwq8X/csfrdG10O6Q1V5q0zUqSQGd3QrjRKkZ4fkaodG4mZahYuIQqxc8rU9jjtGAm9LtBXYB+I5csqj9Bw==",
1015
- "signed_at": "2026-05-15T22:15:26.982Z"
1015
+ "signed_at": "2026-05-15T22:38:12.662Z"
1016
1016
  },
1017
1017
  {
1018
1018
  "name": "identity-assurance",
@@ -1079,7 +1079,7 @@
1079
1079
  "d3fend_refs": [],
1080
1080
  "last_threat_review": "2026-05-11",
1081
1081
  "signature": "k0HrsZMBxiPWB1jl4dRwhv/R5IsqbZ+SLDv1Jx3/sRl51JyXjtm8vyogTNhSwsl5/IkaRakqIPJFRFRl5h/9CQ==",
1082
- "signed_at": "2026-05-15T22:15:26.982Z"
1082
+ "signed_at": "2026-05-15T22:38:12.662Z"
1083
1083
  },
1084
1084
  {
1085
1085
  "name": "ot-ics-security",
@@ -1135,7 +1135,7 @@
1135
1135
  "d3fend_refs": [],
1136
1136
  "last_threat_review": "2026-05-11",
1137
1137
  "signature": "oHxjumOhk8y86WcwhAX8sSWIlPzt60KfTMn4DCJLeRrrQd5+i54fVADKAdZ3vOqfDN+DexO0uX4f5dLPtacRCQ==",
1138
- "signed_at": "2026-05-15T22:15:26.982Z"
1138
+ "signed_at": "2026-05-15T22:38:12.662Z"
1139
1139
  },
1140
1140
  {
1141
1141
  "name": "coordinated-vuln-disclosure",
@@ -1187,7 +1187,7 @@
1187
1187
  "NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
1188
1188
  ],
1189
1189
  "signature": "UCiNjncvhkZItmLQA/Sm1/NCsOiLMwdCjfUw+067v4NIxhaMMaqRrAeD3KgMyEtov7m2Hq2kfwYSt5+DQsYDCQ==",
1190
- "signed_at": "2026-05-15T22:15:26.983Z"
1190
+ "signed_at": "2026-05-15T22:38:12.663Z"
1191
1191
  },
1192
1192
  {
1193
1193
  "name": "threat-modeling-methodology",
@@ -1237,7 +1237,7 @@
1237
1237
  "PASTA v2 updates incorporating AI/ML application threats"
1238
1238
  ],
1239
1239
  "signature": "V9kl8Cf8UMjNFyn3D/fSyhWHLeXWlx3WV/jT9jdF9SrjfDqymimuTt2o91cZ2FOEJndAH9V0JGXB13Ohz8K4CQ==",
1240
- "signed_at": "2026-05-15T22:15:26.983Z"
1240
+ "signed_at": "2026-05-15T22:38:12.663Z"
1241
1241
  },
1242
1242
  {
1243
1243
  "name": "webapp-security",
@@ -1311,7 +1311,7 @@
1311
1311
  "d3fend_refs": [],
1312
1312
  "last_threat_review": "2026-05-11",
1313
1313
  "signature": "ENSL4MJSNXhriKsTVBjg2jTc7JTtb6mxqbfBw/SVVajPMkLMcLBk4Gem9LhZWZ8DSqyWLnFO2d6hlz5q8bjuCg==",
1314
- "signed_at": "2026-05-15T22:15:26.983Z"
1314
+ "signed_at": "2026-05-15T22:38:12.663Z"
1315
1315
  },
1316
1316
  {
1317
1317
  "name": "ai-risk-management",
@@ -1361,7 +1361,7 @@
1361
1361
  "d3fend_refs": [],
1362
1362
  "last_threat_review": "2026-05-11",
1363
1363
  "signature": "8E82UwKFNraXV/MKAbiUV6gUryYuN+Ff/kiv1aW4/XtriShdTyt/UgRuQJ8LXGXl0jMH8hRJ/xTAV8LOJqexDA==",
1364
- "signed_at": "2026-05-15T22:15:26.984Z"
1364
+ "signed_at": "2026-05-15T22:38:12.663Z"
1365
1365
  },
1366
1366
  {
1367
1367
  "name": "sector-healthcare",
@@ -1421,7 +1421,7 @@
1421
1421
  "d3fend_refs": [],
1422
1422
  "last_threat_review": "2026-05-11",
1423
1423
  "signature": "BDuLcpTeFp2BNSf1q4rYOhYKNhlgd3o5RZ0Uw9xW5olyYxPbZSgqekQ+6Ggaec09s7y6sqR37GS0vuAMdbrdDQ==",
1424
- "signed_at": "2026-05-15T22:15:26.984Z"
1424
+ "signed_at": "2026-05-15T22:38:12.664Z"
1425
1425
  },
1426
1426
  {
1427
1427
  "name": "sector-financial",
@@ -1502,7 +1502,7 @@
1502
1502
  "TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
1503
1503
  ],
1504
1504
  "signature": "w12QqGBlRDaDVYug9uQVmEbxR7+gX23rOKZSjlt3XcszYDHBCRiP4cBRKMuEguu44DCaQsg+Btu4vAVMlss9Dg==",
1505
- "signed_at": "2026-05-15T22:15:26.984Z"
1505
+ "signed_at": "2026-05-15T22:38:12.664Z"
1506
1506
  },
1507
1507
  {
1508
1508
  "name": "sector-federal-government",
@@ -1571,7 +1571,7 @@
1571
1571
  "Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
1572
1572
  ],
1573
1573
  "signature": "nMsyJ+rp5fM8/VjC7zsZyDjOC4hpxB+noT1VX7W0HBlq5t3SY56cwOGApwES/kBcCuf4qexKY376OxUr93zvCQ==",
1574
- "signed_at": "2026-05-15T22:15:26.985Z"
1574
+ "signed_at": "2026-05-15T22:38:12.665Z"
1575
1575
  },
1576
1576
  {
1577
1577
  "name": "sector-energy",
@@ -1636,7 +1636,7 @@
1636
1636
  "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"
1637
1637
  ],
1638
1638
  "signature": "L1moEqEGkBkqY/3ohJcfqrlJn40UurDCyb2MOP/IwTAeZD+QbVZ17/drdsydkJ6qSXPiyiE6u8HDfZsDS13NBQ==",
1639
- "signed_at": "2026-05-15T22:15:26.985Z"
1639
+ "signed_at": "2026-05-15T22:38:12.665Z"
1640
1640
  },
1641
1641
  {
1642
1642
  "name": "sector-telecom",
@@ -1722,7 +1722,7 @@
1722
1722
  "O-RAN SFG / WG11 security specifications"
1723
1723
  ],
1724
1724
  "signature": "VKLuoRkFq7lNXqySipwzPSaiHaqemHQ2cReemF/Xy9hUpD9orQaTVZWClOA4lzoF6d2eQ/CeS///Jjnj4g9dCg==",
1725
- "signed_at": "2026-05-15T22:15:26.986Z"
1725
+ "signed_at": "2026-05-15T22:38:12.666Z"
1726
1726
  },
1727
1727
  {
1728
1728
  "name": "api-security",
@@ -1791,7 +1791,7 @@
1791
1791
  "d3fend_refs": [],
1792
1792
  "last_threat_review": "2026-05-11",
1793
1793
  "signature": "JHGu5OI35payaFR1At3XZIX4HnflgF3lI9vk/XsHpu0loWHtbTiA/SrNzTuWO+be8aIfd36uNz7WnJNwBTCHDA==",
1794
- "signed_at": "2026-05-15T22:15:26.986Z"
1794
+ "signed_at": "2026-05-15T22:38:12.666Z"
1795
1795
  },
1796
1796
  {
1797
1797
  "name": "cloud-security",
@@ -1872,7 +1872,7 @@
1872
1872
  "CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
1873
1873
  ],
1874
1874
  "signature": "UEn0305KAEqIfYOdzadLBdPG/PJ+3sJ/8ubvPFNcXfqXp2uOWTfqGUqY65PApA992VEEa1RBQt5R7Nyhd/OjDQ==",
1875
- "signed_at": "2026-05-15T22:15:26.986Z"
1875
+ "signed_at": "2026-05-15T22:38:12.666Z"
1876
1876
  },
1877
1877
  {
1878
1878
  "name": "container-runtime-security",
@@ -1934,7 +1934,7 @@
1934
1934
  "d3fend_refs": [],
1935
1935
  "last_threat_review": "2026-05-11",
1936
1936
  "signature": "lPd9tHAskNapjrWwFWhsb8ntAL0xovDCIGElsOCyjcafzby4ArwRw5Lq28sfNloJZAhMN+AWj+lDdFytiUQHCQ==",
1937
- "signed_at": "2026-05-15T22:15:26.987Z"
1937
+ "signed_at": "2026-05-15T22:38:12.667Z"
1938
1938
  },
1939
1939
  {
1940
1940
  "name": "mlops-security",
@@ -2005,7 +2005,7 @@
2005
2005
  "MITRE ATLAS v5.2 — track AML.T0010 sub-technique expansion and any new MLOps-pipeline-specific TTPs"
2006
2006
  ],
2007
2007
  "signature": "U+HyElcP007FIblXUE/nFpj/rZ5z3VohsvxRCWEuuJDLdOnsXYEadb7ccr3X7S4aRG2MC4T2KtVtgbKIuO5QDw==",
2008
- "signed_at": "2026-05-15T22:15:26.987Z"
2008
+ "signed_at": "2026-05-15T22:38:12.667Z"
2009
2009
  },
2010
2010
  {
2011
2011
  "name": "incident-response-playbook",
@@ -2067,7 +2067,7 @@
2067
2067
  "NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
2068
2068
  ],
2069
2069
  "signature": "XB3TVjNRBlqqbIhatFoYtTJHTS51nVt9k7DVrb2roUflLDjbCnaTbrpztA2oqJyyxwgnLlX+K7NW8oYOYEMeCg==",
2070
- "signed_at": "2026-05-15T22:15:26.987Z"
2070
+ "signed_at": "2026-05-15T22:38:12.667Z"
2071
2071
  },
2072
2072
  {
2073
2073
  "name": "email-security-anti-phishing",
@@ -2120,7 +2120,7 @@
2120
2120
  "d3fend_refs": [],
2121
2121
  "last_threat_review": "2026-05-11",
2122
2122
  "signature": "RiCryJEd66T2NNcSo/mZTd3sGWDycE3C37guLJanLdVL5co35DrPFmIl8qy3ZM/y+Wzg5vpny8VKgr1//1/bCA==",
2123
- "signed_at": "2026-05-15T22:15:26.988Z"
2123
+ "signed_at": "2026-05-15T22:38:12.668Z"
2124
2124
  },
2125
2125
  {
2126
2126
  "name": "age-gates-child-safety",
@@ -2188,11 +2188,11 @@
2188
2188
  "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"
2189
2189
  ],
2190
2190
  "signature": "MMWvg3lIf5ygm31zyf1E43t3W9MfRbMBBPrqlj1wOa8AxVJL8LICnAXfmyJ/TNJXwpF+rfZeDdoxXkql8wmtBA==",
2191
- "signed_at": "2026-05-15T22:15:26.988Z"
2191
+ "signed_at": "2026-05-15T22:38:12.668Z"
2192
2192
  }
2193
2193
  ],
2194
2194
  "manifest_signature": {
2195
2195
  "algorithm": "Ed25519",
2196
- "signature_base64": "G344ApDWCadCdjzlTgTMnX/DbpyebRxP+TCeSpUVupPmXHX6EL9Sq61YmAbliIzvktl8xK5JerqbGAXXFOPaDA=="
2196
+ "signature_base64": "hRjCIjBncoCecBmhExyEZhUaTyUAe0s3pbLg1Oj7eHaSsdEfynFJ2RvW+LqpEPokeS6HgaU9ORyC4LMNBiWeAQ=="
2197
2197
  }
2198
2198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.12.26",
3
+ "version": "0.12.27",
4
4
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
5
5
  "keywords": [
6
6
  "ai-security",
package/sbom.cdx.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:b073ce4f-f31d-4836-a0fb-181ae601a5cf",
4
+ "serialNumber": "urn:uuid:c26c612d-400e-4270-9716-299e76224a35",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-05-15T22:10:26.119Z",
7
+ "timestamp": "2026-05-15T22:38:14.241Z",
8
8
  "tools": [
9
9
  {
10
10
  "name": "hand-written",
@@ -13,10 +13,10 @@
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.12.26",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.12.27",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.12.26",
19
+ "version": "0.12.27",
20
20
  "description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
21
21
  "licenses": [
22
22
  {
@@ -25,11 +25,11 @@
25
25
  }
26
26
  }
27
27
  ],
28
- "purl": "pkg:npm/%40blamejs/exceptd-skills@0.12.26",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.12.27",
29
29
  "externalReferences": [
30
30
  {
31
31
  "type": "distribution",
32
- "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.12.26"
32
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.12.27"
33
33
  },
34
34
  {
35
35
  "type": "vcs",