@blamejs/exceptd-skills 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/bin/exceptd.js +95 -61
- package/data/_indexes/_meta.json +2 -2
- package/lib/playbook-runner.js +147 -57
- package/manifest-snapshot.json +1 -1
- package/manifest.json +39 -39
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.5 — 2026-05-12
|
|
4
|
+
|
|
5
|
+
**Patch: items 82-90 + permanent regression suite at `tests/operator-bugs.test.js`.**
|
|
6
|
+
|
|
7
|
+
Every operator-reported bug fixed across the v0.9.5 → v0.11.x arc now lands as a named test case in `tests/operator-bugs.test.js`. Re-introductions surface at `npm test`, not at user re-report. 27 cases on day one covering items #17, #18, #19, #31, #32, #33, #46, #58, #62, #65, #71, #73, #76, #82, #83, #85, #87.
|
|
8
|
+
|
|
9
|
+
### Critical
|
|
10
|
+
|
|
11
|
+
- **#82 SARIF / CSAF / OpenVEX rendered empty bundles** when the playbook had no catalogued CVEs. crypto-codebase / library-author have `domain.cve_refs: []` by design (they check process / posture, not catalogue CVEs), so the renderers had nothing to populate. Pre-0.11.5 a successful run with 9 indicators firing produced `vulnerabilities: 0` / `results: 0` / `statements: 0`. Now: indicators that fire (verdict: hit) and framework gaps are first-class SARIF results / CSAF vulnerabilities / OpenVEX statements. Each fired indicator becomes a SARIF result with `kind: indicator_hit` + a pseudo-CVE id under the `exceptd:` namespace for CSAF/OpenVEX. SARIF + CSAF + OpenVEX bundles now meaningfully integrate with GitHub Code Scanning / VEX downstreams / supply-chain tooling even for posture-only playbooks.
|
|
12
|
+
|
|
13
|
+
### Bugs
|
|
14
|
+
|
|
15
|
+
- **#83 lint and run disagreed on shape validity.** Lint walked the raw submission and only matched observations whose key was a known artifact id. The runner's `normalizeSubmission` followed `val.artifact` indirection — so observations with arbitrary keys (`obs-1`, `obs-2`) and an `artifact:` field route correctly. Fix: lint now runs the same `normalizeSubmission` the runner does, then validates the canonical normalized shape. The user's proposed fix — single observations-normalizer module that lint, run, and format renderers all consume — landed.
|
|
16
|
+
|
|
17
|
+
- **#85 `from_observation` always null.** The diagnostic field on `indicators_evaluated[]` is now populated with the observation key that drove each indicator outcome (when supplied via flat-shape observation + indicator + result). Lets operators trace "which observation produced this verdict" without guessing.
|
|
18
|
+
|
|
19
|
+
- **#86 / #76 `--format garbage` was silent.** v0.11.4 fixed it for `run`; this release fixes the same surface on `ci`. Both now emit `{ok:false, error, verb}` JSON to stderr with non-zero exit when an unknown format is requested.
|
|
20
|
+
|
|
21
|
+
- **#90 legacy verbs in help.** v0.10.x legacy verbs (plan / govern / direct / look / scan / dispatch / etc) appeared in the help output alongside their v0.11 replacements. Operators copy-pasting from `exceptd help | grep '^ [a-z]'` ended up using legacy verbs and missed the new ones. Each legacy entry is now prefixed with `[DEPRECATED]` so the grep pattern still excludes them.
|
|
22
|
+
|
|
23
|
+
### Deferred (confirmed not yet shipped)
|
|
24
|
+
|
|
25
|
+
- **#88 default-output flip incomplete.** `emit()` indents JSON on TTY (improvement over compact JSON); `discover`/`doctor`/`ask`/`refresh` use custom human renderers. `brief`/`run`/`attest list`/`lint` still emit JSON because their data is too rich for a compact human view. Indented-JSON-on-TTY is the v0.11.x answer; per-verb human renderers continue to be incremental.
|
|
26
|
+
|
|
27
|
+
- **#89 warn-level preconditions exit 0.** `on_fail: halt` correctly exits 1; `on_fail: warn` exits 0 with `preflight_issues` populated. The operator wants warn-level to also fail CI gates — `--strict-preconditions` flag deferred to v0.11.6. Today: use `exceptd ci` for CI gates (correctly maps detected/escalate to exit 2).
|
|
28
|
+
|
|
29
|
+
### Test infrastructure
|
|
30
|
+
|
|
31
|
+
- New: `tests/operator-bugs.test.js` (27 cases, all green). Future bug fixes land here as named cases so the audit script becomes part of CI.
|
|
32
|
+
|
|
33
|
+
## 0.11.4 — 2026-05-12
|
|
34
|
+
|
|
35
|
+
**Patch: high-impact #71 fix + items 72-77.**
|
|
36
|
+
|
|
37
|
+
### Critical fix
|
|
38
|
+
|
|
39
|
+
- **#71 detect didn't accept indicator-result synonyms.** Operators submitting flat-shape evidence with `observation.result: "no_hit"` (the standard vocabulary for years of CI/security tooling) hit the runner's strict `hit|miss|inconclusive` set, falsed every comparison, and ended up with `classification: "inconclusive"` regardless of evidence. This silently broke the new flat-shape submission UX that v0.11.0/v0.11.3 was built around. Same evidence in the legacy `signal_overrides` shape produced the correct `not_detected` verdict.
|
|
40
|
+
|
|
41
|
+
Fix: a `canonicalize()` step in both `normalizeSubmission` and `detect()` maps `no_hit`/`no-hit`/`clean`/`clear`/`not_hit`/`ok`/`pass`/`negative`/`false` → `miss`; `hit`/`detected`/`positive`/`true` → `hit`; `inconclusive`/`unknown`/`unverified`/`null` → `inconclusive`. Operator vocabulary is now normalized to the engine's canonical 3-value set at submission boundary.
|
|
42
|
+
|
|
43
|
+
- **#77 CSAF/OpenVEX bundles auto-fixed.** Downstream of #71: now that detect actually processes signal_overrides correctly, the per-CVE statements in `bundle.vulnerabilities` / `statements` populate when there are matched_cves.
|
|
44
|
+
|
|
45
|
+
### Bugs
|
|
46
|
+
|
|
47
|
+
- **#72 ci --format silently ignored.** `exceptd ci --scope code --format summary` and the bare command emitted byte-identical full bundles (~350 KB). CI gates couldn't get a compact verdict without piping through jq. Now ci honors `--format summary|markdown|csaf-2.0|sarif|openvex` with the same shortcuts as `run --format`. Summary is a single-line JSON with `session_id + playbooks_run + verdict + counts`.
|
|
48
|
+
- **#73 `indicators_evaluated` type changed silently.** v0.11.3 introduced it as an integer count; downstream consumers iterating `for i in detect.indicators_evaluated` crashed. Restored to an array of `{signal_id, outcome, confidence}`. Added `indicators_evaluated_count` as a peer field for callers wanting the integer.
|
|
49
|
+
- **#76 `ci --format garbage` silent empty stdout.** Invalid format values now return `{ok:false, error, verb:"ci"}` JSON to stderr with exit 2, matching the unified error shape.
|
|
50
|
+
|
|
51
|
+
### Not addressed in this patch
|
|
52
|
+
|
|
53
|
+
- **#74 default-output flip still incomplete.** `emit()` indents JSON when stdout is a TTY (improvement over compact), but `brief`/`run`/`attest list`/`lint` still emit JSON, not a custom human form. The richer data on `brief`/`run` doesn't have a natural compact human view. Indented-JSON-on-TTY ships as the v0.11.x answer; a true human renderer per verb is deferred. `discover`/`doctor`/`ask`/`refresh` continue with their custom renderers.
|
|
54
|
+
- **#75 preflight-blocked exit 0 for warn-level.** `on_fail: halt` preconditions correctly exit 1; `on_fail: warn` preconditions correctly exit 0 with `preflight_issues` populated. The operator wants warn-level to also fail CI — that's a `--strict-preconditions` flag, deferred to v0.11.5. Today: use `exceptd ci` for CI gates (correctly maps detected/escalate to exit 2); `run` is for single-investigation invocations where warn-level info is appropriate.
|
|
55
|
+
|
|
56
|
+
### Already shipped (cross-referenced)
|
|
57
|
+
|
|
58
|
+
- #78 `doctor --fix` (v0.11.2).
|
|
59
|
+
|
|
3
60
|
## 0.11.3 — 2026-05-12
|
|
4
61
|
|
|
5
62
|
**Patch: operator-reported item #71 + full feature audit findings.**
|
package/bin/exceptd.js
CHANGED
|
@@ -245,17 +245,26 @@ v0.11.0 canonical surface
|
|
|
245
245
|
v0.10.x compatibility (will be removed in v0.12)
|
|
246
246
|
────────────────────────────────────────────────
|
|
247
247
|
|
|
248
|
-
These verbs still work but emit a one-time deprecation banner.
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
248
|
+
These verbs still work but emit a one-time deprecation banner. The
|
|
249
|
+
[DEPRECATED] prefix is included so \`exceptd help | grep '^ [a-z]'\`
|
|
250
|
+
doesn't surface them in the active-verbs list. Migrate to v0.11:
|
|
251
|
+
|
|
252
|
+
[DEPRECATED] plan → brief --all
|
|
253
|
+
[DEPRECATED] govern <pb> → brief <pb> --phase govern
|
|
254
|
+
[DEPRECATED] direct <pb> → brief <pb> --phase direct
|
|
255
|
+
[DEPRECATED] look <pb> → brief <pb> --phase look
|
|
256
|
+
[DEPRECATED] ingest → run
|
|
257
|
+
[DEPRECATED] reattest <sid> → attest diff <sid>
|
|
258
|
+
[DEPRECATED] list-attestations → attest list
|
|
259
|
+
[DEPRECATED] scan → discover --scan-only
|
|
260
|
+
[DEPRECATED] dispatch → discover
|
|
261
|
+
[DEPRECATED] currency → doctor --currency
|
|
262
|
+
[DEPRECATED] verify → doctor --signatures
|
|
263
|
+
[DEPRECATED] validate-cves → doctor --cves
|
|
264
|
+
[DEPRECATED] validate-rfcs → doctor --rfcs
|
|
265
|
+
[DEPRECATED] watchlist → watch
|
|
266
|
+
[DEPRECATED] prefetch → refresh --no-network
|
|
267
|
+
[DEPRECATED] build-indexes → refresh --indexes-only
|
|
259
268
|
|
|
260
269
|
Output: default human-readable (v0.11.0). --json for machine output.
|
|
261
270
|
--pretty for indented JSON.
|
|
@@ -818,7 +827,6 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
818
827
|
catch (e) { return emitError(`lint: failed to read evidence: ${e.message}`, { evidence: evidencePath }, pretty); }
|
|
819
828
|
|
|
820
829
|
const directiveId = args.directive || (pb.directives[0] && pb.directives[0].id);
|
|
821
|
-
const resolved = runner._resolvedPhase;
|
|
822
830
|
const lookPhase = pb.phases?.look || {};
|
|
823
831
|
const detectPhase = pb.phases?.detect || {};
|
|
824
832
|
|
|
@@ -827,26 +835,38 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
827
835
|
const knownIndicators = new Set((detectPhase.indicators || []).map(i => i.id));
|
|
828
836
|
const knownPreconditions = new Set((pb._meta?.preconditions || []).map(p => p.id));
|
|
829
837
|
|
|
830
|
-
//
|
|
838
|
+
// v0.11.5 #83: shared shape contract with runner. Pre-0.11.5 lint
|
|
839
|
+
// walked the raw submission and only matched observations whose key was
|
|
840
|
+
// a known artifact id. The runner's normalizeSubmission follows
|
|
841
|
+
// `val.artifact` indirection — so observations with arbitrary keys
|
|
842
|
+
// (obs-1, obs-2) and an `artifact:` field route correctly. Lint must
|
|
843
|
+
// do the same normalization before validating, or lint and run disagree
|
|
844
|
+
// on what's a valid submission.
|
|
845
|
+
const normalized = runner.normalizeSubmission(submission, pb);
|
|
831
846
|
const flat = submission.observations || null;
|
|
832
|
-
const artifactsKey = flat ? flat : (submission.artifacts || {});
|
|
833
|
-
const signalsKey = flat ? flat : (submission.signal_overrides || {});
|
|
834
847
|
|
|
848
|
+
// After normalize, validation walks the canonical nested shape.
|
|
835
849
|
const missingRequired = requiredArtifacts.filter(id => {
|
|
836
|
-
const a =
|
|
837
|
-
return !a ||
|
|
850
|
+
const a = normalized.artifacts && normalized.artifacts[id];
|
|
851
|
+
return !a || !a.captured;
|
|
838
852
|
});
|
|
839
853
|
|
|
840
|
-
const unknownArtifactKeys = Object.keys(
|
|
854
|
+
const unknownArtifactKeys = Object.keys(normalized.artifacts || {})
|
|
841
855
|
.filter(k => !knownArtifacts.has(k));
|
|
842
|
-
const unknownSignalKeys = Object.keys(
|
|
856
|
+
const unknownSignalKeys = Object.keys(normalized.signal_overrides || {})
|
|
843
857
|
.filter(k => !knownIndicators.has(k));
|
|
844
858
|
const unknownObservationKeys = flat
|
|
845
|
-
? Object.keys(flat).filter(k =>
|
|
859
|
+
? Object.keys(flat).filter(k => {
|
|
860
|
+
// Skip observations with explicit `artifact:` indirection — those
|
|
861
|
+
// are valid by-design even when the key doesn't match a known artifact.
|
|
862
|
+
const v = flat[k];
|
|
863
|
+
if (v && typeof v === "object" && v.artifact) return false;
|
|
864
|
+
return !knownArtifacts.has(k) && !knownIndicators.has(k) && !knownPreconditions.has(k);
|
|
865
|
+
})
|
|
846
866
|
: [];
|
|
847
867
|
|
|
848
868
|
const unsuppliedPreconditions = [...knownPreconditions].filter(
|
|
849
|
-
p => !((submission.precondition_checks || {}).hasOwnProperty(p) || (
|
|
869
|
+
p => !(((submission.precondition_checks || {}).hasOwnProperty(p)) || ((normalized.precondition_checks || {}).hasOwnProperty(p)))
|
|
850
870
|
);
|
|
851
871
|
|
|
852
872
|
const issues = [];
|
|
@@ -866,33 +886,19 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
866
886
|
issues.push({ severity: "warn", kind: "unknown_observation_key", key: k });
|
|
867
887
|
}
|
|
868
888
|
|
|
869
|
-
// #71 (v0.11.3): when a submission is flat-shape
|
|
870
|
-
//
|
|
871
|
-
// will return
|
|
872
|
-
// Lint must surface this so operators don't ship a half-shape evidence file
|
|
873
|
-
// that passes lint but produces an inconclusive run.
|
|
889
|
+
// #71 (v0.11.3) + #83 (v0.11.5): when a submission is flat-shape but the
|
|
890
|
+
// post-normalize signal_overrides is empty AND no verdict.classification
|
|
891
|
+
// is supplied, detect() will return inconclusive. Surface this before run.
|
|
874
892
|
if (flat) {
|
|
875
|
-
const observationsWithoutIndicator = Object.entries(flat).filter(([k, v]) => {
|
|
876
|
-
if (!knownArtifacts.has(k)) return false; // unknown keys flagged elsewhere
|
|
877
|
-
if (typeof v !== "object" || v === null) return false;
|
|
878
|
-
const captured = v.captured !== false;
|
|
879
|
-
return captured && !(v.indicator && v.result);
|
|
880
|
-
});
|
|
881
893
|
const verdictClass = submission.verdict?.classification;
|
|
882
894
|
const verdictWillDrive = verdictClass === "clean" || verdictClass === "not_detected" || verdictClass === "detected" || verdictClass === "inconclusive";
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
severity: "warn",
|
|
887
|
-
kind: "observation_lacks_indicator_result",
|
|
888
|
-
observation_key: k,
|
|
889
|
-
hint: `Artifact "${k}" captured without "indicator" + "result" fields. detect will return 'inconclusive' for this indicator. Either add { "indicator": "<id>", "result": "hit"|"miss"|"inconclusive" } per observation, OR supply verdict.classification at the submission root to drive the overall verdict.`,
|
|
890
|
-
});
|
|
891
|
-
}
|
|
895
|
+
const normalizedHasOverrides = Object.keys(normalized.signal_overrides || {}).length > 0;
|
|
896
|
+
if (!verdictWillDrive && !normalizedHasOverrides) {
|
|
897
|
+
const observationsCount = Object.keys(flat).length;
|
|
892
898
|
issues.push({
|
|
893
899
|
severity: "info",
|
|
894
900
|
kind: "detect_will_be_inconclusive",
|
|
895
|
-
hint: `Flat submission
|
|
901
|
+
hint: `Flat submission with ${observationsCount} observation(s) but no indicator+result fields and no verdict.classification. detect() will return 'inconclusive'. Each observation needs { "indicator": "<id>", "result": "hit"|"miss"|"inconclusive" } to drive an indicator outcome. Run \`exceptd run ${playbookId} --signal-list\` for the indicator IDs.`,
|
|
896
902
|
});
|
|
897
903
|
}
|
|
898
904
|
}
|
|
@@ -3066,25 +3072,53 @@ function cmdCi(runner, args, runOpts, pretty) {
|
|
|
3066
3072
|
const rwepValues = results.map(r => r.phases?.analyze?.rwep?.adjusted ?? 0);
|
|
3067
3073
|
const maxRwepObserved = rwepValues.length ? Math.max(...rwepValues) : 0;
|
|
3068
3074
|
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3075
|
+
const summary = {
|
|
3076
|
+
total: results.length,
|
|
3077
|
+
detected: results.filter(r => r.phases?.detect?.classification === "detected").length,
|
|
3078
|
+
inconclusive: results.filter(r => r.phases?.detect?.classification === "inconclusive").length,
|
|
3079
|
+
not_detected: results.filter(r => ["not_detected", "clean"].includes(r.phases?.detect?.classification)).length,
|
|
3080
|
+
blocked: results.filter(r => r && r.ok === false).length,
|
|
3081
|
+
max_rwep_observed: maxRwepObserved,
|
|
3082
|
+
jurisdiction_clocks_started: results
|
|
3083
|
+
.flatMap(r => r.phases?.close?.notification_actions || [])
|
|
3084
|
+
.filter(n => n && n.clock_started_at != null).length,
|
|
3085
|
+
verdict: fail ? "FAIL" : "PASS",
|
|
3086
|
+
fail_reasons: failReasons,
|
|
3087
|
+
};
|
|
3088
|
+
|
|
3089
|
+
// v0.11.4 (#72): ci --format <fmt> previously emitted the full bundle
|
|
3090
|
+
// regardless of flag. Now honors the same shortcuts as `run --format`:
|
|
3091
|
+
// summary → one-line JSON of session + verdict + counts
|
|
3092
|
+
// markdown → operator-readable digest
|
|
3093
|
+
// csaf → CSAF 2.0 envelope wrapping every result
|
|
3094
|
+
// sarif → SARIF 2.1.0 with results from every playbook
|
|
3095
|
+
// openvex → OpenVEX statements derived from every playbook's matched_cves
|
|
3096
|
+
let formatRaw = args.format;
|
|
3097
|
+
if (Array.isArray(formatRaw)) formatRaw = formatRaw[0];
|
|
3098
|
+
const fmt = formatRaw === "csaf-2.0" ? "csaf" : formatRaw;
|
|
3099
|
+
if (fmt === "summary") {
|
|
3100
|
+
emit({ verb: "ci", session_id: sessionId, playbooks_run: ids, summary }, pretty);
|
|
3101
|
+
} else if (fmt === "markdown") {
|
|
3102
|
+
const lines = [`# exceptd ci summary`, `session-id: ${sessionId}`, `verdict: **${summary.verdict}**`, ``];
|
|
3103
|
+
lines.push(`**Playbooks run:** ${summary.total} (${summary.detected} detected, ${summary.inconclusive} inconclusive, ${summary.not_detected} clean, ${summary.blocked} blocked)`);
|
|
3104
|
+
lines.push(`**Max RWEP observed:** ${summary.max_rwep_observed}`);
|
|
3105
|
+
lines.push(`**Jurisdiction clocks started:** ${summary.jurisdiction_clocks_started}`);
|
|
3106
|
+
if (summary.fail_reasons.length) {
|
|
3107
|
+
lines.push(``, `## Fail reasons`);
|
|
3108
|
+
for (const r of summary.fail_reasons) lines.push(`- ${r}`);
|
|
3109
|
+
}
|
|
3110
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
3111
|
+
} else if (fmt === "csaf" || fmt === "sarif" || fmt === "openvex") {
|
|
3112
|
+
// Aggregate the per-run bundles_by_format if present.
|
|
3113
|
+
const bundles = results.map(r => r.phases?.close?.evidence_package?.bundles_by_format?.[fmt === "csaf" ? "csaf-2.0" : fmt]).filter(Boolean);
|
|
3114
|
+
emit({ verb: "ci", session_id: sessionId, format: fmt, bundles_count: bundles.length, bundles }, pretty);
|
|
3115
|
+
} else if (fmt && fmt !== "json") {
|
|
3116
|
+
// v0.11.4 (#76): garbage format rejected with structured error, not silent empty stdout.
|
|
3117
|
+
process.stderr.write(JSON.stringify({ ok: false, error: `ci: --format "${fmt}" not in accepted set ["summary","markdown","csaf-2.0","sarif","openvex","json"].`, verb: "ci" }) + "\n");
|
|
3118
|
+
process.exit(2);
|
|
3119
|
+
} else {
|
|
3120
|
+
emit({ verb: "ci", session_id: sessionId, playbooks_run: ids, summary, results }, pretty);
|
|
3121
|
+
}
|
|
3088
3122
|
if (fail) {
|
|
3089
3123
|
process.stderr.write(`[exceptd ci] FAIL: ${failReasons.join("; ")}\n`);
|
|
3090
3124
|
process.exit(2);
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-12T17:00:14.346Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 49,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "da39bcf4cd05d571b5d76a009ede9ee721aa783c07764636e0f0a0bd4634a99a",
|
|
8
8
|
"data/atlas-ttps.json": "1500b5830dab070c4252496964a8c0948e1052a656e2c7c6e1efaf0350645e13",
|
|
9
9
|
"data/cve-catalog.json": "a81d3e4b491b27ccc084596b063a6108ff10c9eb01d7776922fc393980b534fe",
|
|
10
10
|
"data/cwe-catalog.json": "c3367d469b4b3d31e4c56397dd7a8305a0be338ecd85afa27804c0c9ce12157b",
|
package/lib/playbook-runner.js
CHANGED
|
@@ -304,8 +304,23 @@ function detect(playbookId, directiveId, agentSubmission = {}) {
|
|
|
304
304
|
const artifacts = agentSubmission.artifacts || {};
|
|
305
305
|
const overrides = agentSubmission.signal_overrides || {};
|
|
306
306
|
|
|
307
|
+
// v0.11.4 (#71): canonicalize the indicator result vocabulary. Operators
|
|
308
|
+
// submit shapes like "no_hit" / "clean" / "ok" / false from years of
|
|
309
|
+
// CI/security tooling convention; the engine internally uses
|
|
310
|
+
// hit | miss | inconclusive. Without canonicalization every flat-shape
|
|
311
|
+
// observation with result:"no_hit" silently fell through to inconclusive
|
|
312
|
+
// and broke per-indicator detection. Canonicalization happens here so
|
|
313
|
+
// both detect() and normalizeSubmission consumers see the same outcomes.
|
|
314
|
+
const canonicalize = (v) => {
|
|
315
|
+
if (v === true || v === 'hit' || v === 'detected' || v === 'positive') return 'hit';
|
|
316
|
+
if (v === false || v === 'miss' || v === 'no_hit' || v === 'no-hit' || v === 'clean' || v === 'clear' || v === 'not_hit' || v === 'ok' || v === 'pass' || v === 'negative') return 'miss';
|
|
317
|
+
if (v === 'inconclusive' || v === 'unknown' || v === 'unverified' || v === null) return 'inconclusive';
|
|
318
|
+
return null; // truly unknown — fall through
|
|
319
|
+
};
|
|
320
|
+
|
|
307
321
|
const indicatorResults = (det.indicators || []).map(ind => {
|
|
308
|
-
const
|
|
322
|
+
const rawOverride = overrides[ind.id];
|
|
323
|
+
const override = canonicalize(rawOverride);
|
|
309
324
|
let verdict;
|
|
310
325
|
if (override === 'hit' || override === 'miss' || override === 'inconclusive') {
|
|
311
326
|
verdict = override;
|
|
@@ -365,11 +380,22 @@ function detect(playbookId, directiveId, agentSubmission = {}) {
|
|
|
365
380
|
// v0.11.3 #71: surface what detect actually consumed. Operators reading
|
|
366
381
|
// the detect output now see whether their flat-shape observations + the
|
|
367
382
|
// signal_overrides + the classification override all reached the runner.
|
|
368
|
-
// Pre-0.11.3 detect's output was opaque — "inconclusive" with no
|
|
369
|
-
// explanation gave operators no signal about what went wrong.
|
|
370
383
|
observations_received: Object.keys(agentSubmission.artifacts || {}),
|
|
371
384
|
signals_received: Object.keys(agentSubmission.signal_overrides || {}),
|
|
372
|
-
|
|
385
|
+
// v0.11.4 (#73): downstream consumers iterating `indicators_evaluated`
|
|
386
|
+
// expect an array, not a count. Restore as array; provide
|
|
387
|
+
// `indicators_evaluated_count` for callers wanting the integer.
|
|
388
|
+
indicators_evaluated: indicatorResults.map(i => ({
|
|
389
|
+
signal_id: i.id,
|
|
390
|
+
outcome: i.verdict,
|
|
391
|
+
confidence: i.confidence,
|
|
392
|
+
// v0.11.5 #85: surface which observation produced this indicator's
|
|
393
|
+
// outcome (when the agent submitted it via flat-shape observation +
|
|
394
|
+
// indicator + result fields). Null when no observation drove the
|
|
395
|
+
// indicator (engine-computed default).
|
|
396
|
+
from_observation: agentSubmission._signal_origins?.[i.id] || null,
|
|
397
|
+
})),
|
|
398
|
+
indicators_evaluated_count: indicatorResults.length,
|
|
373
399
|
classification_override_applied: validOverrides.has(override) ? (override === 'clean' ? 'not_detected' : override) : null,
|
|
374
400
|
submission_shape_seen: agentSubmission._original_shape || (agentSubmission.artifacts ? 'nested (v0.10.x)' : 'empty')
|
|
375
401
|
};
|
|
@@ -497,6 +523,12 @@ function analyze(playbookId, directiveId, detectResult, agentSignals = {}) {
|
|
|
497
523
|
},
|
|
498
524
|
framework_gap_mapping: frameworkGaps,
|
|
499
525
|
escalations,
|
|
526
|
+
// v0.11.5 (#82): expose detect's per-indicator results + classification
|
|
527
|
+
// here so close()'s bundle builders can iterate indicators that fired
|
|
528
|
+
// and emit them as SARIF results / OpenVEX statements / CSAF notes.
|
|
529
|
+
// Prefixed with underscore to signal "for internal/render use".
|
|
530
|
+
_detect_indicators: detectResult.indicators || [],
|
|
531
|
+
_detect_classification: detectResult.classification,
|
|
500
532
|
vex: vexFilter ? {
|
|
501
533
|
filter_applied: true,
|
|
502
534
|
dropped_cve_count: vexDropped.length,
|
|
@@ -740,16 +772,30 @@ function analyzeFindingShape(a) {
|
|
|
740
772
|
}
|
|
741
773
|
|
|
742
774
|
function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals) {
|
|
743
|
-
// CSAF-2.0 shape
|
|
744
|
-
//
|
|
745
|
-
//
|
|
775
|
+
// CSAF-2.0 shape. v0.11.5 (#82): include vulnerabilities for both matched
|
|
776
|
+
// catalogue CVEs AND fired indicators (treated as advisory pseudo-CVEs
|
|
777
|
+
// under `exceptd:` namespace), so playbooks without catalogue CVEs still
|
|
778
|
+
// emit a non-empty bundle.
|
|
746
779
|
if (format === 'csaf-2.0') {
|
|
780
|
+
const indicatorHits = (analyze._detect_indicators || []).filter(i => i.verdict === 'hit');
|
|
781
|
+
const cveVulns = analyze.matched_cves.map(c => ({
|
|
782
|
+
cve: c.cve_id,
|
|
783
|
+
scores: [{ products: [], cvss_v3: { base_score: c.cvss_score || 0 } }],
|
|
784
|
+
threats: c.active_exploitation === 'confirmed' ? [{ category: 'exploit_status', details: 'Active exploitation confirmed (CISA KEV).' }] : [],
|
|
785
|
+
remediations: [{ category: 'vendor_fix', details: validate.selected_remediation?.description || 'See selected remediation path.' }]
|
|
786
|
+
}));
|
|
787
|
+
const indicatorVulns = indicatorHits.map(i => ({
|
|
788
|
+
// Pseudo-CVE id for indicator findings (CSAF requires `cve` or `ids`).
|
|
789
|
+
ids: [{ system_name: 'exceptd-indicator', text: `${playbook._meta.id}:${i.id}` }],
|
|
790
|
+
notes: [{ category: 'description', text: `Indicator ${i.id} fired (${i.confidence}${i.deterministic ? ' / deterministic' : ''}) in playbook ${playbook._meta.id}.` }],
|
|
791
|
+
remediations: [{ category: 'mitigation', details: validate.selected_remediation?.description || `Consult playbook brief: exceptd brief ${playbook._meta.id}.` }],
|
|
792
|
+
}));
|
|
747
793
|
return {
|
|
748
794
|
document: {
|
|
749
795
|
category: 'csaf_security_advisory',
|
|
750
796
|
csaf_version: '2.0',
|
|
751
797
|
publisher: { category: 'vendor', name: 'exceptd', namespace: 'https://exceptd.com' },
|
|
752
|
-
title: `exceptd finding: ${playbook.domain.name} (${analyze.matched_cves.length}
|
|
798
|
+
title: `exceptd finding: ${playbook.domain.name} (${analyze.matched_cves.length} CVE(s), ${indicatorHits.length} indicator hit(s))`,
|
|
753
799
|
tracking: {
|
|
754
800
|
id: `exceptd-${playbook._meta.id}-${Date.now()}`,
|
|
755
801
|
status: 'final',
|
|
@@ -758,82 +804,109 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals)
|
|
|
758
804
|
revision_history: [{ number: '1', date: new Date().toISOString(), summary: 'Initial finding emission' }]
|
|
759
805
|
}
|
|
760
806
|
},
|
|
761
|
-
vulnerabilities:
|
|
762
|
-
cve: c.cve_id,
|
|
763
|
-
scores: [{ products: [], cvss_v3: { base_score: c.cvss_score || 0 } }],
|
|
764
|
-
threats: c.active_exploitation === 'confirmed' ? [{ category: 'exploit_status', details: 'Active exploitation confirmed (CISA KEV).' }] : [],
|
|
765
|
-
remediations: [{ category: 'vendor_fix', details: validate.selected_remediation?.description || 'See selected remediation path.' }]
|
|
766
|
-
})),
|
|
807
|
+
vulnerabilities: [...cveVulns, ...indicatorVulns],
|
|
767
808
|
exceptd_extension: {
|
|
809
|
+
classification: analyze._detect_classification,
|
|
768
810
|
rwep: analyze.rwep,
|
|
769
811
|
blast_radius_score: analyze.blast_radius_score,
|
|
770
812
|
compliance_theater: analyze.compliance_theater_check,
|
|
771
813
|
framework_gap_mapping: analyze.framework_gap_mapping,
|
|
772
814
|
evidence_requirements: validate.evidence_requirements,
|
|
773
|
-
residual_risk_statement: validate.residual_risk_statement
|
|
815
|
+
residual_risk_statement: validate.residual_risk_statement,
|
|
816
|
+
indicators_fired: indicatorHits.map(i => ({ id: i.id, confidence: i.confidence, deterministic: i.deterministic })),
|
|
774
817
|
}
|
|
775
818
|
};
|
|
776
819
|
}
|
|
777
820
|
|
|
778
821
|
// SARIF 2.1.0 — GitHub Code Scanning / VS Code SARIF Viewer / Azure DevOps
|
|
779
|
-
//
|
|
780
|
-
//
|
|
781
|
-
//
|
|
822
|
+
// / most static-analysis tooling.
|
|
823
|
+
//
|
|
824
|
+
// v0.11.5 (#82): emit results from BOTH matched_cves AND fired indicators.
|
|
825
|
+
// Pre-0.11.5 we emitted only matched_cves, which produced an empty bundle
|
|
826
|
+
// for playbooks like crypto-codebase / library-author whose domain.cve_refs
|
|
827
|
+
// is intentionally empty (the playbook checks process/posture, not catalog
|
|
828
|
+
// CVEs). Indicators that fire (verdict: hit) and framework gaps are now
|
|
829
|
+
// first-class SARIF results — a clean run still emits a usable bundle.
|
|
782
830
|
if (format === 'sarif' || format === 'sarif-2.1.0') {
|
|
831
|
+
const cveResults = analyze.matched_cves.map(c => ({
|
|
832
|
+
ruleId: c.cve_id,
|
|
833
|
+
level: c.rwep >= 90 ? 'error' : c.rwep >= 70 ? 'warning' : 'note',
|
|
834
|
+
message: { text: `${c.cve_id}: RWEP ${c.rwep}, blast_radius ${analyze.blast_radius_score}. ${validate.selected_remediation?.description || ''}` },
|
|
835
|
+
properties: {
|
|
836
|
+
kind: 'cve_match',
|
|
837
|
+
rwep: c.rwep, cisa_kev: c.cisa_kev, cisa_kev_due_date: c.cisa_kev_due_date,
|
|
838
|
+
active_exploitation: c.active_exploitation, ai_discovered: c.ai_discovered,
|
|
839
|
+
blast_radius_score: analyze.blast_radius_score,
|
|
840
|
+
}
|
|
841
|
+
}));
|
|
842
|
+
const indicatorHits = (analyze._detect_indicators || []).filter(i => i.verdict === 'hit');
|
|
843
|
+
const indicatorResults = indicatorHits.map(i => ({
|
|
844
|
+
ruleId: i.id,
|
|
845
|
+
level: i.deterministic ? 'error' : (i.confidence === 'high' ? 'warning' : 'note'),
|
|
846
|
+
message: { text: `Indicator ${i.id} fired (${i.confidence}${i.deterministic ? ' / deterministic' : ''}). Playbook: ${playbook._meta.id}.` },
|
|
847
|
+
properties: { kind: 'indicator_hit', confidence: i.confidence, deterministic: i.deterministic, atlas_ref: i.atlas_ref, attack_ref: i.attack_ref },
|
|
848
|
+
}));
|
|
849
|
+
const gapResults = (analyze.framework_gap_mapping || []).map((g, idx) => ({
|
|
850
|
+
ruleId: `framework-gap-${idx}`,
|
|
851
|
+
level: 'note',
|
|
852
|
+
message: { text: `${g.framework}: ${g.claimed_control} — ${g.actual_gap}${g.required_control ? '. Required: ' + g.required_control : ''}` },
|
|
853
|
+
properties: { kind: 'framework_gap', framework: g.framework, control: g.claimed_control },
|
|
854
|
+
}));
|
|
855
|
+
const cveRules = analyze.matched_cves.map(c => ({
|
|
856
|
+
id: c.cve_id, shortDescription: { text: c.cve_id },
|
|
857
|
+
fullDescription: { text: `RWEP ${c.rwep} · KEV=${c.cisa_kev} · active_exploitation=${c.active_exploitation}` },
|
|
858
|
+
defaultConfiguration: { level: c.rwep >= 90 ? 'error' : c.rwep >= 70 ? 'warning' : 'note' },
|
|
859
|
+
helpUri: `https://nvd.nist.gov/vuln/detail/${c.cve_id}`,
|
|
860
|
+
}));
|
|
861
|
+
const indicatorRules = indicatorHits.map(i => ({
|
|
862
|
+
id: i.id, shortDescription: { text: i.id },
|
|
863
|
+
fullDescription: { text: `Indicator from playbook ${playbook._meta.id}. Type: ${i.type}. Confidence: ${i.confidence}.` },
|
|
864
|
+
defaultConfiguration: { level: i.deterministic ? 'error' : (i.confidence === 'high' ? 'warning' : 'note') },
|
|
865
|
+
}));
|
|
783
866
|
return {
|
|
784
867
|
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
785
868
|
version: '2.1.0',
|
|
786
869
|
runs: [{
|
|
787
|
-
tool: {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
helpUri: `https://nvd.nist.gov/vuln/detail/${c.cve_id}`,
|
|
798
|
-
}))
|
|
799
|
-
}
|
|
800
|
-
},
|
|
801
|
-
results: analyze.matched_cves.map(c => ({
|
|
802
|
-
ruleId: c.cve_id,
|
|
803
|
-
level: c.rwep >= 90 ? 'error' : c.rwep >= 70 ? 'warning' : 'note',
|
|
804
|
-
message: { text: `${c.cve_id}: RWEP ${c.rwep}, blast_radius ${analyze.blast_radius_score}. ${validate.selected_remediation?.description || ''}` },
|
|
805
|
-
properties: {
|
|
806
|
-
rwep: c.rwep,
|
|
807
|
-
cisa_kev: c.cisa_kev,
|
|
808
|
-
cisa_kev_due_date: c.cisa_kev_due_date,
|
|
809
|
-
active_exploitation: c.active_exploitation,
|
|
810
|
-
ai_discovered: c.ai_discovered,
|
|
811
|
-
blast_radius_score: analyze.blast_radius_score,
|
|
812
|
-
framework_gaps: analyze.framework_gap_mapping?.length || 0,
|
|
813
|
-
}
|
|
814
|
-
}))
|
|
870
|
+
tool: { driver: {
|
|
871
|
+
name: 'exceptd', version: playbook._meta.version, informationUri: 'https://exceptd.com',
|
|
872
|
+
rules: [...cveRules, ...indicatorRules],
|
|
873
|
+
} },
|
|
874
|
+
results: [...cveResults, ...indicatorResults, ...gapResults],
|
|
875
|
+
invocations: [{ executionSuccessful: true, properties: {
|
|
876
|
+
playbook: playbook._meta.id, classification: analyze._detect_classification || 'unknown',
|
|
877
|
+
rwep_adjusted: analyze.rwep?.adjusted || 0,
|
|
878
|
+
remediation: validate.selected_remediation?.id || null,
|
|
879
|
+
} }],
|
|
815
880
|
}]
|
|
816
881
|
};
|
|
817
882
|
}
|
|
818
883
|
|
|
819
|
-
// OpenVEX 0.2.0 — supply-chain VEX statements.
|
|
820
|
-
//
|
|
821
|
-
//
|
|
884
|
+
// OpenVEX 0.2.0 — supply-chain VEX statements. v0.11.5 (#82): also include
|
|
885
|
+
// statements derived from fired indicators (treated as advisory findings)
|
|
886
|
+
// so playbooks with empty cve_refs still emit a meaningful bundle.
|
|
822
887
|
if (format === 'openvex' || format === 'openvex-0.2.0') {
|
|
823
888
|
const issued = new Date().toISOString();
|
|
889
|
+
const cveStatements = analyze.matched_cves.map(c => ({
|
|
890
|
+
vulnerability: { '@id': c.cve_id, name: c.cve_id },
|
|
891
|
+
status: c.active_exploitation === 'confirmed' ? 'under_investigation' : (c.live_patch_available ? 'fixed' : 'affected'),
|
|
892
|
+
timestamp: issued,
|
|
893
|
+
action_statement: validate.selected_remediation?.description || null,
|
|
894
|
+
impact_statement: `RWEP ${c.rwep}. Blast radius ${analyze.blast_radius_score}/5.`
|
|
895
|
+
}));
|
|
896
|
+
const indicatorStatements = (analyze._detect_indicators || []).filter(i => i.verdict === 'hit').map(i => ({
|
|
897
|
+
vulnerability: { '@id': `exceptd:${playbook._meta.id}:${i.id}`, name: i.id },
|
|
898
|
+
status: 'under_investigation',
|
|
899
|
+
timestamp: issued,
|
|
900
|
+
action_statement: validate.selected_remediation?.description || `Run \`exceptd brief ${playbook._meta.id}\` for context.`,
|
|
901
|
+
impact_statement: `Indicator ${i.id} fired (${i.confidence}${i.deterministic ? '/deterministic' : ''}) in playbook ${playbook._meta.id}.`,
|
|
902
|
+
}));
|
|
824
903
|
return {
|
|
825
904
|
'@context': 'https://openvex.dev/ns/v0.2.0',
|
|
826
905
|
'@id': `https://exceptd.com/vex/${playbook._meta.id}/${Date.now()}`,
|
|
827
906
|
author: 'exceptd',
|
|
828
907
|
timestamp: issued,
|
|
829
908
|
version: 1,
|
|
830
|
-
statements:
|
|
831
|
-
vulnerability: { '@id': c.cve_id, name: c.cve_id },
|
|
832
|
-
status: c.active_exploitation === 'confirmed' ? 'under_investigation' : (c.live_patch_available ? 'fixed' : 'affected'),
|
|
833
|
-
timestamp: issued,
|
|
834
|
-
action_statement: validate.selected_remediation?.description || null,
|
|
835
|
-
impact_statement: `RWEP ${c.rwep}. Blast radius ${analyze.blast_radius_score}/5.`
|
|
836
|
-
}))
|
|
909
|
+
statements: [...cveStatements, ...indicatorStatements],
|
|
837
910
|
};
|
|
838
911
|
}
|
|
839
912
|
|
|
@@ -920,6 +993,20 @@ function normalizeSubmission(submission, playbook) {
|
|
|
920
993
|
const knownPreconditions = new Set((playbook?._meta?.preconditions || []).map(p => p.id));
|
|
921
994
|
const knownArtifacts = new Set((playbook?.phases?.look?.artifacts || []).map(a => a.id));
|
|
922
995
|
|
|
996
|
+
// v0.11.4 (#71): canonicalize indicator outcome strings here too so the
|
|
997
|
+
// signal_overrides object handed to detect() carries the runner's expected
|
|
998
|
+
// hit|miss|inconclusive vocabulary regardless of what the operator typed.
|
|
999
|
+
const canonicalizeOutcome = (v) => {
|
|
1000
|
+
if (v === true || v === 'hit' || v === 'detected' || v === 'positive') return 'hit';
|
|
1001
|
+
if (v === false || v === 'miss' || v === 'no_hit' || v === 'no-hit' || v === 'clean' || v === 'clear' || v === 'not_hit' || v === 'ok' || v === 'pass' || v === 'negative') return 'miss';
|
|
1002
|
+
if (v === 'inconclusive' || v === 'unknown' || v === 'unverified' || v === null) return 'inconclusive';
|
|
1003
|
+
return v; // leave unrecognized values for detect() to decide
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
// v0.11.5 (#85): track which observation produced each signal_override so
|
|
1007
|
+
// detect can emit `from_observation` on each indicator result. Diagnostic
|
|
1008
|
+
// value for operators chasing "which observation drove this verdict".
|
|
1009
|
+
out._signal_origins = out._signal_origins || {};
|
|
923
1010
|
for (const [key, val] of Object.entries(submission.observations || {})) {
|
|
924
1011
|
if (knownPreconditions.has(key)) {
|
|
925
1012
|
out.precondition_checks[key] = val === "ok" || val === true || val === "true";
|
|
@@ -928,7 +1015,10 @@ function normalizeSubmission(submission, playbook) {
|
|
|
928
1015
|
if (typeof val === "object" && val !== null) {
|
|
929
1016
|
const aid = knownArtifacts.has(key) ? key : (val.artifact || key);
|
|
930
1017
|
out.artifacts[aid] = { value: val.value, captured: val.captured !== false };
|
|
931
|
-
if (val.indicator && val.result
|
|
1018
|
+
if (val.indicator && val.result !== undefined) {
|
|
1019
|
+
out.signal_overrides[val.indicator] = canonicalizeOutcome(val.result);
|
|
1020
|
+
out._signal_origins[val.indicator] = key;
|
|
1021
|
+
}
|
|
932
1022
|
}
|
|
933
1023
|
}
|
|
934
1024
|
|
package/manifest-snapshot.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Auto-generated by scripts/refresh-manifest-snapshot.js — do not hand-edit. Public skill surface used by check-manifest-snapshot.js to detect breaking removals.",
|
|
3
|
-
"_generated_at": "2026-05-
|
|
3
|
+
"_generated_at": "2026-05-12T16:58:59.224Z",
|
|
4
4
|
"atlas_version": "5.1.0",
|
|
5
5
|
"skill_count": 38,
|
|
6
6
|
"skills": [
|
package/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exceptd-security",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.5",
|
|
4
4
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation",
|
|
5
5
|
"homepage": "https://exceptd.com",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
],
|
|
53
53
|
"last_threat_review": "2026-05-01",
|
|
54
54
|
"signature": "WprHkO1KOjQtCBj6/EJghBTNyNKJhn7O2HDbAQZPi5jn4flwHpSrtP8LC15a4Unoh+xiIIgGhvTHZIQFHGMpBQ==",
|
|
55
|
-
"signed_at": "2026-05-
|
|
55
|
+
"signed_at": "2026-05-12T16:58:58.659Z",
|
|
56
56
|
"cwe_refs": [
|
|
57
57
|
"CWE-125",
|
|
58
58
|
"CWE-362",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
],
|
|
117
117
|
"last_threat_review": "2026-05-01",
|
|
118
118
|
"signature": "fg20bOXGRkPUdLmegeXpTM4hnzl/ArgcVc88rItZN5DdsnFnzPgUU1PwCI82zooyj2GfxJHYjxNkq5qd2zNPBg==",
|
|
119
|
-
"signed_at": "2026-05-
|
|
119
|
+
"signed_at": "2026-05-12T16:58:58.661Z",
|
|
120
120
|
"cwe_refs": [
|
|
121
121
|
"CWE-1039",
|
|
122
122
|
"CWE-1426",
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
],
|
|
180
180
|
"last_threat_review": "2026-05-01",
|
|
181
181
|
"signature": "6JuSzkSSFzFHEZ3ANzqjtIbKPOkwJeKhQ+8WAPB4+dTRvDSeg46n3D88XfGaNd2z7pmg/i8p9ZoImQcHFS4BCg==",
|
|
182
|
-
"signed_at": "2026-05-
|
|
182
|
+
"signed_at": "2026-05-12T16:58:58.662Z",
|
|
183
183
|
"cwe_refs": [
|
|
184
184
|
"CWE-22",
|
|
185
185
|
"CWE-345",
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
"framework_gaps": [],
|
|
226
226
|
"last_threat_review": "2026-05-01",
|
|
227
227
|
"signature": "PYSw9abiYfW+y7IkY8udJG5LSds2a4rMimlw3rrdD0zE3vunEeV/y7oTmDD4o83OqHSCKNzF/7vMhvd/noqICQ==",
|
|
228
|
-
"signed_at": "2026-05-
|
|
228
|
+
"signed_at": "2026-05-12T16:58:58.662Z"
|
|
229
229
|
},
|
|
230
230
|
{
|
|
231
231
|
"name": "compliance-theater",
|
|
@@ -256,7 +256,7 @@
|
|
|
256
256
|
],
|
|
257
257
|
"last_threat_review": "2026-05-01",
|
|
258
258
|
"signature": "BMFmmJYP3HsHIjUqnhw8E3MiMGZJsI/eDq51we+nxUicZ8nFUQT9DhmRntAqOs6BUnsfiQNNLc/rrsNh8yg1CQ==",
|
|
259
|
-
"signed_at": "2026-05-
|
|
259
|
+
"signed_at": "2026-05-12T16:58:58.663Z"
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"name": "exploit-scoring",
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
],
|
|
286
286
|
"last_threat_review": "2026-05-01",
|
|
287
287
|
"signature": "VGPyDwy5BRlpn1lZthhPB6ytb4ZcU2j0KtCZbaMkyLdMugQJtK2yEuwrsDH4yEtAhTB6/A4B3eSygJckum49Ag==",
|
|
288
|
-
"signed_at": "2026-05-
|
|
288
|
+
"signed_at": "2026-05-12T16:58:58.663Z"
|
|
289
289
|
},
|
|
290
290
|
{
|
|
291
291
|
"name": "rag-pipeline-security",
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
],
|
|
323
323
|
"last_threat_review": "2026-05-01",
|
|
324
324
|
"signature": "XkFGpsNnXBVslkQ48usEu9l1LjPiV2ppW+M4B63zXFBP2Puh52qYCffEPjUHYhoO5bjgTM7yCbK8XF/Dzk5wBw==",
|
|
325
|
-
"signed_at": "2026-05-
|
|
325
|
+
"signed_at": "2026-05-12T16:58:58.663Z",
|
|
326
326
|
"cwe_refs": [
|
|
327
327
|
"CWE-1395",
|
|
328
328
|
"CWE-1426"
|
|
@@ -379,7 +379,7 @@
|
|
|
379
379
|
],
|
|
380
380
|
"last_threat_review": "2026-05-01",
|
|
381
381
|
"signature": "1Xqy7Kxxy6GpTvuYJPdllPzVDRFxb7N6AuxKuoaO4v91CiZLmiXt0sTIWImKJ3p9Eup6rJNDdsY71dolFhHNBA==",
|
|
382
|
-
"signed_at": "2026-05-
|
|
382
|
+
"signed_at": "2026-05-12T16:58:58.664Z",
|
|
383
383
|
"d3fend_refs": [
|
|
384
384
|
"D3-CA",
|
|
385
385
|
"D3-CSPP",
|
|
@@ -414,7 +414,7 @@
|
|
|
414
414
|
"framework_gaps": [],
|
|
415
415
|
"last_threat_review": "2026-05-01",
|
|
416
416
|
"signature": "QNLOmAL54S/Cmk4cdO4L2BCGkqZ/FgY4UBsKWtg/EEW+YXF5ev+a8XsUT8q5veuUa2VYcYna7rD1iAnE+2PDBA==",
|
|
417
|
-
"signed_at": "2026-05-
|
|
417
|
+
"signed_at": "2026-05-12T16:58:58.664Z",
|
|
418
418
|
"cwe_refs": [
|
|
419
419
|
"CWE-1188"
|
|
420
420
|
]
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
"framework_gaps": [],
|
|
443
443
|
"last_threat_review": "2026-05-01",
|
|
444
444
|
"signature": "aFHq4cSl3CKchnVITxx+BrAEWD33WtFFJoQtwAug5g9R3/3ABtjaXYGVQaZcdcG1AIZkMoGSPywgLQWDY7ZDCw==",
|
|
445
|
-
"signed_at": "2026-05-
|
|
445
|
+
"signed_at": "2026-05-12T16:58:58.664Z"
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
"name": "global-grc",
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
"framework_gaps": [],
|
|
475
475
|
"last_threat_review": "2026-05-01",
|
|
476
476
|
"signature": "viCTUWdy6euvd2KTAo6sLvarK/FZkDtYGocxBt0H+fY94kLQGW8K5cSpqIWdUF5NUytSHBCiG4YcSze8P9Z/BQ==",
|
|
477
|
-
"signed_at": "2026-05-
|
|
477
|
+
"signed_at": "2026-05-12T16:58:58.665Z"
|
|
478
478
|
},
|
|
479
479
|
{
|
|
480
480
|
"name": "zeroday-gap-learn",
|
|
@@ -501,7 +501,7 @@
|
|
|
501
501
|
"framework_gaps": [],
|
|
502
502
|
"last_threat_review": "2026-05-01",
|
|
503
503
|
"signature": "6PkUaHQi3Hxuqq/Jp4GYckvfqVEofmeT87NUH0T+pwyjlc+xZkoqNPn65f7ldciEPL86JIPi3/dDTKQbIFFBCw==",
|
|
504
|
-
"signed_at": "2026-05-
|
|
504
|
+
"signed_at": "2026-05-12T16:58:58.665Z"
|
|
505
505
|
},
|
|
506
506
|
{
|
|
507
507
|
"name": "pqc-first",
|
|
@@ -553,7 +553,7 @@
|
|
|
553
553
|
],
|
|
554
554
|
"last_threat_review": "2026-05-01",
|
|
555
555
|
"signature": "ZenFTEzWx+DzrSXlNXhbZ70vOdJSXfrnKkAwqMlBf5nlDf38V1/hG4XCKj43snQXWr4mVJOX6ilqFLTYNIjnBw==",
|
|
556
|
-
"signed_at": "2026-05-
|
|
556
|
+
"signed_at": "2026-05-12T16:58:58.665Z",
|
|
557
557
|
"cwe_refs": [
|
|
558
558
|
"CWE-327"
|
|
559
559
|
],
|
|
@@ -600,7 +600,7 @@
|
|
|
600
600
|
],
|
|
601
601
|
"last_threat_review": "2026-05-01",
|
|
602
602
|
"signature": "ih0vpd2v2zS31JSJv7SnABoya8JlJdrXZXx4rBnrsV3Assj+dbjAP0pQ1HMT/5RX8yTTswRQsg0bJV3qmbJ3Bw==",
|
|
603
|
-
"signed_at": "2026-05-
|
|
603
|
+
"signed_at": "2026-05-12T16:58:58.666Z"
|
|
604
604
|
},
|
|
605
605
|
{
|
|
606
606
|
"name": "security-maturity-tiers",
|
|
@@ -637,7 +637,7 @@
|
|
|
637
637
|
],
|
|
638
638
|
"last_threat_review": "2026-05-01",
|
|
639
639
|
"signature": "Lv8dHiwIqUbNsywCCB/+pYWGF+MHCvxVn1IAvR7Cnif5fy0sICv0N4SVsSb621qAAkHNshpfxqwuhbuQnE1TBA==",
|
|
640
|
-
"signed_at": "2026-05-
|
|
640
|
+
"signed_at": "2026-05-12T16:58:58.666Z",
|
|
641
641
|
"cwe_refs": [
|
|
642
642
|
"CWE-1188"
|
|
643
643
|
]
|
|
@@ -672,7 +672,7 @@
|
|
|
672
672
|
"framework_gaps": [],
|
|
673
673
|
"last_threat_review": "2026-05-11",
|
|
674
674
|
"signature": "BS+wrL28HHYhBpe+v84VLoq9KPBXu6alfG968katfGIoLNYQueaHP931bRmlkrjfeb6qbDf067GWdPEh7nroAw==",
|
|
675
|
-
"signed_at": "2026-05-
|
|
675
|
+
"signed_at": "2026-05-12T16:58:58.666Z"
|
|
676
676
|
},
|
|
677
677
|
{
|
|
678
678
|
"name": "attack-surface-pentest",
|
|
@@ -743,7 +743,7 @@
|
|
|
743
743
|
"PTES revision incorporating AI-surface enumeration"
|
|
744
744
|
],
|
|
745
745
|
"signature": "vLhIYT/CC3IzxMRa+UPeqGSZTvthuwUeTMGNFMm37+TaEk0TtfwPrPyrBJLHw4W6Wt7+pufjHs46X3nTgzoRAg==",
|
|
746
|
-
"signed_at": "2026-05-
|
|
746
|
+
"signed_at": "2026-05-12T16:58:58.667Z"
|
|
747
747
|
},
|
|
748
748
|
{
|
|
749
749
|
"name": "fuzz-testing-strategy",
|
|
@@ -803,7 +803,7 @@
|
|
|
803
803
|
"OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
|
|
804
804
|
],
|
|
805
805
|
"signature": "TOcQLy/427cuf0Lw90J7A0oIeuhUmf9NXb6tOUS5K3SazCKTJujPgYSVAPZOYf1zZrRAY/aq0iqELd5cLyk5DA==",
|
|
806
|
-
"signed_at": "2026-05-
|
|
806
|
+
"signed_at": "2026-05-12T16:58:58.667Z"
|
|
807
807
|
},
|
|
808
808
|
{
|
|
809
809
|
"name": "dlp-gap-analysis",
|
|
@@ -878,7 +878,7 @@
|
|
|
878
878
|
"Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
|
|
879
879
|
],
|
|
880
880
|
"signature": "u4IN7escQa5V+OgdtaJXLdvhmNiGZsdmGOvebTLZ30WoImT+WiksvaqSa0POGdbr6HzFkALe2RrZEH9Tr0U6Dg==",
|
|
881
|
-
"signed_at": "2026-05-
|
|
881
|
+
"signed_at": "2026-05-12T16:58:58.667Z"
|
|
882
882
|
},
|
|
883
883
|
{
|
|
884
884
|
"name": "supply-chain-integrity",
|
|
@@ -955,7 +955,7 @@
|
|
|
955
955
|
"OpenSSF model-signing — emerging Sigstore-based signing standard for ML model weights; track for production adoption"
|
|
956
956
|
],
|
|
957
957
|
"signature": "eTGQJ3gnG24WggfwuFNNIFOWV/ttPxTa3pvx9OH28m5KDS1a4ZmOR7K8y01wk/su8bH0ClYYRfoBfKQOtRswAg==",
|
|
958
|
-
"signed_at": "2026-05-
|
|
958
|
+
"signed_at": "2026-05-12T16:58:58.667Z"
|
|
959
959
|
},
|
|
960
960
|
{
|
|
961
961
|
"name": "defensive-countermeasure-mapping",
|
|
@@ -1012,7 +1012,7 @@
|
|
|
1012
1012
|
],
|
|
1013
1013
|
"last_threat_review": "2026-05-11",
|
|
1014
1014
|
"signature": "q7gFLPoqf/8bqATR6gt/nj0EoyUOlfzi+bZ0bT3pC9KW7O6M/ji9fT+AXSGNp6PKd+70ACb3mkMGmWgjLpQXCg==",
|
|
1015
|
-
"signed_at": "2026-05-
|
|
1015
|
+
"signed_at": "2026-05-12T16:58:58.668Z"
|
|
1016
1016
|
},
|
|
1017
1017
|
{
|
|
1018
1018
|
"name": "identity-assurance",
|
|
@@ -1079,7 +1079,7 @@
|
|
|
1079
1079
|
"d3fend_refs": [],
|
|
1080
1080
|
"last_threat_review": "2026-05-11",
|
|
1081
1081
|
"signature": "pX8rhrrzuyG3iRrPORLqTZAjzGdWK/bKPUGJG5WHSZcv4LB0kQXOit4sHG0exdXxI6HY8jyX67QY4r5vEHHACw==",
|
|
1082
|
-
"signed_at": "2026-05-
|
|
1082
|
+
"signed_at": "2026-05-12T16:58:58.668Z"
|
|
1083
1083
|
},
|
|
1084
1084
|
{
|
|
1085
1085
|
"name": "ot-ics-security",
|
|
@@ -1135,7 +1135,7 @@
|
|
|
1135
1135
|
"d3fend_refs": [],
|
|
1136
1136
|
"last_threat_review": "2026-05-11",
|
|
1137
1137
|
"signature": "ypb8kNZQRdyu5mWeveB7sjCjNKXS1yXvjDJv88muzwhOs/a4Fu/Gb532js5NKyy+eCw/emrphpTZaL8R9a2lBA==",
|
|
1138
|
-
"signed_at": "2026-05-
|
|
1138
|
+
"signed_at": "2026-05-12T16:58:58.668Z"
|
|
1139
1139
|
},
|
|
1140
1140
|
{
|
|
1141
1141
|
"name": "coordinated-vuln-disclosure",
|
|
@@ -1187,7 +1187,7 @@
|
|
|
1187
1187
|
"NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
|
|
1188
1188
|
],
|
|
1189
1189
|
"signature": "346Lt+277ycRNsyAOGwLSONi4awgxKy3hP9G+BWjwaa8ySmTeqbYsbyyhtxjeohk9bV2SF+Hl2q4JdSvc/2qCQ==",
|
|
1190
|
-
"signed_at": "2026-05-
|
|
1190
|
+
"signed_at": "2026-05-12T16:58:58.668Z"
|
|
1191
1191
|
},
|
|
1192
1192
|
{
|
|
1193
1193
|
"name": "threat-modeling-methodology",
|
|
@@ -1237,7 +1237,7 @@
|
|
|
1237
1237
|
"PASTA v2 updates incorporating AI/ML application threats"
|
|
1238
1238
|
],
|
|
1239
1239
|
"signature": "ewTvG5vu3ngFHyXgBur5vSKDFQsOZx0x79djGMricl7LCvQf5//OG6LZKXa+AOuEq58prRS+HgzrFA1DiTfeCQ==",
|
|
1240
|
-
"signed_at": "2026-05-
|
|
1240
|
+
"signed_at": "2026-05-12T16:58:58.669Z"
|
|
1241
1241
|
},
|
|
1242
1242
|
{
|
|
1243
1243
|
"name": "webapp-security",
|
|
@@ -1311,7 +1311,7 @@
|
|
|
1311
1311
|
"d3fend_refs": [],
|
|
1312
1312
|
"last_threat_review": "2026-05-11",
|
|
1313
1313
|
"signature": "ZHjbKu0Em92Kimr2esL1g93mf9TmcsChBhVEMWf/lFrjeLcg8nyHEIcDstIZ3FWYgc6MQNHnc3Rup3Xp/Za1Cw==",
|
|
1314
|
-
"signed_at": "2026-05-
|
|
1314
|
+
"signed_at": "2026-05-12T16:58:58.669Z"
|
|
1315
1315
|
},
|
|
1316
1316
|
{
|
|
1317
1317
|
"name": "ai-risk-management",
|
|
@@ -1361,7 +1361,7 @@
|
|
|
1361
1361
|
"d3fend_refs": [],
|
|
1362
1362
|
"last_threat_review": "2026-05-11",
|
|
1363
1363
|
"signature": "1KRxjCbAX0Rs5NTOioi1w/f1SOzDQrtRoXjTDtzEwJ+d1QzFf9cqmBlp0uXmGpL0bzEaHWIctjigSychmoL2Dw==",
|
|
1364
|
-
"signed_at": "2026-05-
|
|
1364
|
+
"signed_at": "2026-05-12T16:58:58.670Z"
|
|
1365
1365
|
},
|
|
1366
1366
|
{
|
|
1367
1367
|
"name": "sector-healthcare",
|
|
@@ -1421,7 +1421,7 @@
|
|
|
1421
1421
|
"d3fend_refs": [],
|
|
1422
1422
|
"last_threat_review": "2026-05-11",
|
|
1423
1423
|
"signature": "eiajFh7w7d4g+/crGalTtw9Qsu0deVsdHkdthZSy595ifGmgu0zaFD8usKThbPhOdUCCclTYkZYz5GalQmkhCw==",
|
|
1424
|
-
"signed_at": "2026-05-
|
|
1424
|
+
"signed_at": "2026-05-12T16:58:58.670Z"
|
|
1425
1425
|
},
|
|
1426
1426
|
{
|
|
1427
1427
|
"name": "sector-financial",
|
|
@@ -1502,7 +1502,7 @@
|
|
|
1502
1502
|
"TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
|
|
1503
1503
|
],
|
|
1504
1504
|
"signature": "iSZR/fYESQVyjkcqj+O+yzU0BQfaELH5s7WizzUTWvDPDTD2ZyOnZTT1r/Zfx2l4mbPmVeFGWdYnnVFTk/i3Aw==",
|
|
1505
|
-
"signed_at": "2026-05-
|
|
1505
|
+
"signed_at": "2026-05-12T16:58:58.670Z"
|
|
1506
1506
|
},
|
|
1507
1507
|
{
|
|
1508
1508
|
"name": "sector-federal-government",
|
|
@@ -1571,7 +1571,7 @@
|
|
|
1571
1571
|
"Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
|
|
1572
1572
|
],
|
|
1573
1573
|
"signature": "Wjdo5YXEL8XeNZkaEueG1DOUoyalstNPzQkxD/cwP5iMrJWg/Ly+sC0Oluuqm3aU7d63z55PrbGQCJD0XVZqBg==",
|
|
1574
|
-
"signed_at": "2026-05-
|
|
1574
|
+
"signed_at": "2026-05-12T16:58:58.671Z"
|
|
1575
1575
|
},
|
|
1576
1576
|
{
|
|
1577
1577
|
"name": "sector-energy",
|
|
@@ -1636,7 +1636,7 @@
|
|
|
1636
1636
|
"ICS-CERT advisory feed (https://www.cisa.gov/news-events/cybersecurity-advisories/ics-advisories) for vendor CVEs in Siemens, Rockwell, Schneider Electric, ABB, GE Vernova, Hitachi Energy, AVEVA / OSIsoft PI"
|
|
1637
1637
|
],
|
|
1638
1638
|
"signature": "c/l7dOHe0Zj6Ag3abUaEie6o0f8M4rhY5aPI9/wG4z6FDue9PzCVw8vUGoITFgg89g97lMfy2C3CE2PegQoFCw==",
|
|
1639
|
-
"signed_at": "2026-05-
|
|
1639
|
+
"signed_at": "2026-05-12T16:58:58.671Z"
|
|
1640
1640
|
},
|
|
1641
1641
|
{
|
|
1642
1642
|
"name": "api-security",
|
|
@@ -1705,7 +1705,7 @@
|
|
|
1705
1705
|
"d3fend_refs": [],
|
|
1706
1706
|
"last_threat_review": "2026-05-11",
|
|
1707
1707
|
"signature": "9FgcJvYeo07QxQ+mnVRQk4jYLDMO/AVSXMs8cueO2f/qMOTQmrhBMVhj5ze7hzvXpGkp7EK/3Q1XKqde61JMAg==",
|
|
1708
|
-
"signed_at": "2026-05-
|
|
1708
|
+
"signed_at": "2026-05-12T16:58:58.671Z"
|
|
1709
1709
|
},
|
|
1710
1710
|
{
|
|
1711
1711
|
"name": "cloud-security",
|
|
@@ -1786,7 +1786,7 @@
|
|
|
1786
1786
|
"CISA KEV additions for cloud-control-plane CVEs (IMDSv1 abuses, federation token mishandling, cross-tenant boundary failures); CISA Cybersecurity Advisories for cross-cloud advisories"
|
|
1787
1787
|
],
|
|
1788
1788
|
"signature": "xRA0XZf7VPtuBtbsm41bay9yBLphw/hlL3YxIUrpko5g9ldM3oJe9o1qSwzIj/wSnQSI29qqPpNsnlks+HEOCA==",
|
|
1789
|
-
"signed_at": "2026-05-
|
|
1789
|
+
"signed_at": "2026-05-12T16:58:58.672Z"
|
|
1790
1790
|
},
|
|
1791
1791
|
{
|
|
1792
1792
|
"name": "container-runtime-security",
|
|
@@ -1848,7 +1848,7 @@
|
|
|
1848
1848
|
"d3fend_refs": [],
|
|
1849
1849
|
"last_threat_review": "2026-05-11",
|
|
1850
1850
|
"signature": "GcU50DStuN1gU/Evm/sFRgeieQbqffVp12rgbGnasRX89Q7kM4ltFXB+bgCXHIvICzYb78hPIifWQb9UVupWBQ==",
|
|
1851
|
-
"signed_at": "2026-05-
|
|
1851
|
+
"signed_at": "2026-05-12T16:58:58.672Z"
|
|
1852
1852
|
},
|
|
1853
1853
|
{
|
|
1854
1854
|
"name": "mlops-security",
|
|
@@ -1919,7 +1919,7 @@
|
|
|
1919
1919
|
"MITRE ATLAS v5.2 — track AML.T0010 sub-technique expansion and any new MLOps-pipeline-specific TTPs"
|
|
1920
1920
|
],
|
|
1921
1921
|
"signature": "onIazpFoL1t4PMNRsoF06ggnl7BzCKjt0x+ZmVfWfyt1V06DgllsrbN3AAz4+g4jW2Sc71q0vIFKfwEUWpGVAQ==",
|
|
1922
|
-
"signed_at": "2026-05-
|
|
1922
|
+
"signed_at": "2026-05-12T16:58:58.672Z"
|
|
1923
1923
|
},
|
|
1924
1924
|
{
|
|
1925
1925
|
"name": "incident-response-playbook",
|
|
@@ -1981,7 +1981,7 @@
|
|
|
1981
1981
|
"NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
|
|
1982
1982
|
],
|
|
1983
1983
|
"signature": "P0Yv4CtqbnBNP6nSIxQUYYHL7T7ci+iE7iE2UXVfnMPeWVdKG2nvRePjBXc3JZTLima1Txn/I5ocDNhLTIeUAQ==",
|
|
1984
|
-
"signed_at": "2026-05-
|
|
1984
|
+
"signed_at": "2026-05-12T16:58:58.673Z"
|
|
1985
1985
|
},
|
|
1986
1986
|
{
|
|
1987
1987
|
"name": "email-security-anti-phishing",
|
|
@@ -2034,7 +2034,7 @@
|
|
|
2034
2034
|
"d3fend_refs": [],
|
|
2035
2035
|
"last_threat_review": "2026-05-11",
|
|
2036
2036
|
"signature": "2pv81lLRbazpHqundCANb3YiLB4lkVsYctIDvI8rxSvHxhPS9jYXqmAoB5APSdDuOaew6XqpfZOehQUj9WmyBw==",
|
|
2037
|
-
"signed_at": "2026-05-
|
|
2037
|
+
"signed_at": "2026-05-12T16:58:58.673Z"
|
|
2038
2038
|
},
|
|
2039
2039
|
{
|
|
2040
2040
|
"name": "age-gates-child-safety",
|
|
@@ -2102,7 +2102,7 @@
|
|
|
2102
2102
|
"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"
|
|
2103
2103
|
],
|
|
2104
2104
|
"signature": "BJ/YYnGVXeSBaR9oWAVrcNX7Wz+kE8R4CghX6+XEI/qY89fyrkKNNwo2veqqf49wffJhHVJ1wTp8ZDECjNp+Dw==",
|
|
2105
|
-
"signed_at": "2026-05-
|
|
2105
|
+
"signed_at": "2026-05-12T16:58:58.673Z"
|
|
2106
2106
|
}
|
|
2107
2107
|
]
|
|
2108
2108
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/exceptd-skills",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.5",
|
|
4
4
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-security",
|
package/sbom.cdx.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.6",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:396b0919-1153-402c-9438-12692eb441ce",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-05-
|
|
7
|
+
"timestamp": "2026-05-12T16:58:59.686Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"name": "hand-written",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.11.
|
|
16
|
+
"bom-ref": "pkg:npm/@blamejs/exceptd-skills@0.11.5",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.11.
|
|
19
|
+
"version": "0.11.5",
|
|
20
20
|
"description": "AI security skills grounded in mid-2026 threat reality, not stale framework documentation. 38 skills, 10 catalogs, 34 jurisdictions, pre-computed indexes, Ed25519-signed.",
|
|
21
21
|
"licenses": [
|
|
22
22
|
{
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.11.
|
|
28
|
+
"purl": "pkg:npm/%40blamejs/exceptd-skills@0.11.5",
|
|
29
29
|
"externalReferences": [
|
|
30
30
|
{
|
|
31
31
|
"type": "distribution",
|
|
32
|
-
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.11.
|
|
32
|
+
"url": "https://www.npmjs.com/package/@blamejs/exceptd-skills/v/0.11.5"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"type": "vcs",
|