@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 +20 -0
- package/bin/exceptd.js +36 -1
- package/data/_indexes/_meta.json +2 -2
- package/data/playbooks/citation-hygiene.json +2 -2
- package/data/playbooks/crypto-codebase.json +2 -2
- package/data/playbooks/idp-incident.json +1 -1
- package/data/playbooks/mcp.json +1 -1
- package/lib/playbook-runner.js +124 -14
- package/lib/validate-playbooks.js +59 -4
- package/manifest.json +53 -53
- package/package.json +1 -1
- package/sbom.cdx.json +26 -26
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)
|
|
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
|
}
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-06-
|
|
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": "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
937
|
+
"condition": "rwep >= 75 AND blast_radius_score >= 4",
|
|
938
938
|
"action": "page_on_call"
|
|
939
939
|
},
|
|
940
940
|
{
|
package/data/playbooks/mcp.json
CHANGED
package/lib/playbook-runner.js
CHANGED
|
@@ -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, ...
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
3238
|
+
"signed_at": "2026-06-22T17:11:02.809Z"
|
|
3239
3239
|
}
|
|
3240
3240
|
],
|
|
3241
3241
|
"manifest_signature": {
|
|
3242
3242
|
"algorithm": "Ed25519",
|
|
3243
|
-
"signature_base64": "
|
|
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.
|
|
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:
|
|
4
|
+
"serialNumber": "urn:uuid:0b112883-81a5-4ba8-8e61-746f26fe6ffc",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "
|
|
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.
|
|
12
|
+
"version": "0.18.16"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.18.
|
|
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.
|
|
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.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.18.16",
|
|
29
29
|
"hashes": [
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
32
|
-
"content": "
|
|
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.
|
|
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": "
|
|
119
|
+
"content": "5fa37969557300b07eb36f4c82162ffd75f626d93c5b86c25bd6afb0c0c4e196"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"alg": "SHA3-512",
|
|
123
|
-
"content": "
|
|
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": "
|
|
284
|
+
"content": "d2c715002ff57fe0896b994d946a379e2e11258b6df2080c2241247dd9cbe39e"
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
"alg": "SHA3-512",
|
|
288
|
-
"content": "
|
|
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": "
|
|
494
|
+
"content": "d346a9fdd0dc289cee02cdbd2355eb61bd31231fa3c7745af6e071de5e85f747"
|
|
495
495
|
},
|
|
496
496
|
{
|
|
497
497
|
"alg": "SHA3-512",
|
|
498
|
-
"content": "
|
|
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": "
|
|
554
|
+
"content": "4734aa4636acf4f5cefb4c086ca8d64d6d5c2875b5f6ec23c7304a50166f41ba"
|
|
555
555
|
},
|
|
556
556
|
{
|
|
557
557
|
"alg": "SHA3-512",
|
|
558
|
-
"content": "
|
|
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": "
|
|
644
|
+
"content": "5a21e14c0c1e59f28f530f7121b7a94f096766751b01fbfa4c82c9a4ed819a4c"
|
|
645
645
|
},
|
|
646
646
|
{
|
|
647
647
|
"alg": "SHA3-512",
|
|
648
|
-
"content": "
|
|
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": "
|
|
734
|
+
"content": "78d4e0b31bf3eb161cd343df2c6f87945bb5df3dafa59fc9ec68229a2d2d2ee1"
|
|
735
735
|
},
|
|
736
736
|
{
|
|
737
737
|
"alg": "SHA3-512",
|
|
738
|
-
"content": "
|
|
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": "
|
|
1469
|
+
"content": "c33e749b67d2d1ff19a4f5e6aac4947c97b9ef03d44f0f273cd44eb7a1a76cd1"
|
|
1470
1470
|
},
|
|
1471
1471
|
{
|
|
1472
1472
|
"alg": "SHA3-512",
|
|
1473
|
-
"content": "
|
|
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": "
|
|
1784
|
+
"content": "67a4ea6652f8be1602acf415461ce22536b4fe07245cbb577a237709f49d1b7f"
|
|
1785
1785
|
},
|
|
1786
1786
|
{
|
|
1787
1787
|
"alg": "SHA3-512",
|
|
1788
|
-
"content": "
|
|
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": "
|
|
1904
|
+
"content": "c8376cbda42a9657b6a5d50f75d8c08a0bcab7c8456340042be7998133922ecb"
|
|
1905
1905
|
},
|
|
1906
1906
|
{
|
|
1907
1907
|
"alg": "SHA3-512",
|
|
1908
|
-
"content": "
|
|
1908
|
+
"content": "eb932bc21fa7e5c210f265b6e7a5eaebae58354c5a4f8cd61c66b4e35939c65a02ea9e1d84e0f44975c1e582217a3e8dae2172e1824b26b4f05d2555c54562e2"
|
|
1909
1909
|
}
|
|
1910
1910
|
]
|
|
1911
1911
|
},
|