@blamejs/exceptd-skills 0.18.14 → 0.18.16

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,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.16 — 2026-06-22
4
+
5
+ A correctness pass on the evidence-output and attestation surfaces.
6
+
7
+ SARIF finding results always carry a location now. A result with no `locations` is silently dropped by GitHub Code Scanning; previously only playbooks whose look-artifact sources happened to be bare file paths got one, so most playbooks' (prose/glob/command sources) findings were emitted location-less and disappeared from the alerts list. Each finding without a concrete file now carries a rule-scoped logical location, keeping it visible and attributable; a physical file location is still used whenever the agent supplied one.
8
+
9
+ `attest diff` no longer reports identical evidence as changed. The artifact diff was keyed by the operator-chosen observation key rather than the indicator the observation targeted, so two attestations with the same finding under different observation keys (a different collector run, the array vs object evidence shape, reordered observations) showed every artifact as added-and-removed with zero unchanged. The diff is now keyed by the stable indicator id, so identical evidence compares as unchanged.
10
+
11
+ Risk-acceptance exception language now reports its unresolved placeholders. The auditor-ready exception text is filled from operator-supplied values that a standard run does not pre-stage, so it could ship literal `<MISSING:…>` tokens; the exception now carries a `missing_interpolation_vars` list (and surfaces a runtime error) naming exactly which values still need filling, matching the notification-draft behavior.
12
+
13
+ CSAF advisories bind their per-version affected products. The product tree's per-version product entries were defined but never referenced from any vulnerability, so a dashboard correlating affected products to the tree saw only the generic synthetic product; each CVE's version entries are now bound into its `product_status`.
14
+
15
+ ## 0.18.15 — 2026-06-22
16
+
17
+ Indicator-gated escalations and playbook chains now fire when their indicators are detected. An escalation or `feeds_into` written as `<indicator-id> == true` (the catalog's standard form) resolved only when that id was also passed as a raw signal; the normal scan path delivers detections separately, so on a real `exceptd collect <playbook>` these escalations and downstream chains stayed silent even when the indicator was found. Fired indicators are now bound into the escalation, chain, and remediation-precondition contexts, so a detection drives the condition that gates on it. An operator can still override a specific indicator, and engine-computed values still take precedence.
18
+
19
+ Six more compound escalations now fire. Each combined a working clause (`rwep >= 90`, `blast_radius_score >= 4`, a theater verdict) with a second clause written in prose or shorthand the engine cannot evaluate; because the working clause came first and evaluation stopped at the first clause that resolved, the dead second clause went unnoticed. The compound conditions are now fully expressed in the condition language and gate on the playbook's real indicators.
20
+
21
+ The validator's condition gate now checks every sub-clause of an AND/OR condition independently, so a dead clause hidden behind a clause that happens to resolve first can no longer ship.
22
+
3
23
  ## 0.18.14 — 2026-06-22
4
24
 
5
25
  Severity escalations, downstream-playbook chaining, and remediation-path preconditions now fire as authored across the playbook catalog. A number of these conditions had been written as free-text English or in a shorthand the engine does not evaluate, so they returned false for every input and silently never triggered — a raise-severity rule, a `feeds_into` chain into a deeper playbook, or a remediation precondition that looked active but never fired. Every such condition is now expressed in the engine's condition language and verified to evaluate against the indicators it gates on.
package/bin/exceptd.js CHANGED
@@ -6554,6 +6554,39 @@ function submissionHasData(submission) {
6554
6554
  || nonEmpty(submission.signal_overrides)
6555
6555
  || nonEmpty(submission.observations);
6556
6556
  }
6557
+ // attest diff compares artifacts by map key. normalizeSubmission keys
6558
+ // flat-shape artifacts by the operator-chosen OBSERVATION key (obs-kver, x1, an
6559
+ // array index, …), NOT by the stable indicator id the observation targeted — so
6560
+ // two attestations with identical (indicator, value) evidence under different
6561
+ // observation keys diffed as all-added/all-removed (unchanged_count 0), while the
6562
+ // correctly-keyed signal_override_diff on the same comparison showed the true
6563
+ // unchanged result. Re-key the artifact map by the stable indicator id (recovered
6564
+ // from normalizeSubmission's _signal_origins, which maps indicator -> obs-key)
6565
+ // before diffing, falling back to the original key when an observation carries no
6566
+ // indicator binding. Diff-only — does NOT touch normalizeSubmission's map or the
6567
+ // evidence_hash.
6568
+ function rekeyArtifactsByStableId(artifacts, signalOrigins) {
6569
+ if (!artifacts || !signalOrigins || typeof signalOrigins !== "object") return artifacts || {};
6570
+ const obsKeyToIndicator = {};
6571
+ for (const [indicatorId, obsKey] of Object.entries(signalOrigins)) {
6572
+ if (typeof obsKey === "string") obsKeyToIndicator[obsKey] = indicatorId;
6573
+ }
6574
+ const originalKeys = new Set(Object.keys(artifacts));
6575
+ const out = {};
6576
+ for (const [k, v] of Object.entries(artifacts)) {
6577
+ const mapped = obsKeyToIndicator[k];
6578
+ // Re-key to the stable indicator id ONLY when it won't drop an artifact: the
6579
+ // indicator id must not already be a DISTINCT original artifact key, and must
6580
+ // not have been claimed by an earlier re-keyed entry. On any such collision,
6581
+ // keep the original key so no artifact is silently overwritten / lost from
6582
+ // the diff (two observations sharing one indicator, or an artifact key that
6583
+ // already equals an indicator id).
6584
+ const stable = (mapped && mapped !== k && !originalKeys.has(mapped)
6585
+ && !Object.prototype.hasOwnProperty.call(out, mapped)) ? mapped : k;
6586
+ out[stable] = v;
6587
+ }
6588
+ return out;
6589
+ }
6557
6590
  function normalizedArtifacts(submission, runner, playbookId, applyEmptyFallback = true) {
6558
6591
  if (!submission || typeof submission !== "object") {
6559
6592
  return applyEmptyFallback ? (_playbookArtifactCatalog(runner, playbookId) || {}) : {};
@@ -6568,7 +6601,9 @@ function normalizedArtifacts(submission, runner, playbookId, applyEmptyFallback
6568
6601
  const pb = runner.loadPlaybook ? runner.loadPlaybook(playbookId) : null;
6569
6602
  if (pb) {
6570
6603
  const norm = runner.normalizeSubmission({ observations: submission.observations }, pb);
6571
- if (norm && norm.artifacts && Object.keys(norm.artifacts).length > 0) return norm.artifacts;
6604
+ if (norm && norm.artifacts && Object.keys(norm.artifacts).length > 0) {
6605
+ return rekeyArtifactsByStableId(norm.artifacts, norm._signal_origins);
6606
+ }
6572
6607
  }
6573
6608
  } catch { /* fall through */ }
6574
6609
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "schema_version": "1.1.0",
3
- "generated_at": "2026-06-22T14:16:42.035Z",
3
+ "generated_at": "2026-06-22T17:11:04.498Z",
4
4
  "generator": "scripts/build-indexes.js",
5
5
  "source_count": 64,
6
6
  "source_hashes": {
7
- "manifest.json": "be5406117ede866ec4dc653cf15f9555f8fa58c254fc601800e4bacb07ddb609",
7
+ "manifest.json": "c8376cbda42a9657b6a5d50f75d8c08a0bcab7c8456340042be7998133922ecb",
8
8
  "README.md": "e7b854e7db9a364a1b368b5084b4f0c2a8282f0459ce39800ac1d1dabdc06074",
9
9
  "data/atlas-ttps.json": "5bc59e23d6c2defa54168de161a0825299b9cc4a49c6b26df2dae70b4f42eedf",
10
10
  "data/attack-techniques.json": "53c6f248760eecb11a0354f74ab467a5814e95075a686b9b3bf18c34e2f7435e",
@@ -533,11 +533,11 @@
533
533
  ],
534
534
  "escalation_criteria": [
535
535
  {
536
- "condition": "blast_radius_score >= 3 AND any deterministic indicator fires",
536
+ "condition": "blast_radius_score >= 3 AND fabricated-cve-id == true",
537
537
  "action": "raise_severity"
538
538
  },
539
539
  {
540
- "condition": "compliance_theater_check.verdict == 'theater' AND citation appears in published disclosure surface",
540
+ "condition": "compliance_theater_check.verdict == 'theater' AND (fabricated-cve-id == true OR rejected-or-disputed-cve == true)",
541
541
  "action": "trigger_playbook",
542
542
  "target_playbook": "framework"
543
543
  },
@@ -930,11 +930,11 @@
930
930
  ],
931
931
  "escalation_criteria": [
932
932
  {
933
- "condition": "blast_radius_score >= 4 AND any deterministic indicator fires",
933
+ "condition": "blast_radius_score >= 4 AND (weak-hash-import == true OR weak-cipher-mode == true OR rsa-1024-anywhere == true OR math-random-in-security-path == true OR pbkdf2-under-iterated == true OR bcrypt-cost-low == true OR no-ml-kem-implementation == true OR fips-claim-without-runtime-activation == true OR hardcoded-key-material == true OR tls-old-protocol == true)",
934
934
  "action": "raise_severity"
935
935
  },
936
936
  {
937
- "condition": "rwep >= 75 AND library is published_to_registry == true",
937
+ "condition": "rwep >= 75 AND blast_radius_score >= 4",
938
938
  "action": "page_on_call"
939
939
  },
940
940
  {
@@ -877,7 +877,7 @@
877
877
  ],
878
878
  "escalation_criteria": [
879
879
  {
880
- "condition": "rwep >= 90 AND any deterministic indicator fires",
880
+ "condition": "rwep >= 90 AND anomalous-federated-trust-addition == true",
881
881
  "action": "page_on_call"
882
882
  },
883
883
  {
@@ -847,7 +847,7 @@
847
847
  "action": "page_on_call"
848
848
  },
849
849
  {
850
- "condition": "mcp-typosquat-candidate == true AND no benign_match",
850
+ "condition": "mcp-typosquat-candidate == true",
851
851
  "action": "raise_severity"
852
852
  },
853
853
  {
@@ -953,6 +953,31 @@ function detect(playbookId, directiveId, agentSubmission = {}, runOpts = {}) {
953
953
 
954
954
  // --- phase 5: analyze ---
955
955
 
956
+ /**
957
+ * Mirror the FIRED detect indicators (verdict === 'hit') into a flat
958
+ * `{ <indicator-id>: true }` map for the escalation / feeds_into / precondition
959
+ * eval contexts. Indicator hits arrive from the collector / AI evidence path as
960
+ * `signal_overrides` (which detect() reads); the condition contexts spread
961
+ * `agentSubmission.signals`, NOT signal_overrides — so an escalation written as
962
+ * `<indicator-id> == true` (the catalog's canonical form) stayed false even when
963
+ * that indicator DETECTED, unless the operator redundantly re-submitted the id
964
+ * under `signals`. The standard `exceptd collect <playbook>` path never does
965
+ * that, so the entire indicator-gated escalation/chain layer was dead on the
966
+ * real evidence path. Mirroring fired indicators here binds the condition layer
967
+ * to the detect layer. Only verdict 'hit' (FP-checks satisfied) is mirrored — an
968
+ * 'inconclusive' indicator is unconfirmed and must not fire an escalation. The
969
+ * map is spread with the LOWEST precedence (before agentSignals, which is before
970
+ * the engine roots) so an operator can still override a specific id and the
971
+ * engine-computed values always win.
972
+ */
973
+ function firedIndicatorSignals(detectIndicators) {
974
+ const out = {};
975
+ for (const ind of (detectIndicators || [])) {
976
+ if (ind && ind.verdict === 'hit' && typeof ind.id === 'string') out[ind.id] = true;
977
+ }
978
+ return out;
979
+ }
980
+
956
981
  /**
957
982
  * RWEP composition + blast-radius scoring + theater check + framework gap
958
983
  * mapping + escalation evaluation. Inputs are the detect result + any
@@ -1490,7 +1515,7 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}, runOp
1490
1515
  findingShape = {};
1491
1516
  }
1492
1517
  for (const ec of an.escalation_criteria || []) {
1493
- if (evalCondition(ec.condition, { ...agentSignals, ...evalCtxRoot, rwep: adjustedRwep, blast_radius_score: blastRadiusScore, theater_verdict: theaterVerdict, compliance_theater_check: result.compliance_theater_check, jurisdiction_obligations: (playbook.phases && playbook.phases.govern && playbook.phases.govern.jurisdiction_obligations) || [], analyze: result, matched_cve: result.matched_cves || [],
1518
+ if (evalCondition(ec.condition, { ...firedIndicatorSignals(detectResult.indicators), ...agentSignals, ...evalCtxRoot, rwep: adjustedRwep, blast_radius_score: blastRadiusScore, theater_verdict: theaterVerdict, compliance_theater_check: result.compliance_theater_check, jurisdiction_obligations: (playbook.phases && playbook.phases.govern && playbook.phases.govern.jurisdiction_obligations) || [], analyze: result, matched_cve: result.matched_cves || [],
1494
1519
  // finding.* is two-sourced: the engine computes the CVE/severity-derived
1495
1520
  // keys (severity, rwep_adjusted, matched_cve_*, blast_radius_score,
1496
1521
  // active_exploitation, framework/control_id_first) via analyzeFindingShape,
@@ -1596,6 +1621,10 @@ function validate(playbookId, directiveId, analyzeResult, agentSignals = {}, run
1596
1621
  let findingShape = {};
1597
1622
  try { findingShape = analyzeFindingShape(analyzeResult || {}); } catch { findingShape = {}; }
1598
1623
  const evalCtx = {
1624
+ // Fired detect indicators mirrored in (lowest precedence) so a precondition
1625
+ // referencing an indicator id resolves on the collector path. See
1626
+ // firedIndicatorSignals.
1627
+ ...firedIndicatorSignals(analyzeResult && analyzeResult._detect_indicators),
1599
1628
  ...agentSignals,
1600
1629
  rwep: analyzeResult && analyzeResult.rwep ? analyzeResult.rwep.adjusted : undefined,
1601
1630
  blast_radius_score: analyzeResult ? analyzeResult.blast_radius_score : undefined,
@@ -1965,14 +1994,26 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1965
1994
  const triggered = evalCondition(c.exception_generation.trigger_condition, closeEvalCtx, playbook);
1966
1995
  if (triggered) {
1967
1996
  const t = c.exception_generation.exception_template;
1997
+ // Track unresolved ${placeholder} tokens the SAME way the draft_notification
1998
+ // render does (missing_interpolation_vars). Exception-template tokens are
1999
+ // operator-fill values (affected_host_count, compensating_controls,
2000
+ // patch_available_status, …) that analyzeFindingShape does NOT supply and
2001
+ // that operators rarely pre-stage on the standard `exceptd run` path, so the
2002
+ // auditor-ready risk-acceptance language otherwise ships literal
2003
+ // `<MISSING:…>` tokens with no machine-readable signal of which placeholders
2004
+ // failed. Surface them as exception.missing_interpolation_vars (empty when
2005
+ // all resolved) so a `ci`/JSON consumer — and the operator filling the
2006
+ // exception — sees exactly what still needs a value.
2007
+ const exMissing = [];
2008
+ const findingShape = (() => { try { return analyzeFindingShape(analyzeResult); } catch { return {}; } })();
1968
2009
  exception = {
1969
- scope: interpolate(t.scope, { ...agentSignals, ...analyzeFindingShape(analyzeResult) }),
2010
+ scope: interpolate(t.scope, { ...agentSignals, ...findingShape }, exMissing),
1970
2011
  duration: t.duration,
1971
2012
  compensating_controls: t.compensating_controls,
1972
2013
  risk_acceptance_owner: t.risk_acceptance_owner,
1973
2014
  auditor_ready_language: interpolate(t.auditor_ready_language, {
1974
2015
  ...agentSignals,
1975
- ...analyzeFindingShape(analyzeResult),
2016
+ ...findingShape,
1976
2017
  framework_id: playbook.domain.frameworks_in_scope[0] || 'unspecified',
1977
2018
  control_id: analyzeResult.framework_gap_mapping?.[0]?.claimed_control || 'unspecified',
1978
2019
  ciso_name: agentSignals.ciso_name || '<CISO NAME>',
@@ -1981,8 +2022,16 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
1981
2022
  // same auditor-facing date.
1982
2023
  acceptance_date: (deterministic ? frozenEpoch : new Date().toISOString()).slice(0, 10),
1983
2024
  duration_expiry: agentSignals.duration_expiry || 'until vendor patch'
1984
- })
2025
+ }, exMissing),
2026
+ missing_interpolation_vars: exMissing,
1985
2027
  };
2028
+ // Make the unresolved-placeholder gap observable to JSON/ci consumers, not
2029
+ // just present on the exception object.
2030
+ if (exMissing.length && Array.isArray(runOpts._runErrors)) {
2031
+ pushRunError(runOpts._runErrors,
2032
+ { kind: 'exception_unresolved_placeholders', placeholders: exMissing.slice() },
2033
+ { dedupeKey: e => (e.placeholders || []).join(',') });
2034
+ }
1986
2035
  }
1987
2036
  }
1988
2037
 
@@ -2083,6 +2132,11 @@ function close(playbookId, directiveId, analyzeResult, validateResult, agentSign
2083
2132
  // feeds_into chaining — full analyze result is exposed so conditions can
2084
2133
  // reference `analyze.compliance_theater_check.verdict` etc.
2085
2134
  const feedsCtx = {
2135
+ // Fired detect indicators (verdict 'hit') mirrored in so a feeds_into
2136
+ // condition written as `<indicator-id> == true` resolves on the standard
2137
+ // collector path (signal_overrides), not only when the id is re-submitted
2138
+ // under signals. Lowest precedence — operator signals and engine values win.
2139
+ ...firedIndicatorSignals(analyzeResult && analyzeResult._detect_indicators),
2086
2140
  // Operator-submitted signals come FIRST so the engine-computed reserved
2087
2141
  // keys below always win — otherwise a submitted signals.rwep / finding /
2088
2142
  // analyze would override the engine value the feeds_into condition tests
@@ -2296,12 +2350,23 @@ function vulnIdToUrn(id) {
2296
2350
  function buildCsafBranches(matchedCves, runOpts) {
2297
2351
  // Build a (vendor → product → Set<version>) map.
2298
2352
  const tree = new Map();
2299
- const addLeaf = (vendor, product, version) => {
2353
+ // Track which CVE contributed each leaf so the emitted CSAFPID-N product_ids
2354
+ // can be bound back into that CVE's vulnerabilities[].product_status — without
2355
+ // this the branch tree's per-version products are referenced by nothing and a
2356
+ // CSAF consumer correlating product_status to the tree resolves only the
2357
+ // generic synthetic product. leafKey = vendor\0product\0version.
2358
+ const leafKey = (vendor, product, version) => JSON.stringify([vendor, product, version]);
2359
+ const cveLeaves = new Map(); // cve_id → Set<leafKey>
2360
+ const addLeaf = (vendor, product, version, cveId) => {
2300
2361
  if (!vendor || !product || !version) return;
2301
2362
  if (!tree.has(vendor)) tree.set(vendor, new Map());
2302
2363
  const products = tree.get(vendor);
2303
2364
  if (!products.has(product)) products.set(product, new Set());
2304
2365
  products.get(product).add(version);
2366
+ if (cveId) {
2367
+ if (!cveLeaves.has(cveId)) cveLeaves.set(cveId, new Set());
2368
+ cveLeaves.get(cveId).add(leafKey(vendor, product, version));
2369
+ }
2305
2370
  };
2306
2371
 
2307
2372
  // Comparison / range operators that appear between a package name and a
@@ -2352,7 +2417,7 @@ function buildCsafBranches(matchedCves, runOpts) {
2352
2417
  if (Array.isArray(c.affected_products) && c.affected_products.length > 0) {
2353
2418
  for (const ap of c.affected_products) {
2354
2419
  if (ap && typeof ap === 'object' && ap.vendor && ap.product && ap.version) {
2355
- addLeaf(String(ap.vendor), String(ap.product), String(ap.version));
2420
+ addLeaf(String(ap.vendor), String(ap.product), String(ap.version), c.cve_id);
2356
2421
  }
2357
2422
  }
2358
2423
  continue;
@@ -2362,7 +2427,7 @@ function buildCsafBranches(matchedCves, runOpts) {
2362
2427
  for (const comp of components) {
2363
2428
  const parsed = parseComponentString(comp);
2364
2429
  if (parsed) {
2365
- addLeaf(parsed.vendor, parsed.product, parsed.version);
2430
+ addLeaf(parsed.vendor, parsed.product, parsed.version, c.cve_id);
2366
2431
  } else if (typeof comp === 'string' && comp.trim() && runOpts && Array.isArray(runOpts._runErrors)) {
2367
2432
  pushRunError(runOpts._runErrors, {
2368
2433
  kind: 'csaf_branch_unparseable',
@@ -2375,6 +2440,7 @@ function buildCsafBranches(matchedCves, runOpts) {
2375
2440
 
2376
2441
  // Sort + emit.
2377
2442
  const productIds = [];
2443
+ const leafKeyToPid = new Map();
2378
2444
  let pidCounter = 0;
2379
2445
  const vendors = Array.from(tree.keys()).sort();
2380
2446
  const branches = vendors.map(vendor => {
@@ -2391,6 +2457,7 @@ function buildCsafBranches(matchedCves, runOpts) {
2391
2457
  branches: versions.map(version => {
2392
2458
  const pid = `CSAFPID-${pidCounter++}`;
2393
2459
  productIds.push({ vendor, product, version, product_id: pid });
2460
+ leafKeyToPid.set(leafKey(vendor, product, version), pid);
2394
2461
  return {
2395
2462
  category: 'product_version',
2396
2463
  name: version,
@@ -2404,7 +2471,15 @@ function buildCsafBranches(matchedCves, runOpts) {
2404
2471
  }),
2405
2472
  };
2406
2473
  });
2407
- return { branches, productIds };
2474
+ // Per-CVE CSAFPID binding so the caller can add the version leaves to each
2475
+ // vulnerability's product_status.known_affected.
2476
+ const cveProductIds = {};
2477
+ for (const [cveId, keys] of cveLeaves.entries()) {
2478
+ const pids = [];
2479
+ for (const k of keys) { const pid = leafKeyToPid.get(k); if (pid) pids.push(pid); }
2480
+ if (pids.length) cveProductIds[cveId] = pids.sort();
2481
+ }
2482
+ return { branches, productIds, cveProductIds };
2408
2483
  }
2409
2484
 
2410
2485
  // Slugify a string into a URN-safe segment (RFC 8141 NSS). Empty input →
@@ -2511,6 +2586,24 @@ function sarifLocationsForIndicator(playbook, indicator) {
2511
2586
  return [{ physicalLocation: { artifactLocation: { uri: candidates[0] } } }];
2512
2587
  }
2513
2588
 
2589
+ // Locations for a finding-class SARIF result, with a guaranteed non-empty
2590
+ // fallback. sarifLocationsForIndicator returns a PHYSICAL location only when the
2591
+ // agent supplied evidence_locations or the playbook's look-artifact source is a
2592
+ // bare path token; the dominant catalog shape is a prose / glob / shell-command
2593
+ // source (which carries whitespace and is rejected by looksLikePath), so the
2594
+ // physical fallback is null for ~most playbooks. A SARIF result with no
2595
+ // `locations[]` is silently DROPPED by GitHub Code Scanning — so a result with
2596
+ // no concrete file still gets a `logicalLocations` entry naming its rule, which
2597
+ // is SARIF-conformant (§3.33), keeps the finding attributable + visible in the
2598
+ // alerts list and in SARIF viewers, and is honest (there is no physical file to
2599
+ // point at when the agent supplied no evidence location). Physical location is
2600
+ // always preferred when available.
2601
+ function sarifResultLocations(playbook, indicator, fqRuleId) {
2602
+ const phys = sarifLocationsForIndicator(playbook, indicator);
2603
+ if (phys && phys.length) return phys;
2604
+ return [{ logicalLocations: [{ name: fqRuleId, fullyQualifiedName: fqRuleId, kind: 'rule' }] }];
2605
+ }
2606
+
2514
2607
  // Resolve the package version once per process so CSAF tracking.generator
2515
2608
  // can name the engine that emitted the advisory. Best-effort read — bundle
2516
2609
  // emission must not crash if package.json is missing (e.g. exotic install).
@@ -2749,6 +2842,11 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2749
2842
  };
2750
2843
  const CSAF_CVE_RE = /^CVE-\d{4}-\d{4,}$/;
2751
2844
 
2845
+ // Build the product-tree branches up front so each CVE's per-version CSAFPID
2846
+ // leaves can be bound into its product_status.known_affected (and the same
2847
+ // tree reused for product_tree below — buildCsafBranches is not re-run).
2848
+ const csafProductTree = buildCsafBranches(analyze.matched_cves || [], runOpts);
2849
+
2752
2850
  const cveVulns = analyze.matched_cves.map(c => {
2753
2851
  const isFixed = c.vex_status === 'fixed';
2754
2852
  const remediations = [{
@@ -2833,7 +2931,17 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
2833
2931
  scores,
2834
2932
  threats: c.active_exploitation === 'confirmed' ? [{ category: 'exploit_status', details: `Active exploitation confirmed${c.cisa_kev ? ' (CISA KEV)' : ''}.` }] : [],
2835
2933
  remediations,
2836
- product_status: isFixed ? { fixed: [productId] } : { known_affected: [productId] }
2934
+ // Bind this CVE's per-version CSAFPID leaves (from the product tree) into
2935
+ // known_affected, so a CSAF consumer correlating product_status back to
2936
+ // the branches tree resolves the real affected versions, not only the
2937
+ // opaque synthetic product. The leaves are parsed from affected_products
2938
+ // / affected_versions — VULNERABLE version ranges — so they belong ONLY
2939
+ // under known_affected. The VEX-fixed disposition keeps just the synthetic
2940
+ // target product under `fixed`; adding affected ranges there would
2941
+ // mislabel vulnerable versions as fixed releases.
2942
+ product_status: isFixed
2943
+ ? { fixed: [productId] }
2944
+ : { known_affected: [productId, ...(csafProductTree.cveProductIds[c.cve_id] || [])] }
2837
2945
  };
2838
2946
  // route by id shape.
2839
2947
  if (idIsCve) {
@@ -3020,7 +3128,7 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
3020
3128
  // from catalog data. CSAF §3.1.5.1 makes branches[] strongly
3021
3129
  // recommended because NVD / ENISA / Red Hat dashboards render the
3022
3130
  // affected-product list off the branches tree, not full_product_names[].
3023
- const { branches } = buildCsafBranches(analyze.matched_cves || [], runOpts);
3131
+ const branches = csafProductTree.branches;
3024
3132
  const tree = { full_product_names: fullProductNames };
3025
3133
  if (branches.length > 0) tree.branches = branches;
3026
3134
  return tree;
@@ -3075,7 +3183,6 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
3075
3183
  // (passing a null indicator skips the per-indicator evidence-locations
3076
3184
  // branch). Without any `locations`, GitHub Code Scanning silently DROPS
3077
3185
  // these results — the highest-severity result class would never surface.
3078
- const cveFallbackLocs = sarifLocationsForIndicator(playbook, null);
3079
3186
  const cveResults = analyze.matched_cves.map(c => {
3080
3187
  const result = {
3081
3188
  ruleId: `${rulePrefix}${c.cve_id}`,
@@ -3091,12 +3198,15 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
3091
3198
  blast_radius_score: analyze.blast_radius_score,
3092
3199
  }),
3093
3200
  };
3094
- if (cveFallbackLocs) result.locations = cveFallbackLocs;
3201
+ // Always carry a location (physical when known, else a rule-scoped
3202
+ // logicalLocations fallback) so GitHub Code Scanning does not drop the
3203
+ // highest-severity result class.
3204
+ result.locations = sarifResultLocations(playbook, null, result.ruleId);
3095
3205
  return result;
3096
3206
  });
3097
3207
  const indicatorHits = (analyze._detect_indicators || []).filter(i => i.verdict === 'hit');
3098
3208
  const indicatorResults = indicatorHits.map(i => {
3099
- const locs = sarifLocationsForIndicator(playbook, i);
3209
+ const locs = sarifResultLocations(playbook, i, `${rulePrefix}${i.id}`);
3100
3210
  const result = {
3101
3211
  ruleId: `${rulePrefix}${i.id}`,
3102
3212
  level: i.deterministic ? 'error' : (i.confidence === 'high' ? 'warning' : 'note'),
@@ -3109,7 +3219,7 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals,
3109
3219
  attack_ref: i.attack_ref,
3110
3220
  }),
3111
3221
  };
3112
- if (locs) result.locations = locs;
3222
+ result.locations = locs; // always present (physical or logical fallback)
3113
3223
  return result;
3114
3224
  });
3115
3225
  const gapResults = (analyze.framework_gap_mapping || []).map((g, idx) => ({
@@ -95,6 +95,55 @@ const { safeExit } = require('./exit-codes');
95
95
  let _evalCondition = null;
96
96
  try { ({ _evalCondition } = require('./playbook-runner')); } catch { /* warned at the gate */ }
97
97
 
98
+ // Decompose a condition into its leaf atoms the SAME way evalCondition does —
99
+ // strip wrapping parens, split on top-level OR (lowest precedence), then on
100
+ // top-level AND, quote- and depth-aware. This exists because evalCondition
101
+ // evaluates AND via `.every` / OR via `.some`, which SHORT-CIRCUIT: a dead
102
+ // (unparseable) sub-clause behind a leading clause that resolves false (AND) or
103
+ // true (OR) in the empty validation context is never reached, so running the
104
+ // whole condition once never surfaces its condition_unparsed. Atomizing and
105
+ // parse-checking each leaf independently catches a dead sub-clause regardless of
106
+ // short-circuit (e.g. `blast_radius_score >= 3 AND any deterministic indicator
107
+ // fires` — the first clause is false with no signals, so the dead prose second
108
+ // clause never parsed). Mirrors evalCondition's splitAtTopLevel / stripOuterParens.
109
+ function _splitTopLevel(expr, sep) {
110
+ const parts = []; const needle = ' ' + sep + ' ';
111
+ let depth = 0, buf = '', i = 0, quote = null;
112
+ while (i < expr.length) {
113
+ const ch = expr[i];
114
+ if (quote) { if (ch === '\\' && i + 1 < expr.length) { buf += ch + expr[i + 1]; i += 2; continue; } if (ch === quote) quote = null; buf += ch; i++; continue; }
115
+ if (ch === "'" || ch === '"') { quote = ch; buf += ch; i++; continue; }
116
+ if (ch === '(') { depth++; buf += ch; i++; continue; }
117
+ if (ch === ')') { depth--; buf += ch; i++; continue; }
118
+ if (depth === 0 && expr.startsWith(needle, i)) { parts.push(buf.trim()); buf = ''; i += needle.length; continue; }
119
+ buf += ch; i++;
120
+ }
121
+ parts.push(buf.trim());
122
+ return parts;
123
+ }
124
+ function _stripWrappingParens(e) {
125
+ e = e.trim();
126
+ while (e.startsWith('(') && e.endsWith(')')) {
127
+ let d = 0, wraps = true;
128
+ for (let i = 0; i < e.length; i++) {
129
+ if (e[i] === '(') d++;
130
+ else if (e[i] === ')') { d--; if (d === 0 && i < e.length - 1) { wraps = false; break; } }
131
+ }
132
+ if (wraps) e = e.slice(1, -1).trim(); else break;
133
+ }
134
+ return e;
135
+ }
136
+ function atomizeCondition(cond) {
137
+ const atoms = [];
138
+ (function rec(e) {
139
+ e = _stripWrappingParens(e);
140
+ const ors = _splitTopLevel(e, 'OR'); if (ors.length > 1) { ors.forEach(rec); return; }
141
+ const ands = _splitTopLevel(e, 'AND'); if (ands.length > 1) { ands.forEach(rec); return; }
142
+ atoms.push(e);
143
+ })(cond);
144
+ return atoms;
145
+ }
146
+
98
147
  const REPO_ROOT = path.resolve(__dirname, '..');
99
148
  const SCHEMA_PATH = path.join(REPO_ROOT, 'lib', 'schemas', 'playbook.schema.json');
100
149
  const PLAYBOOKS_DIR = path.join(REPO_ROOT, 'data', 'playbooks');
@@ -597,10 +646,16 @@ function checkCrossRefs(playbook, ctx, playbookIds) {
597
646
  const conditionParses = (cond) => {
598
647
  if (typeof cond !== 'string' || !cond.trim()) return true;
599
648
  if (!_evalCondition) return true; // engine unavailable — warned above, never a silent error-skip
600
- const runErrors = [];
601
- try { _evalCondition(cond, { _runErrors: runErrors }, { _runErrors: runErrors }); }
602
- catch { return true; } // an evaluator throw is a separate concern, not "unparseable"
603
- return !runErrors.some((e) => e && e.kind === 'condition_unparsed');
649
+ // Check EACH atomic leaf, not the whole condition once: evalCondition's
650
+ // AND/OR short-circuit hides a dead sub-clause behind a leading clause that
651
+ // resolves false (AND) / true (OR) in the empty validation context.
652
+ for (const atom of atomizeCondition(cond)) {
653
+ const runErrors = [];
654
+ try { _evalCondition(atom, { _runErrors: runErrors }, { _runErrors: runErrors }); }
655
+ catch { continue; } // an evaluator throw is a separate concern, not "unparseable"
656
+ if (runErrors.some((e) => e && e.kind === 'condition_unparsed')) return false;
657
+ }
658
+ return true;
604
659
  };
605
660
  for (const [i, ec] of (((phases.analyze || {}).escalation_criteria) || []).entries()) {
606
661
  if (ec && typeof ec === 'object' && !conditionParses(ec.condition)) {
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceptd-security",
3
- "version": "0.18.14",
3
+ "version": "0.18.16",
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",
@@ -57,7 +57,7 @@
57
57
  ],
58
58
  "last_threat_review": "2026-05-15",
59
59
  "signature": "0H+JfyUVmo/pVFEi5rLENATHjlukPVUqnOWmNPEH77wm8svKGK0aNJ46k6QU5GdHb8c9X9pVJKiuhON6AxDjDw==",
60
- "signed_at": "2026-06-22T14:16:29.795Z",
60
+ "signed_at": "2026-06-22T17:11:02.747Z",
61
61
  "cwe_refs": [
62
62
  "CWE-125",
63
63
  "CWE-362",
@@ -133,7 +133,7 @@
133
133
  ],
134
134
  "last_threat_review": "2026-06-10",
135
135
  "signature": "WfqxU2x/feWgatYrCeO9PD3dIY0FAfs4nkVdQRac4fg5BDQPG/vhzhvYVN6Mg11oITACghbPOtZXvS37sACsCA==",
136
- "signed_at": "2026-06-22T14:16:29.797Z",
136
+ "signed_at": "2026-06-22T17:11:02.749Z",
137
137
  "cwe_refs": [
138
138
  "CWE-1039",
139
139
  "CWE-1426",
@@ -215,7 +215,7 @@
215
215
  ],
216
216
  "last_threat_review": "2026-06-10",
217
217
  "signature": "9bNOW4MzmUlFgkXH5tIZkq8YpzhMM+r8YVcNEM39kdEZwfRNG4Bj+Dgr7v4Qgp9D4q7hGNFpK0ywX+dPd3VWAg==",
218
- "signed_at": "2026-06-22T14:16:29.798Z",
218
+ "signed_at": "2026-06-22T17:11:02.751Z",
219
219
  "cwe_refs": [
220
220
  "CWE-22",
221
221
  "CWE-345",
@@ -269,7 +269,7 @@
269
269
  "framework_gaps": [],
270
270
  "last_threat_review": "2026-06-10",
271
271
  "signature": "5A08+nAu5jwNQ1+vGbFgFAJA/kYD3oiaevhCxT4/2FeW++F070Pm4MD1+oXdv8R2DbtYr+7yXnCGM7UbUPR2AA==",
272
- "signed_at": "2026-06-22T14:16:29.798Z"
272
+ "signed_at": "2026-06-22T17:11:02.752Z"
273
273
  },
274
274
  {
275
275
  "name": "compliance-theater",
@@ -301,7 +301,7 @@
301
301
  ],
302
302
  "last_threat_review": "2026-06-10",
303
303
  "signature": "RCgbI0sMNTKAscXUtrVU25u7OuE64uQvO5JKsB8vIJFWguvT6nZyD7euKuIxIzYhLTyHBiD7Rhp70btlK+JkCg==",
304
- "signed_at": "2026-06-22T14:16:29.799Z"
304
+ "signed_at": "2026-06-22T17:11:02.753Z"
305
305
  },
306
306
  {
307
307
  "name": "exploit-scoring",
@@ -332,7 +332,7 @@
332
332
  ],
333
333
  "last_threat_review": "2026-06-10",
334
334
  "signature": "JJrs+B22/hYxvdYs+abmoire5PbQXPb2uVYyjInZ3UbT8gEjs/5SyfGKx7z86jbtvB3ItApQVAx1AuseIgdyAQ==",
335
- "signed_at": "2026-06-22T14:16:29.799Z"
335
+ "signed_at": "2026-06-22T17:11:02.754Z"
336
336
  },
337
337
  {
338
338
  "name": "rag-pipeline-security",
@@ -374,7 +374,7 @@
374
374
  ],
375
375
  "last_threat_review": "2026-06-10",
376
376
  "signature": "Z6WbTqG98bQVRd6+kXDb6xIEe0ZryAeGxVSuKKxdK1PoZqew7NJcMsa6sJVSq+0r7KMsFwfTEdKorp6UDdhpCA==",
377
- "signed_at": "2026-06-22T14:16:29.799Z",
377
+ "signed_at": "2026-06-22T17:11:02.755Z",
378
378
  "cwe_refs": [
379
379
  "CWE-1395",
380
380
  "CWE-1426"
@@ -441,7 +441,7 @@
441
441
  ],
442
442
  "last_threat_review": "2026-06-10",
443
443
  "signature": "DLKcrgon+MZrqmivlFLBH4vlzZbJSW1531RsYDm9/KdW2CgcUUHwNMSzAfU1mAsFwFkAHjy/a8e1FbS9msTvDA==",
444
- "signed_at": "2026-06-22T14:16:29.800Z",
444
+ "signed_at": "2026-06-22T17:11:02.756Z",
445
445
  "cwe_refs": [
446
446
  "CWE-918"
447
447
  ],
@@ -482,7 +482,7 @@
482
482
  "framework_gaps": [],
483
483
  "last_threat_review": "2026-06-10",
484
484
  "signature": "eWCOD+mR1c6B8vKDBot8y8UkQrKPCIEBHU3JRQET9xZnYGx8hrOyeRUElcKqCO5aG/2LISs9Atg1H+ehWDNbCw==",
485
- "signed_at": "2026-06-22T14:16:29.800Z",
485
+ "signed_at": "2026-06-22T17:11:02.757Z",
486
486
  "cwe_refs": [
487
487
  "CWE-1188"
488
488
  ],
@@ -517,7 +517,7 @@
517
517
  "framework_gaps": [],
518
518
  "last_threat_review": "2026-06-10",
519
519
  "signature": "kZca725/+BFr1mpxL29+cGzjqsiYhChWScf7VjU6vCTBZrmCps6W9gYYjf4lVORXaM4DAg3UVBOXyhpTst0rAw==",
520
- "signed_at": "2026-06-22T14:16:29.801Z",
520
+ "signed_at": "2026-06-22T17:11:02.758Z",
521
521
  "forward_watch": [
522
522
  "New AI attack classes as ATLAS v6 publishes",
523
523
  "Post-quantum adversary capability timeline",
@@ -557,7 +557,7 @@
557
557
  "framework_gaps": [],
558
558
  "last_threat_review": "2026-05-01",
559
559
  "signature": "b5miTiY0cnxETd2btxorfZBdJKt/fLnQx20sGYUb9zEqGqtm0LMLpghkW68j4/9k48KNyuGMtNWiKTSnodUGBw==",
560
- "signed_at": "2026-06-22T14:16:29.801Z"
560
+ "signed_at": "2026-06-22T17:11:02.759Z"
561
561
  },
562
562
  {
563
563
  "name": "zeroday-gap-learn",
@@ -584,7 +584,7 @@
584
584
  "framework_gaps": [],
585
585
  "last_threat_review": "2026-06-10",
586
586
  "signature": "NVBY4I5a+v2/nUeCYcy3LLVKk3Tg9gmz+sCKyvnf8t2fXMNSIGtoy+P8K4Gfos1rGzRRMaU01V7L4kh/IQ8UBQ==",
587
- "signed_at": "2026-06-22T14:16:29.801Z",
587
+ "signed_at": "2026-06-22T17:11:02.760Z",
588
588
  "forward_watch": [
589
589
  "New CISA KEV entries",
590
590
  "New ATLAS TTP additions in each ATLAS release",
@@ -652,7 +652,7 @@
652
652
  ],
653
653
  "last_threat_review": "2026-06-10",
654
654
  "signature": "U54OxIjPT+W9yafjpeFgY7klXMltLYBuSCtu/AAlXxOIPehJrbf/vbGgnBn58nqOd7RjoKjmKz9WQs4DMqWcDw==",
655
- "signed_at": "2026-06-22T14:16:29.802Z",
655
+ "signed_at": "2026-06-22T17:11:02.762Z",
656
656
  "cwe_refs": [
657
657
  "CWE-327"
658
658
  ],
@@ -702,7 +702,7 @@
702
702
  ],
703
703
  "last_threat_review": "2026-06-10",
704
704
  "signature": "QsJMwvnUJXHwoBPJ4BiUx3FO9giXfos/sMbx5tyO83AJ2JxdVskeC6g/WDfO+rVkWNXRgWi4h1eSYz6T8/tpAg==",
705
- "signed_at": "2026-06-22T14:16:29.803Z"
705
+ "signed_at": "2026-06-22T17:11:02.763Z"
706
706
  },
707
707
  {
708
708
  "name": "security-maturity-tiers",
@@ -740,7 +740,7 @@
740
740
  ],
741
741
  "last_threat_review": "2026-05-01",
742
742
  "signature": "8KbLVVPI1QgYa+HAnEYvKf1cXnxQ3zsncqTGrAOrBP4rwUeZsxJm8KyFCwI0XytnZG35PZ830BFlG/okPW3sBQ==",
743
- "signed_at": "2026-06-22T14:16:29.803Z",
743
+ "signed_at": "2026-06-22T17:11:02.764Z",
744
744
  "cwe_refs": [
745
745
  "CWE-1188"
746
746
  ]
@@ -776,7 +776,7 @@
776
776
  "framework_gaps": [],
777
777
  "last_threat_review": "2026-05-11",
778
778
  "signature": "iJWevUBurLvt2v8X+Ch2eHmZkPWpKeAtIpxTIP4MwbUHyco3igDeBywJCyaR2vURYRx8LkzzIMM8DxQM4LAXBQ==",
779
- "signed_at": "2026-06-22T14:16:29.803Z"
779
+ "signed_at": "2026-06-22T17:11:02.766Z"
780
780
  },
781
781
  {
782
782
  "name": "attack-surface-pentest",
@@ -852,7 +852,7 @@
852
852
  "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"
853
853
  ],
854
854
  "signature": "ZnFi+Rk4CWrjFEVU4hC6Q66UbDmndNL/ICLWQNA1TQYFGSTnng+nboM4tfi6h0yMbbDj7dOXYqqeHNK+M5ZiBA==",
855
- "signed_at": "2026-06-22T14:16:29.804Z"
855
+ "signed_at": "2026-06-22T17:11:02.767Z"
856
856
  },
857
857
  {
858
858
  "name": "fuzz-testing-strategy",
@@ -913,7 +913,7 @@
913
913
  "OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
914
914
  ],
915
915
  "signature": "45ICz7vcnmHYE1+RIDv8wQjScKj6p8hqJQ5m6I0tDdKVnGdMXCYIgZnYqFJgHBDXYAXlGx7D7UxhFz6WOCDqAQ==",
916
- "signed_at": "2026-06-22T14:16:29.804Z"
916
+ "signed_at": "2026-06-22T17:11:02.768Z"
917
917
  },
918
918
  {
919
919
  "name": "dlp-gap-analysis",
@@ -991,7 +991,7 @@
991
991
  "Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
992
992
  ],
993
993
  "signature": "k6A7QcFEzgGXYWftt/elhQe7kU4F67c0ol3a8DZfj1XW4UrH99SLhPHr6ih3R3EYCl/Ey/zXt/x3a9fDT3RmAw==",
994
- "signed_at": "2026-06-22T14:16:29.804Z"
994
+ "signed_at": "2026-06-22T17:11:02.770Z"
995
995
  },
996
996
  {
997
997
  "name": "supply-chain-integrity",
@@ -1075,7 +1075,7 @@
1075
1075
  "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"
1076
1076
  ],
1077
1077
  "signature": "5shKqNcLC8Obt0EdvKnck8OwWIuRMgZuuZd12pvWqJIltsYMyTrw2M+lgbQfqg3x2KO6qsJljGAhTew5jM58DQ==",
1078
- "signed_at": "2026-06-22T14:16:29.805Z"
1078
+ "signed_at": "2026-06-22T17:11:02.771Z"
1079
1079
  },
1080
1080
  {
1081
1081
  "name": "defensive-countermeasure-mapping",
@@ -1132,7 +1132,7 @@
1132
1132
  ],
1133
1133
  "last_threat_review": "2026-06-10",
1134
1134
  "signature": "lfa5Wgep26mK3o2ZSe5qN2vVxg+xRRURRpmnqP4JdQvvecW3T2+PxHqjT48DegXJHRXJ6DPzyIElFc7no86TAg==",
1135
- "signed_at": "2026-06-22T14:16:29.805Z"
1135
+ "signed_at": "2026-06-22T17:11:02.772Z"
1136
1136
  },
1137
1137
  {
1138
1138
  "name": "identity-assurance",
@@ -1206,7 +1206,7 @@
1206
1206
  ],
1207
1207
  "last_threat_review": "2026-05-11",
1208
1208
  "signature": "UV3458QXSkEpenzrOmdlTTfPHUD4hNyKMDHoeZDq/kiFb4mAG0ghQGTTgI9Ru8cJbSmYM1++m9N5TFIJ6JJPBg==",
1209
- "signed_at": "2026-06-22T14:16:29.805Z"
1209
+ "signed_at": "2026-06-22T17:11:02.773Z"
1210
1210
  },
1211
1211
  {
1212
1212
  "name": "ot-ics-security",
@@ -1265,7 +1265,7 @@
1265
1265
  "d3fend_refs": [],
1266
1266
  "last_threat_review": "2026-06-10",
1267
1267
  "signature": "afJ00EDXgARQJrbRqN6xe1+E/kbHF7O2fxQFVvrLLXdrKfK86RK/oJU1AwpxX6mclX9f+5ivy6FvFIuRMA4dBQ==",
1268
- "signed_at": "2026-06-22T14:16:29.806Z"
1268
+ "signed_at": "2026-06-22T17:11:02.774Z"
1269
1269
  },
1270
1270
  {
1271
1271
  "name": "coordinated-vuln-disclosure",
@@ -1325,7 +1325,7 @@
1325
1325
  "NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
1326
1326
  ],
1327
1327
  "signature": "VzivMW82mRrG9+07ZX8sj4j9O9YjE/m07m1UHgSwEgex+FLaVPvIj3y1Vk6qeFOvDURmtVaztWlpSlk7iSn0DA==",
1328
- "signed_at": "2026-06-22T14:16:29.806Z"
1328
+ "signed_at": "2026-06-22T17:11:02.776Z"
1329
1329
  },
1330
1330
  {
1331
1331
  "name": "threat-modeling-methodology",
@@ -1379,7 +1379,7 @@
1379
1379
  "PASTA v2 updates incorporating AI/ML application threats"
1380
1380
  ],
1381
1381
  "signature": "Fk5LzzeS/bH5SQJNySgZqSdeQf1udwdRs3FVLwig5kuqyyKkuIJcF4HW+xHF/1e66HR+gjgRUoqWZGsv58lLAg==",
1382
- "signed_at": "2026-06-22T14:16:29.807Z"
1382
+ "signed_at": "2026-06-22T17:11:02.778Z"
1383
1383
  },
1384
1384
  {
1385
1385
  "name": "webapp-security",
@@ -1462,7 +1462,7 @@
1462
1462
  ],
1463
1463
  "last_threat_review": "2026-06-10",
1464
1464
  "signature": "1skHJsPPOIXLQdU5MGHirP/Q07aCYTirtDmfWDO7ZNuto58NlCttW5ZjCpyh37vASlMljIZx0/R+PIl2PfwTBw==",
1465
- "signed_at": "2026-06-22T14:16:29.807Z",
1465
+ "signed_at": "2026-06-22T17:11:02.778Z",
1466
1466
  "forward_watch": [
1467
1467
  "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"
1468
1468
  ]
@@ -1522,7 +1522,7 @@
1522
1522
  ],
1523
1523
  "last_threat_review": "2026-05-15",
1524
1524
  "signature": "SBB7c3wNYfIdkyOp4g4nW0WP7xS+YokMzg32aaeJdbf14LTGQRzQUvSqb2TCj2HFUSHESOyKT1JpkAfyHLSQBQ==",
1525
- "signed_at": "2026-06-22T14:16:29.808Z"
1525
+ "signed_at": "2026-06-22T17:11:02.780Z"
1526
1526
  },
1527
1527
  {
1528
1528
  "name": "sector-healthcare",
@@ -1589,7 +1589,7 @@
1589
1589
  ],
1590
1590
  "last_threat_review": "2026-06-10",
1591
1591
  "signature": "yyAGG3nUC6/XWQGRF2ufzILfiJitMFczqWNY83lZShCgYSxfFCul0UsICClOc7Xjw76052uydm8kLH9gIzhsAg==",
1592
- "signed_at": "2026-06-22T14:16:29.808Z"
1592
+ "signed_at": "2026-06-22T17:11:02.781Z"
1593
1593
  },
1594
1594
  {
1595
1595
  "name": "sector-financial",
@@ -1678,7 +1678,7 @@
1678
1678
  "TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
1679
1679
  ],
1680
1680
  "signature": "/+df91Qhl1TIxEAqzgqB7+YJ6mneX6Dif+h6RaCbxOLGE6KRTFst1Q/Fu5SStt8Zx5JI62bkQ2BBrNOKfUlPBg==",
1681
- "signed_at": "2026-06-22T14:16:29.808Z"
1681
+ "signed_at": "2026-06-22T17:11:02.782Z"
1682
1682
  },
1683
1683
  {
1684
1684
  "name": "sector-federal-government",
@@ -1755,7 +1755,7 @@
1755
1755
  "Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
1756
1756
  ],
1757
1757
  "signature": "k6MAPS8HJivu0DNwC1azPvyKeig2xLGz/hcqYd8T+s5GvIypPGfkeYRRKmdqMgHUe24f7bQeRZ7AxcQ9Bi6SCg==",
1758
- "signed_at": "2026-06-22T14:16:29.809Z"
1758
+ "signed_at": "2026-06-22T17:11:02.783Z"
1759
1759
  },
1760
1760
  {
1761
1761
  "name": "sector-energy",
@@ -1829,7 +1829,7 @@
1829
1829
  "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"
1830
1830
  ],
1831
1831
  "signature": "IYKXn009jfhpKdQKiGB4xb7VgxsJXa6PZawoguP42khU6S/TTemp/YsK1YQCUyabE+2biElL/PP1Hi8zCkSSBw==",
1832
- "signed_at": "2026-06-22T14:16:29.809Z"
1832
+ "signed_at": "2026-06-22T17:11:02.785Z"
1833
1833
  },
1834
1834
  {
1835
1835
  "name": "sector-telecom",
@@ -1915,7 +1915,7 @@
1915
1915
  "O-RAN SFG / WG11 security specifications"
1916
1916
  ],
1917
1917
  "signature": "NAtyzfLPXlUuB78Snb9nWmbZalC1CNlIYN9rYhdEmtB/xQGC6vVnThgrEAHlm7v/jMCFuknvEpUHKdscUnUADw==",
1918
- "signed_at": "2026-06-22T14:16:29.810Z"
1918
+ "signed_at": "2026-06-22T17:11:02.786Z"
1919
1919
  },
1920
1920
  {
1921
1921
  "name": "api-security",
@@ -1994,7 +1994,7 @@
1994
1994
  ],
1995
1995
  "last_threat_review": "2026-06-10",
1996
1996
  "signature": "HUjhPGpZeZeVi1nzBIsEAYh18x5ltlIXnt1yQ5YKz+jC2Dvxw7vV/Clx1/1gDBLAuBvRW9wcNtDr4c0RxhfGDQ==",
1997
- "signed_at": "2026-06-22T14:16:29.810Z",
1997
+ "signed_at": "2026-06-22T17:11:02.789Z",
1998
1998
  "forward_watch": [
1999
1999
  "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",
2000
2000
  "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",
@@ -2089,7 +2089,7 @@
2089
2089
  "CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
2090
2090
  ],
2091
2091
  "signature": "xa1kr/92/0fBds7LVtRy9aCM2tQRWAfNSFoC6ygbOU7QjamUo8Wt2h2n9W+nZT34STN3woYhXqtfmm8NSjXdBQ==",
2092
- "signed_at": "2026-06-22T14:16:29.811Z"
2092
+ "signed_at": "2026-06-22T17:11:02.790Z"
2093
2093
  },
2094
2094
  {
2095
2095
  "name": "container-runtime-security",
@@ -2161,7 +2161,7 @@
2161
2161
  ],
2162
2162
  "last_threat_review": "2026-06-10",
2163
2163
  "signature": "60hyXAiYfY9lI+EpHG3iFFxa+oF5n71c0tTMa6iXFLNjBJKPevTsdNgQlQsBEnmRJ+dWtOjb/GNBW1KiBx4XAQ==",
2164
- "signed_at": "2026-06-22T14:16:29.811Z",
2164
+ "signed_at": "2026-06-22T17:11:02.791Z",
2165
2165
  "forward_watch": [
2166
2166
  "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"
2167
2167
  ]
@@ -2242,7 +2242,7 @@
2242
2242
  "MITRE ATLAS v2026.05 (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"
2243
2243
  ],
2244
2244
  "signature": "IEECw5lO6ZdpkE/j7Hv6/3Jg2vj9fpXlozY+h85Hrn9QYOJuI0gfBjHoBEchJ2GUTmPwUKoiBoI72PBAQw9RCQ==",
2245
- "signed_at": "2026-06-22T14:16:29.812Z"
2245
+ "signed_at": "2026-06-22T17:11:02.793Z"
2246
2246
  },
2247
2247
  {
2248
2248
  "name": "incident-response-playbook",
@@ -2316,7 +2316,7 @@
2316
2316
  "NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
2317
2317
  ],
2318
2318
  "signature": "pN1Qa5NRyNJ6h0pmbbqpy3xWdkMANDMUbBst5SKR5fmmqMFopmyrzwIGagiiNgqxjjCunj9J9Wbzs1sL48sMCQ==",
2319
- "signed_at": "2026-06-22T14:16:29.812Z"
2319
+ "signed_at": "2026-06-22T17:11:02.795Z"
2320
2320
  },
2321
2321
  {
2322
2322
  "name": "ransomware-response",
@@ -2396,7 +2396,7 @@
2396
2396
  ],
2397
2397
  "last_threat_review": "2026-06-10",
2398
2398
  "signature": "sXPA1lvWvZpf5sHxZXRkO0Kcs6gnXiYq0mWA53y1HeBwQGJ+viSPMt5IMobkjd5qLFYchg0CpW/nBUIvUE6/DA==",
2399
- "signed_at": "2026-06-22T14:16:29.812Z"
2399
+ "signed_at": "2026-06-22T17:11:02.796Z"
2400
2400
  },
2401
2401
  {
2402
2402
  "name": "email-security-anti-phishing",
@@ -2464,7 +2464,7 @@
2464
2464
  ],
2465
2465
  "last_threat_review": "2026-05-18",
2466
2466
  "signature": "FVBn4ex2qPIo9SHMVJ6tntoz4tVwjbIq3m6wDjjZyv2JODlS+90GBYCOkNamxxkmw/6de6SMs0YHQiF/xjo/DQ==",
2467
- "signed_at": "2026-06-22T14:16:29.813Z"
2467
+ "signed_at": "2026-06-22T17:11:02.797Z"
2468
2468
  },
2469
2469
  {
2470
2470
  "name": "age-gates-child-safety",
@@ -2539,7 +2539,7 @@
2539
2539
  "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"
2540
2540
  ],
2541
2541
  "signature": "7Wdii9uwYmDrzQohoWRWDa5A4aDRKOmdLPq5Jsql86O0Gm7pQANnj9tX+cHpQcQ/Ui8VLuNVP8UFdKxxHeDnBg==",
2542
- "signed_at": "2026-06-22T14:16:29.813Z"
2542
+ "signed_at": "2026-06-22T17:11:02.799Z"
2543
2543
  },
2544
2544
  {
2545
2545
  "name": "cloud-iam-incident",
@@ -2619,7 +2619,7 @@
2619
2619
  ],
2620
2620
  "last_threat_review": "2026-05-15",
2621
2621
  "signature": "r9ii4nb3HJELdtKCGF5qy9PHOiot3GC24yfxfGAKlLENHkdRvRkvvL99eV/6RXyfUaMyrnc2Te8tPQcNu5bsDg==",
2622
- "signed_at": "2026-06-22T14:16:29.814Z",
2622
+ "signed_at": "2026-06-22T17:11:02.800Z",
2623
2623
  "forward_watch": [
2624
2624
  "AWS IAM Identity Center session-policy refresh and step-up-on-admin enforcement (anticipated 2026-H2 release)",
2625
2625
  "GCP Workload Identity Federation principal-set attribute mapping tightening (post-2026 Q3 Federation hardening guide)",
@@ -2713,7 +2713,7 @@
2713
2713
  ],
2714
2714
  "last_threat_review": "2026-05-15",
2715
2715
  "signature": "9mfDtMApMAg9V/lmwpniNxo/6gNZoOEoYDfyFvyWvKrPMtc7H9F8uz06FVoARe/J49saAKTVXOurNE1D/KtpCQ==",
2716
- "signed_at": "2026-06-22T14:16:29.814Z",
2716
+ "signed_at": "2026-06-22T17:11:02.801Z",
2717
2717
  "forward_watch": [
2718
2718
  "Entra ID conditional access evolution post-Midnight Blizzard — Microsoft's 2025-2026 commitments on legacy-tenant MFA enforcement and OAuth-app consent gating",
2719
2719
  "Okta IPSIE (Interoperability Profile for Secure Identity in the Enterprise) OpenID Foundation working-group output and adoption timeline",
@@ -2785,7 +2785,7 @@
2785
2785
  "d3fend_refs": [],
2786
2786
  "last_threat_review": "2026-06-02",
2787
2787
  "signature": "dOFgANMtHm64uSFRjMfeA0fWZS9ISwMTt59I2CbjaF1PgTtbtb8yg/f+3pC4eXcXIET0KPGxRRmENycfIz3rAw==",
2788
- "signed_at": "2026-06-22T14:16:29.814Z"
2788
+ "signed_at": "2026-06-22T17:11:02.803Z"
2789
2789
  },
2790
2790
  {
2791
2791
  "name": "mail-server-hardening",
@@ -2844,7 +2844,7 @@
2844
2844
  "d3fend_refs": [],
2845
2845
  "last_threat_review": "2026-06-02",
2846
2846
  "signature": "5x8RGzKo/T2IbYnFkYMZs16ThD9a5FEdrKHsnwvtMuGxdyyfZsKNIJVWw2I8O0um0UOXwwkl17Z3zhch26ymCQ==",
2847
- "signed_at": "2026-06-22T14:16:29.814Z"
2847
+ "signed_at": "2026-06-22T17:11:02.803Z"
2848
2848
  },
2849
2849
  {
2850
2850
  "name": "network-trust",
@@ -2900,7 +2900,7 @@
2900
2900
  "d3fend_refs": [],
2901
2901
  "last_threat_review": "2026-06-02",
2902
2902
  "signature": "C/RdmnGjXKreKiGkwJfUESIxUwQzisGJ9A7KBdE9cBRBHBYHkAcScV89L+Z3kOG032Qax6LeIQai7Lr3nrQBCA==",
2903
- "signed_at": "2026-06-22T14:16:29.815Z"
2903
+ "signed_at": "2026-06-22T17:11:02.804Z"
2904
2904
  },
2905
2905
  {
2906
2906
  "name": "audit-log-integrity",
@@ -2955,7 +2955,7 @@
2955
2955
  "d3fend_refs": [],
2956
2956
  "last_threat_review": "2026-06-02",
2957
2957
  "signature": "LvUAq+rXCA8PgNy9lTKOmFqa8T9EJg4JEH0axXvEAf9MIHqbR51ergUkFMLaTuvFuApwsPu0r1C30QgScZbiAw==",
2958
- "signed_at": "2026-06-22T14:16:29.815Z"
2958
+ "signed_at": "2026-06-22T17:11:02.805Z"
2959
2959
  },
2960
2960
  {
2961
2961
  "name": "self-update-integrity",
@@ -3009,7 +3009,7 @@
3009
3009
  "d3fend_refs": [],
3010
3010
  "last_threat_review": "2026-06-02",
3011
3011
  "signature": "BOQZ8G9LdyG/tfpz6rCU4THz5Dq2HGJkUV7uCqSTCMrqnXH6seNXKM41FWawYo0s2VUlXtRgaO7ETp5qgxw+CQ==",
3012
- "signed_at": "2026-06-22T14:16:29.815Z"
3012
+ "signed_at": "2026-06-22T17:11:02.806Z"
3013
3013
  },
3014
3014
  {
3015
3015
  "name": "multitenancy-isolation",
@@ -3067,7 +3067,7 @@
3067
3067
  "d3fend_refs": [],
3068
3068
  "last_threat_review": "2026-06-02",
3069
3069
  "signature": "bncN0VE4ns0lxtPe48xDR33lerw54BPVkSwX7XlrLQcMHTrV94lmjO8z4ZP4Ko61n+KpuP2SNU8v9JT0bGMmAA==",
3070
- "signed_at": "2026-06-22T14:16:29.815Z"
3070
+ "signed_at": "2026-06-22T17:11:02.807Z"
3071
3071
  },
3072
3072
  {
3073
3073
  "name": "decompression-dos",
@@ -3125,7 +3125,7 @@
3125
3125
  "d3fend_refs": [],
3126
3126
  "last_threat_review": "2026-06-02",
3127
3127
  "signature": "03i0ZO5ScHIOjGvDKUEsws6m+20fnlJrEhSBtMU7PJzQutrPpmW9gULE0wbayTD5H9URh5nSKZuuVjvjVhZNCg==",
3128
- "signed_at": "2026-06-22T14:16:29.816Z"
3128
+ "signed_at": "2026-06-22T17:11:02.807Z"
3129
3129
  },
3130
3130
  {
3131
3131
  "name": "log-injection-telemetry",
@@ -3180,7 +3180,7 @@
3180
3180
  "d3fend_refs": [],
3181
3181
  "last_threat_review": "2026-06-02",
3182
3182
  "signature": "twTmA2l+Am2k2PCzB2DNxDQwoYlGopJp/3QgQwBsP7h2LF+uNudisqfuOVV/bYnWfrUhuYZUWbsR3mhzbJCUBw==",
3183
- "signed_at": "2026-06-22T14:16:29.816Z"
3183
+ "signed_at": "2026-06-22T17:11:02.808Z"
3184
3184
  },
3185
3185
  {
3186
3186
  "name": "privacy-consent-ops",
@@ -3235,11 +3235,11 @@
3235
3235
  "d3fend_refs": [],
3236
3236
  "last_threat_review": "2026-06-02",
3237
3237
  "signature": "0sfKSVrIf555c9bDGkXB96fUClN52fMAMYoQsNQ9eUUSzDiWUwdou+q0gYt43nObtj3m9Yb9X6q3oCXDVMgUDg==",
3238
- "signed_at": "2026-06-22T14:16:29.816Z"
3238
+ "signed_at": "2026-06-22T17:11:02.809Z"
3239
3239
  }
3240
3240
  ],
3241
3241
  "manifest_signature": {
3242
3242
  "algorithm": "Ed25519",
3243
- "signature_base64": "M9BUq5cgR8+SjFVD09FDEKLySxzP4ooYVi99hZkybLUt22e4Nw/uZ4frEUvHrxUSbvG2Wej+J5GR1lRdhM4+AA=="
3243
+ "signature_base64": "n5BM7fxzNpnPWaoIaoKOReV3hdZ6095VSvxHN9nOq5mtPMmQ3RZCkF7DXwHgQYjgTsF4wfc4Qjua3vqNpZ3sAQ=="
3244
3244
  }
3245
3245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/exceptd-skills",
3
- "version": "0.18.14",
3
+ "version": "0.18.16",
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:067abada-564e-42a3-b5d4-c0f82acd46c2",
4
+ "serialNumber": "urn:uuid:0b112883-81a5-4ba8-8e61-746f26fe6ffc",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2029-06-12T04:15:22.000Z",
7
+ "timestamp": "2031-11-20T00:04:19.000Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "blamejs",
11
11
  "name": "scripts/refresh-sbom.js",
12
- "version": "0.18.14"
12
+ "version": "0.18.16"
13
13
  }
14
14
  ],
15
15
  "component": {
16
- "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.14",
16
+ "bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.16",
17
17
  "type": "application",
18
18
  "name": "@blamejs/exceptd-skills",
19
- "version": "0.18.14",
19
+ "version": "0.18.16",
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.14",
28
+ "purl": "pkg:npm/%40blamejs/exceptd-skills@0.18.16",
29
29
  "hashes": [
30
30
  {
31
31
  "alg": "SHA-256",
32
- "content": "5229acc52d6c7f075290e6f99516cbc8a832a9a94ba8e421c093d181922db250"
32
+ "content": "e9468ce3b3ced06212212f4d673fd48029af7da454f706b27f808087a9dabb59"
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.14"
38
+ "url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.18.16"
39
39
  },
40
40
  {
41
41
  "type": "vcs",
@@ -116,11 +116,11 @@
116
116
  "hashes": [
117
117
  {
118
118
  "alg": "SHA-256",
119
- "content": "b78694e78efb23ab479560c09f0e08169c646718a4f924dc63c516f7342d8988"
119
+ "content": "5fa37969557300b07eb36f4c82162ffd75f626d93c5b86c25bd6afb0c0c4e196"
120
120
  },
121
121
  {
122
122
  "alg": "SHA3-512",
123
- "content": "c708aa3f4649f7d9675364a253a8de1eebc668a3c10e595c062e5e44f072c36be1ba55e85aef5a9d2a9bda909107800f869310f8451e79bb693d8350d969dbb6"
123
+ "content": "74cc119ea3fafd51b551119855282a14786fd1514ab6a777ceb57e4e352098222840ddbbb5e7eeb1b3ee4b3d5b9e47ada3b34ef85b432922d61d53408ebd3b33"
124
124
  }
125
125
  ]
126
126
  },
@@ -281,11 +281,11 @@
281
281
  "hashes": [
282
282
  {
283
283
  "alg": "SHA-256",
284
- "content": "c92e6b3bda1e17776ad8d0bd2f6ae122fe7b4e2a59fc026fb5ee1d1d2bd5a103"
284
+ "content": "d2c715002ff57fe0896b994d946a379e2e11258b6df2080c2241247dd9cbe39e"
285
285
  },
286
286
  {
287
287
  "alg": "SHA3-512",
288
- "content": "3fb30c0cde51d1aefe3178a6d589597deef8ea474e3337609b245bd7a3dd5e80d41d21956d753fe8947d1ed9bc74147ada7d53ba4d4c7a50f9990a5f2abe9c9f"
288
+ "content": "1aa590b60e4e66a34e882b216fa1862b83a96260859feea15a5bdb263299486e78cdf17ee79e9529936349ab6fef07a294f19ad765f56481fbb211ccbdea02a9"
289
289
  }
290
290
  ]
291
291
  },
@@ -491,11 +491,11 @@
491
491
  "hashes": [
492
492
  {
493
493
  "alg": "SHA-256",
494
- "content": "3e3563a1b737f9e9afe4bdee10247ffbacb971be6e56ee281458ba2e8bd579cf"
494
+ "content": "d346a9fdd0dc289cee02cdbd2355eb61bd31231fa3c7745af6e071de5e85f747"
495
495
  },
496
496
  {
497
497
  "alg": "SHA3-512",
498
- "content": "59d6f29f868301e2879ed3bbe3d76d5a4396ac7ce5e5b2e0812f5372db10e1f022404db653c6ea3b441c6d8dbef4902a82b7c7169fe3dc2607fc204507965010"
498
+ "content": "ee9681ac0aef46f77dc3537eee23b472dac68d4977d700896d341338498c567b1a3323b7b80586a6956f068c8e7128b950a393ff90ebf29f4d84debb25b59d61"
499
499
  }
500
500
  ]
501
501
  },
@@ -551,11 +551,11 @@
551
551
  "hashes": [
552
552
  {
553
553
  "alg": "SHA-256",
554
- "content": "8cb21531b4648f2fe534984adc70cf86962f1c8821732452ded8ad7853835bb7"
554
+ "content": "4734aa4636acf4f5cefb4c086ca8d64d6d5c2875b5f6ec23c7304a50166f41ba"
555
555
  },
556
556
  {
557
557
  "alg": "SHA3-512",
558
- "content": "bce87020e5e9edb5628ecb10580cc04d02d8cd5b89110e2d3e477970a615b9b68ea3ef085d612ccf488b8cdb0441bed1ca14a80140e8c70cfd668f099efb4c29"
558
+ "content": "a82387ab591eec1dff1e63c88ae0a06c6e422d98631c23ba854eb074234ae5aeb2f7de98aa9813de07c3b9769047f9ac254a9cee53f9f8d95dc20a1b17aae38e"
559
559
  }
560
560
  ]
561
561
  },
@@ -641,11 +641,11 @@
641
641
  "hashes": [
642
642
  {
643
643
  "alg": "SHA-256",
644
- "content": "d4a30c75da45a354b6f8d4da6df9496c964c92d6c7223369902b27f52391c481"
644
+ "content": "5a21e14c0c1e59f28f530f7121b7a94f096766751b01fbfa4c82c9a4ed819a4c"
645
645
  },
646
646
  {
647
647
  "alg": "SHA3-512",
648
- "content": "f1eb1c5c4d15006e169afa3e88e7bd04c7fb9aadeeb70caa0572a3ca10fc5df8e16b7a1993f545c98374b70b667f007d0f8ec162531c744316c375d5d6768afa"
648
+ "content": "f6facca12c0e27387d8c7b10064b30272782150ba7cc3cc404d52a6b4090e6b8c43aa34498454194a2230aea7a2c3fbd473dfe09ec40b6b5b2ae9ce5afdd8254"
649
649
  }
650
650
  ]
651
651
  },
@@ -731,11 +731,11 @@
731
731
  "hashes": [
732
732
  {
733
733
  "alg": "SHA-256",
734
- "content": "d477ed782fa2688b2f01dff8a911df6dfddbe9b6dba02eefda5917bc10b39676"
734
+ "content": "78d4e0b31bf3eb161cd343df2c6f87945bb5df3dafa59fc9ec68229a2d2d2ee1"
735
735
  },
736
736
  {
737
737
  "alg": "SHA3-512",
738
- "content": "d3f453f264e65cc7d0df4f1c61b683caeb86dcdff1b5fdf56aa8a30c5e91764046c6a5341e171a9931e2f1ec11a37b1b096d65e67c836e501600582823c82a30"
738
+ "content": "b81137055a740d1b4504409b098b5190ff6860b0e1306425801abc0d6279c6bc327730bcc7bf65aa96b6c089ce7d7b167d5106e5b7ff08543ee1b6b93b9da9fd"
739
739
  }
740
740
  ]
741
741
  },
@@ -1466,11 +1466,11 @@
1466
1466
  "hashes": [
1467
1467
  {
1468
1468
  "alg": "SHA-256",
1469
- "content": "7a80237a0efc67ef1c5356cfe2cb01682f2bdbd1c482c01f803bbcadeb2bd43d"
1469
+ "content": "c33e749b67d2d1ff19a4f5e6aac4947c97b9ef03d44f0f273cd44eb7a1a76cd1"
1470
1470
  },
1471
1471
  {
1472
1472
  "alg": "SHA3-512",
1473
- "content": "f48be7bb4286626dbc5a813850d7995d0eb370eae0933d990adf67de4dd0b11328ca1ade31f1d1db6cc8945fd56fa3c64da79fbd67456996c1c2c71a2f19b708"
1473
+ "content": "84c2b2d74d4fa8b88430a3c2bb662143bb580c7b7b42f2203a01a1384ad0cada0c51bd45f7d2697ba278715c9fd7ed37f3e1c0a3fcb1d6f4479d43386c285a2a"
1474
1474
  }
1475
1475
  ]
1476
1476
  },
@@ -1781,11 +1781,11 @@
1781
1781
  "hashes": [
1782
1782
  {
1783
1783
  "alg": "SHA-256",
1784
- "content": "6e330602c96826afef6d678cb981f7e8b6dc2f85e347157a4fbd31d13b18c423"
1784
+ "content": "67a4ea6652f8be1602acf415461ce22536b4fe07245cbb577a237709f49d1b7f"
1785
1785
  },
1786
1786
  {
1787
1787
  "alg": "SHA3-512",
1788
- "content": "01df582251e0616594050f2c93ac513aaa18be01c330159b429cc775f868da2f4c766d4c6988c869374e0318e0dfb91551b3fa3df109430a238236ce89bdc8be"
1788
+ "content": "2127dba695f401dcbae912b54e57fcea387dbd716974f807ab9a942cb43bc7cbe97a10f35d8a0daa0f8deb2aef3f82be5c651c3eeb0a46de8d546cb87a0a4598"
1789
1789
  }
1790
1790
  ]
1791
1791
  },
@@ -1901,11 +1901,11 @@
1901
1901
  "hashes": [
1902
1902
  {
1903
1903
  "alg": "SHA-256",
1904
- "content": "be5406117ede866ec4dc653cf15f9555f8fa58c254fc601800e4bacb07ddb609"
1904
+ "content": "c8376cbda42a9657b6a5d50f75d8c08a0bcab7c8456340042be7998133922ecb"
1905
1905
  },
1906
1906
  {
1907
1907
  "alg": "SHA3-512",
1908
- "content": "ce5d386778093686cec9cea5bf274ed1b557c03e617d9c6538fbb05debc81cabdeffbb46879b00de2479fd6a76395cea1a4e08182899d34ce6f7220ce2b25321"
1908
+ "content": "eb932bc21fa7e5c210f265b6e7a5eaebae58354c5a4f8cd61c66b4e35939c65a02ea9e1d84e0f44975c1e582217a3e8dae2172e1824b26b4f05d2555c54562e2"
1909
1909
  }
1910
1910
  ]
1911
1911
  },