@blamejs/exceptd-skills 0.11.5 → 0.11.7
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 +49 -0
- package/README.md +2 -0
- package/bin/exceptd.js +104 -20
- package/data/_indexes/_meta.json +2 -2
- package/keys/public.pem +1 -1
- package/lib/playbook-runner.js +44 -6
- package/manifest-snapshot.json +1 -1
- package/manifest.json +77 -77
- package/orchestrator/index.js +13 -0
- package/package.json +2 -2
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.7 — 2026-05-12
|
|
4
|
+
|
|
5
|
+
**Republish of v0.11.6 (which failed CI publish). Adds CI publish-gate fix.**
|
|
6
|
+
|
|
7
|
+
### CI fix
|
|
8
|
+
|
|
9
|
+
v0.11.6 tag was pushed but the release workflow failed publishing to npm. Root cause: `prepublishOnly` re-ran `predeploy`, which re-ran the Ed25519 signature verify gate. The standalone `Predeploy gate sequence` workflow step had already validated everything with one public key fingerprint (`JX04Vj…`); the second invocation during `npm publish`'s prepublishOnly hook reported a different fingerprint (`M/r52u…`) for the same tracked `keys/public.pem`, causing every skill signature to fail verification.
|
|
10
|
+
|
|
11
|
+
The fingerprint divergence between two same-process invocations of the same binary against the same on-disk file remains unexplained (no script writes to `keys/public.pem` between the two runs). Pragmatic fix: the standalone Predeploy step is the authoritative safety net for CI publishes; the workflow now sets `EXCEPTD_SKIP_PREPUBLISH_PREDEPLOY=1` and prepublishOnly skips its redundant predeploy run. Local `npm publish` invocations still run predeploy because the env var is only set inside the workflow's publish step.
|
|
12
|
+
|
|
13
|
+
### What's in this release
|
|
14
|
+
|
|
15
|
+
All v0.11.6 changes (items 91-98 + 8 new regression tests, 322 total). See [v0.11.6 section](#0116--2026-05-12) below — every fix is identical:
|
|
16
|
+
|
|
17
|
+
- **#91** CSAF + OpenVEX include framework_gap_mapping (was: empty bundles for posture-only playbooks)
|
|
18
|
+
- **#92** CSAF tracking.current_release_date populated (spec §3.2.1.12)
|
|
19
|
+
- **#93** SARIF rule definitions for every referenced ruleId (spec §3.27.3)
|
|
20
|
+
- **#94** lint missing_required_artifact downgraded error → warn (align with runner)
|
|
21
|
+
- **#95** default human-readable output for `attest list` + `lint` on TTY
|
|
22
|
+
- **#96** `--strict-preconditions` flag escalates warn-level preconditions to exit 1
|
|
23
|
+
- **#97** `doctor --fix` runs before JSON early-return (was no-op in `--json` mode)
|
|
24
|
+
- **#98** `attest export` + `report` validate `--format` against accepted set
|
|
25
|
+
|
|
26
|
+
### Workflow improvement
|
|
27
|
+
|
|
28
|
+
Per operator request: README + landing-site updates are now part of every release sequence. README v0.11 section + exceptd.com softwareVersion updated alongside the package version bump.
|
|
29
|
+
|
|
30
|
+
## 0.11.6 — 2026-05-12
|
|
31
|
+
|
|
32
|
+
**Patch: items 91-98 + regression coverage extended to 35 cases.**
|
|
33
|
+
|
|
34
|
+
### Critical
|
|
35
|
+
|
|
36
|
+
- **#91 CSAF + OpenVEX renderers excluded framework_gap_mapping.** SARIF already iterated it (added in v0.11.5); the other two formats diverged. Now: both CSAF and OpenVEX emit one vulnerability / statement per framework gap, keyed under `exceptd-framework-gap` (CSAF) / `exceptd:framework-gap:<framework>:<control>` (OpenVEX) pseudo-CVE namespaces. All three formats now share the same findings-extraction layer (CVEs + indicators + framework gaps).
|
|
37
|
+
|
|
38
|
+
### Bugs
|
|
39
|
+
|
|
40
|
+
- **#92 CSAF current_release_date null.** CSAF 2.0 §3.2.1.12 requires this field non-null; downstream validators rejected the bundle. Set to `initial_release_date` (same value, satisfies the spec).
|
|
41
|
+
- **#93 SARIF references ruleIds without rule definitions.** SARIF spec §3.27.3: every referenced `ruleId` must have a corresponding entry in `tool.driver.rules`. Pre-0.11.6 SARIF referenced `framework-gap-0`/`framework-gap-1`/etc but only defined rules for indicator hits and matched CVEs. GitHub Code Scanning + VS Code SARIF Viewer + Azure DevOps would warn or fail to display rule context. Now: one rule definition per framework gap including the gap text and required-control hint.
|
|
42
|
+
- **#94 lint stricter than runner.** Pre-0.11.6 lint reported `missing_required_artifact` as a hard error, but the runner accepted the same submission and ran with indicators returning `inconclusive`. Lint now warns (not errors) on missing required artifacts, with a hint explaining the run will still execute but inconclusively.
|
|
43
|
+
- **#95 default-output flip landed for `attest list` + `lint`.** When stdout is a TTY and no `--json`/`--pretty` is passed, both verbs now emit a human-readable table / summary. `brief` and `run` keep indented JSON because their data is too rich for a compact human view — operators wanting markdown digests use `--format markdown` (run) or read the brief structured.
|
|
44
|
+
- **#96 `--strict-preconditions` flag.** New on `run`: escalates warn-level preflight issues (unverified preconditions, `on_fail: warn`) to exit 1. Default (without the flag) preserves the v0.11.x behavior where warn-level preconditions are informational and exit 0. CI gates wanting "fail on any unverified precondition" pass this flag.
|
|
45
|
+
- **#97 `doctor --fix` was a no-op under `--json`.** The fix logic was placed AFTER the JSON early-return, so `--fix --json` never executed. Moved before the early-return; now generates the keypair and the returned JSON reflects the post-fix state (`summary.fix_applied: "ed25519_keypair_generated"`).
|
|
46
|
+
- **#98 `attest export --format garbage` + `report garbage` silently accepted.** Both now validate against the accepted set and emit structured JSON errors with exit non-zero, matching `run --format` / `ci --format` rejection.
|
|
47
|
+
|
|
48
|
+
### Test infrastructure
|
|
49
|
+
|
|
50
|
+
35 cases in `tests/operator-bugs.test.js` (8 new for 91-98). 322 tests pass total. Future bug fixes continue to land here.
|
|
51
|
+
|
|
3
52
|
## 0.11.5 — 2026-05-12
|
|
4
53
|
|
|
5
54
|
**Patch: items 82-90 + permanent regression suite at `tests/operator-bugs.test.js`.**
|
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ Pre-1.0. Latest release lives on [GitHub Releases](https://github.com/blamejs/ex
|
|
|
36
36
|
|
|
37
37
|
**v0.11.0 collapses the 21-verb CLI into 11 canonical verbs** + flips the default output to human-readable. The new surface: `discover` (scan cwd → recommend playbooks), `brief` (unified info doc, replaces plan + govern + direct + look), `run` (phases 4-7, with flat or nested submission shape, auto-detect cwd context), `ai-run` (JSONL streaming variant for AI conversational flow), `attest` (subverbs: list / show / export / verify / diff — replaces reattest + list-attestations), `doctor` (one-shot health check — signatures + currency + cve/rfc validation + signing status), `ci` (one-shot CI gate, exit-2 on detected or rwep ≥ escalate), `ask` (plain-English routing), `lint` (pre-flight submission shape check). Attestation root moved from cwd-relative `.exceptd/` to `~/.exceptd/attestations/<repo-or-host-tag>/`. v0.10.x verbs (`plan`/`govern`/`direct`/`look`/`scan`/`dispatch`/`currency`/`verify`/`validate-cves`/`validate-rfcs`/`watchlist`/`prefetch`/`build-indexes`/`ingest`/`reattest`/`list-attestations`) still work via one-time deprecation banner — removed in v0.12.
|
|
38
38
|
|
|
39
|
+
**v0.11.1-0.11.7 stability arc** — 30+ operator-reported items fixed across the v0.11.x line: mutex filesystem lockfile, `--vex` filter, `--ci` exit-code gating, `--diff-from-latest`, `--operator`/`--ack` attestation binding, `--format <fmt>` actually transforms output for run + ci, `ask` synonym routing, `lint` shares normalize contract with runner, CSAF/SARIF/OpenVEX bundles include indicator hits + framework gaps (was: empty for posture-only playbooks), CSAF current_release_date populated, SARIF rule definitions for every ruleId, `doctor --fix` for missing private key, `--strict-preconditions` flag, default human output for `attest list`/`lint` on TTY. Permanent regression suite at `tests/operator-bugs.test.js` (35 named test cases) — re-introductions caught at `npm test`, not at user re-report.
|
|
40
|
+
|
|
39
41
|
---
|
|
40
42
|
|
|
41
43
|
## Skill Inventory
|
package/bin/exceptd.js
CHANGED
|
@@ -436,12 +436,18 @@ function parseArgs(argv, opts) {
|
|
|
436
436
|
return out;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
function emit(obj, pretty) {
|
|
440
|
-
// v0.11.
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
//
|
|
439
|
+
function emit(obj, pretty, humanRenderer) {
|
|
440
|
+
// v0.11.6 (#95): real default-human flip. When stdout is a TTY AND no
|
|
441
|
+
// --json/--pretty was passed AND a custom human renderer was supplied,
|
|
442
|
+
// render the human form. Otherwise: indented JSON on TTY (improvement
|
|
443
|
+
// over compact), compact JSON when piped. --pretty forces indented JSON
|
|
444
|
+
// regardless. --json forces JSON (overrides human renderer).
|
|
444
445
|
const interactive = process.stdout.isTTY && !process.env.EXCEPTD_RAW_JSON;
|
|
446
|
+
const wantHuman = humanRenderer && interactive && !pretty && !global.__exceptdWantJson;
|
|
447
|
+
if (wantHuman) {
|
|
448
|
+
process.stdout.write(humanRenderer(obj) + "\n");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
445
451
|
const indent = pretty || (interactive && !pretty);
|
|
446
452
|
const s = indent ? JSON.stringify(obj, null, 2) : JSON.stringify(obj);
|
|
447
453
|
process.stdout.write(s + "\n");
|
|
@@ -488,7 +494,7 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
488
494
|
bool: ["pretty", "air-gap", "force-stale", "all", "flat", "directives",
|
|
489
495
|
"ci", "latest", "diff-from-latest", "explain", "signal-list", "ack",
|
|
490
496
|
"force-overwrite", "no-stream", "block-on-jurisdiction-clock",
|
|
491
|
-
"json-stdout-only", "fix", "human", "json"],
|
|
497
|
+
"json-stdout-only", "fix", "human", "json", "strict-preconditions"],
|
|
492
498
|
multi: ["playbook", "format"],
|
|
493
499
|
});
|
|
494
500
|
// v0.11.2 bug #60: flip defaults to human-readable. JSON via explicit --json
|
|
@@ -502,6 +508,8 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
502
508
|
// ai-run/ci/attest show/export/diff/verify) fall back to indented JSON
|
|
503
509
|
// labeled as such — better than no signal.
|
|
504
510
|
args._jsonMode = !!(args.json || args.pretty || args["json-stdout-only"]);
|
|
511
|
+
// Hoist into module-level state so emit() can read it without plumbing.
|
|
512
|
+
global.__exceptdWantJson = args._jsonMode;
|
|
505
513
|
const pretty = !!args.pretty;
|
|
506
514
|
const runOpts = {
|
|
507
515
|
airGap: !!args["air-gap"],
|
|
@@ -870,8 +878,13 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
870
878
|
);
|
|
871
879
|
|
|
872
880
|
const issues = [];
|
|
881
|
+
// v0.11.6 (#94): missing_required_artifact downgraded from error to warn.
|
|
882
|
+
// The runner doesn't refuse a submission missing required artifacts — it
|
|
883
|
+
// runs with the indicators that have data and marks the rest inconclusive.
|
|
884
|
+
// Lint was stricter than runner; users got errors on submissions the runner
|
|
885
|
+
// accepted. Now: lint warns about missing artifacts but doesn't fail.
|
|
873
886
|
for (const id of missingRequired) {
|
|
874
|
-
issues.push({ severity: "
|
|
887
|
+
issues.push({ severity: "warn", kind: "missing_required_artifact", artifact_id: id, hint: `Add to submission.artifacts.${id} = { value, captured: true } (or under observations in the flat shape). The run will still execute without this; the corresponding indicators will return 'inconclusive'.` });
|
|
875
888
|
}
|
|
876
889
|
for (const k of unknownArtifactKeys) {
|
|
877
890
|
issues.push({ severity: "warn", kind: "unknown_artifact_key", key: k, hint: `Not in playbook ${playbookId} look.artifacts[]. Recognized: ${[...knownArtifacts].slice(0, 10).join(", ")}…` });
|
|
@@ -916,7 +929,20 @@ function cmdLint(runner, args, runOpts, pretty) {
|
|
|
916
929
|
info: issues.filter(i => i.severity === "info").length,
|
|
917
930
|
},
|
|
918
931
|
issues,
|
|
919
|
-
}, pretty)
|
|
932
|
+
}, pretty, (obj) => {
|
|
933
|
+
// v0.11.6 (#95) human renderer for lint.
|
|
934
|
+
const lines = [`lint: ${obj.playbook_id} (${obj.directive_id}) — shape: ${obj.submission_shape}`];
|
|
935
|
+
lines.push(` ${obj.ok ? "[ok]" : "[!! fail]"} errors=${obj.summary.errors} warnings=${obj.summary.warnings} info=${obj.summary.info}`);
|
|
936
|
+
if (obj.issues.length > 0) {
|
|
937
|
+
for (const i of obj.issues.slice(0, 30)) {
|
|
938
|
+
const tag = i.severity === "error" ? "[!! ERROR]" : (i.severity === "warn" ? "[!! WARN ]" : "[i INFO ]");
|
|
939
|
+
lines.push(` ${tag} ${i.kind}${i.artifact_id ? ": " + i.artifact_id : ""}${i.observation_key ? ": " + i.observation_key : ""}${i.key ? ": " + i.key : ""}${i.precondition_id ? ": " + i.precondition_id : ""}`);
|
|
940
|
+
if (i.hint) lines.push(` ${i.hint}`);
|
|
941
|
+
}
|
|
942
|
+
if (obj.issues.length > 30) lines.push(` … and ${obj.issues.length - 30} more (use --json for full list)`);
|
|
943
|
+
}
|
|
944
|
+
return lines.join("\n");
|
|
945
|
+
});
|
|
920
946
|
if (!ok) process.exitCode = 1;
|
|
921
947
|
}
|
|
922
948
|
|
|
@@ -1272,6 +1298,21 @@ function cmdRun(runner, args, runOpts, pretty) {
|
|
|
1272
1298
|
process.exit(1);
|
|
1273
1299
|
}
|
|
1274
1300
|
|
|
1301
|
+
// v0.11.6 (#96): --strict-preconditions escalates warn-level preflight
|
|
1302
|
+
// issues to exit 1. Default (without the flag) preserves the existing
|
|
1303
|
+
// behavior where warn-level issues stay informational. CI gates wanting
|
|
1304
|
+
// "fail on any unverified precondition" pass this flag.
|
|
1305
|
+
if (args["strict-preconditions"] && result && Array.isArray(result.preflight_issues)) {
|
|
1306
|
+
const warnIssues = result.preflight_issues.filter(i =>
|
|
1307
|
+
i.kind === "precondition_unverified" || i.kind === "precondition_warn"
|
|
1308
|
+
);
|
|
1309
|
+
if (warnIssues.length > 0) {
|
|
1310
|
+
process.stderr.write(`[exceptd run] --strict-preconditions: ${warnIssues.length} unverified/warn precondition(s) — exit 1.\n`);
|
|
1311
|
+
emit(result, pretty);
|
|
1312
|
+
process.exit(1);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1275
1316
|
// --diff-from-latest: compare evidence_hash against the most recent prior
|
|
1276
1317
|
// attestation for this playbook. Drift mode for cron baselines.
|
|
1277
1318
|
// We've already persisted the CURRENT attestation above, so the find must
|
|
@@ -1987,6 +2028,13 @@ function cmdAttest(runner, args, runOpts, pretty) {
|
|
|
1987
2028
|
let formatRaw = args.format || "json";
|
|
1988
2029
|
if (Array.isArray(formatRaw)) formatRaw = formatRaw[0];
|
|
1989
2030
|
const format = formatRaw === "csaf-2.0" ? "csaf" : formatRaw;
|
|
2031
|
+
// v0.11.6 (#98): validate against accepted set. Pre-0.11.6 unknown
|
|
2032
|
+
// formats fell through to the default redacted JSON output, silently
|
|
2033
|
+
// accepting any value the operator passed.
|
|
2034
|
+
const VALID_EXPORT_FORMATS = ["json", "csaf", "csaf-2.0"];
|
|
2035
|
+
if (!VALID_EXPORT_FORMATS.includes(formatRaw)) {
|
|
2036
|
+
return emitError(`attest export: --format "${formatRaw}" not in accepted set ${JSON.stringify(VALID_EXPORT_FORMATS)}.`, null, pretty);
|
|
2037
|
+
}
|
|
1990
2038
|
const redacted = attestations.map(a => ({
|
|
1991
2039
|
session_id: a.session_id,
|
|
1992
2040
|
playbook_id: a.playbook_id,
|
|
@@ -2452,6 +2500,31 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
2452
2500
|
},
|
|
2453
2501
|
};
|
|
2454
2502
|
|
|
2503
|
+
// v0.11.6 (#97): --fix runs BEFORE the JSON early-return so `exceptd doctor
|
|
2504
|
+
// --fix --json` actually fixes (was a no-op pre-0.11.6). Re-runs the
|
|
2505
|
+
// signing check after fix so the returned JSON reflects the post-fix state.
|
|
2506
|
+
if (args.fix && checks.signing && !checks.signing.private_key_present) {
|
|
2507
|
+
process.stderr.write("[doctor --fix] generating Ed25519 keypair via `node lib/sign.js generate-keypair`...\n");
|
|
2508
|
+
const r = require("child_process").spawnSync(process.execPath, [path.join(PKG_ROOT, "lib", "sign.js"), "generate-keypair"], {
|
|
2509
|
+
stdio: ["ignore", "pipe", "pipe"], cwd: PKG_ROOT,
|
|
2510
|
+
});
|
|
2511
|
+
if (r.status === 0) {
|
|
2512
|
+
// Re-verify the private key is now present so the JSON output reflects
|
|
2513
|
+
// the fix.
|
|
2514
|
+
const keyPath = path.join(process.cwd(), ".keys", "private.pem");
|
|
2515
|
+
const fallback = path.join(PKG_ROOT, ".keys", "private.pem");
|
|
2516
|
+
const present = fs.existsSync(keyPath) || fs.existsSync(fallback);
|
|
2517
|
+
checks.signing = { ok: present, severity: present ? "info" : "warn", private_key_present: present, can_sign_attestations: present };
|
|
2518
|
+
out.checks = checks;
|
|
2519
|
+
out.summary.fix_applied = "ed25519_keypair_generated";
|
|
2520
|
+
process.stderr.write("[doctor --fix] keypair generated — re-checking signing status.\n");
|
|
2521
|
+
} else {
|
|
2522
|
+
out.summary.fix_attempted = "ed25519_keypair_generation_failed";
|
|
2523
|
+
out.summary.fix_exit_code = r.status;
|
|
2524
|
+
process.stderr.write(`[doctor --fix] generation failed (exit=${r.status}); run \`node lib/sign.js generate-keypair\` manually.\n`);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2455
2528
|
if (wantJson) {
|
|
2456
2529
|
emit(out, indent);
|
|
2457
2530
|
if (!allGreen) process.exitCode = 1;
|
|
@@ -2505,17 +2578,14 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
2505
2578
|
lines.push(`summary: ${errorList.length} fail / ${warnList.length} warn — fail: ${errorList.join(", ")}; warn: ${warnList.join(", ") || "none"}`);
|
|
2506
2579
|
}
|
|
2507
2580
|
process.stdout.write(lines.join("\n") + "\n");
|
|
2508
|
-
//
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
process.exitCode = 1;
|
|
2517
|
-
return;
|
|
2518
|
-
}
|
|
2581
|
+
// v0.11.6 (#97): --fix already ran above the JSON early-return. Echo the
|
|
2582
|
+
// applied/attempted state here for human readers.
|
|
2583
|
+
if (out.summary.fix_applied) {
|
|
2584
|
+
process.stdout.write(`\n[doctor --fix] ${out.summary.fix_applied} — re-run \`exceptd doctor\` to confirm.\n`);
|
|
2585
|
+
} else if (out.summary.fix_attempted) {
|
|
2586
|
+
process.stdout.write(`\n[doctor --fix] ${out.summary.fix_attempted} (exit=${out.summary.fix_exit_code}); run \`node lib/sign.js generate-keypair\` manually.\n`);
|
|
2587
|
+
process.exitCode = 1;
|
|
2588
|
+
return;
|
|
2519
2589
|
}
|
|
2520
2590
|
if (errorList.length > 0) process.exitCode = 1;
|
|
2521
2591
|
// Warnings alone do NOT force exit 1 — CI gates use exit 0 to mean "ran
|
|
@@ -2563,7 +2633,21 @@ function cmdListAttestations(runner, args, runOpts, pretty) {
|
|
|
2563
2633
|
count: entries.length,
|
|
2564
2634
|
filter: { playbook: args.playbook || null, since: args.since || null },
|
|
2565
2635
|
roots_searched: [...seenRoots],
|
|
2566
|
-
}, pretty)
|
|
2636
|
+
}, pretty, (obj) => {
|
|
2637
|
+
// v0.11.6 (#95) human renderer for attest list: one row per session.
|
|
2638
|
+
const lines = [`attest list — ${obj.count} attestation(s)`];
|
|
2639
|
+
if (obj.count === 0) {
|
|
2640
|
+
lines.push(` (no attestations under ${obj.roots_searched.join(' or ')})`);
|
|
2641
|
+
return lines.join("\n");
|
|
2642
|
+
}
|
|
2643
|
+
lines.push(` ${"session-id".padEnd(20)} ${"playbook".padEnd(16)} ${"captured-at".padEnd(20)} evidence-hash`);
|
|
2644
|
+
lines.push(` ${"-".repeat(20)} ${"-".repeat(16)} ${"-".repeat(20)} ${"-".repeat(20)}`);
|
|
2645
|
+
for (const e of obj.attestations.slice(0, 50)) {
|
|
2646
|
+
lines.push(` ${(e.session_id || "?").padEnd(20)} ${(e.playbook_id || "?").padEnd(16)} ${(e.captured_at || "").slice(0, 19).padEnd(20)} ${e.evidence_hash || ""}`);
|
|
2647
|
+
}
|
|
2648
|
+
if (obj.count > 50) lines.push(` … and ${obj.count - 50} more (use --json for full list)`);
|
|
2649
|
+
return lines.join("\n");
|
|
2650
|
+
});
|
|
2567
2651
|
}
|
|
2568
2652
|
|
|
2569
2653
|
// ---------------------------------------------------------------------------
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-12T17:
|
|
3
|
+
"generated_at": "2026-05-12T17:44:09.922Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 49,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "ca19f2d85b4b143dffe5517494455f0a8a2c97bddd028c66ef5a38623503a160",
|
|
8
8
|
"data/atlas-ttps.json": "1500b5830dab070c4252496964a8c0948e1052a656e2c7c6e1efaf0350645e13",
|
|
9
9
|
"data/cve-catalog.json": "a81d3e4b491b27ccc084596b063a6108ff10c9eb01d7776922fc393980b534fe",
|
|
10
10
|
"data/cwe-catalog.json": "c3367d469b4b3d31e4c56397dd7a8305a0be338ecd85afa27804c0c9ce12157b",
|
package/keys/public.pem
CHANGED
package/lib/playbook-runner.js
CHANGED
|
@@ -790,21 +790,37 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals)
|
|
|
790
790
|
notes: [{ category: 'description', text: `Indicator ${i.id} fired (${i.confidence}${i.deterministic ? ' / deterministic' : ''}) in playbook ${playbook._meta.id}.` }],
|
|
791
791
|
remediations: [{ category: 'mitigation', details: validate.selected_remediation?.description || `Consult playbook brief: exceptd brief ${playbook._meta.id}.` }],
|
|
792
792
|
}));
|
|
793
|
+
// v0.11.6 (#91): framework_gap_mapping → CSAF vulnerabilities. Each gap
|
|
794
|
+
// becomes a vulnerability keyed by the framework + control, with the
|
|
795
|
+
// gap text as the description and the required_control as the remediation.
|
|
796
|
+
const gapVulns = (analyze.framework_gap_mapping || []).map((g, idx) => ({
|
|
797
|
+
ids: [{ system_name: 'exceptd-framework-gap', text: `${g.framework}:${g.claimed_control || `gap-${idx}`}` }],
|
|
798
|
+
notes: [
|
|
799
|
+
{ category: 'description', text: g.actual_gap || `Framework gap in ${g.framework} ${g.claimed_control || ''}` },
|
|
800
|
+
{ category: 'general', text: g.claimed_control ? `Claimed control: ${g.claimed_control}` : null },
|
|
801
|
+
].filter(n => n.text),
|
|
802
|
+
remediations: g.required_control ? [{ category: 'mitigation', details: g.required_control }] : [],
|
|
803
|
+
}));
|
|
804
|
+
const now = new Date().toISOString();
|
|
793
805
|
return {
|
|
794
806
|
document: {
|
|
795
807
|
category: 'csaf_security_advisory',
|
|
796
808
|
csaf_version: '2.0',
|
|
797
809
|
publisher: { category: 'vendor', name: 'exceptd', namespace: 'https://exceptd.com' },
|
|
798
|
-
title: `exceptd finding: ${playbook.domain.name} (${analyze.matched_cves.length} CVE(s), ${indicatorHits.length} indicator hit(s))`,
|
|
810
|
+
title: `exceptd finding: ${playbook.domain.name} (${analyze.matched_cves.length} CVE(s), ${indicatorHits.length} indicator hit(s), ${(analyze.framework_gap_mapping || []).length} framework gap(s))`,
|
|
799
811
|
tracking: {
|
|
800
812
|
id: `exceptd-${playbook._meta.id}-${Date.now()}`,
|
|
801
813
|
status: 'final',
|
|
802
814
|
version: playbook._meta.version,
|
|
803
|
-
initial_release_date:
|
|
804
|
-
|
|
815
|
+
initial_release_date: now,
|
|
816
|
+
// v0.11.6 (#92): CSAF 2.0 §3.2.1.12 requires current_release_date
|
|
817
|
+
// non-null. Pre-0.11.6 we only set initial_release_date and
|
|
818
|
+
// downstream validators rejected the bundle.
|
|
819
|
+
current_release_date: now,
|
|
820
|
+
revision_history: [{ number: '1', date: now, summary: 'Initial finding emission' }]
|
|
805
821
|
}
|
|
806
822
|
},
|
|
807
|
-
vulnerabilities: [...cveVulns, ...indicatorVulns],
|
|
823
|
+
vulnerabilities: [...cveVulns, ...indicatorVulns, ...gapVulns],
|
|
808
824
|
exceptd_extension: {
|
|
809
825
|
classification: analyze._detect_classification,
|
|
810
826
|
rwep: analyze.rwep,
|
|
@@ -863,13 +879,25 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals)
|
|
|
863
879
|
fullDescription: { text: `Indicator from playbook ${playbook._meta.id}. Type: ${i.type}. Confidence: ${i.confidence}.` },
|
|
864
880
|
defaultConfiguration: { level: i.deterministic ? 'error' : (i.confidence === 'high' ? 'warning' : 'note') },
|
|
865
881
|
}));
|
|
882
|
+
// v0.11.6 (#93): SARIF spec §3.27.3 — every referenced ruleId SHOULD have
|
|
883
|
+
// a corresponding rule definition in tool.driver.rules. Pre-0.11.6 we
|
|
884
|
+
// referenced framework-gap-N ids without defining them; GitHub Code
|
|
885
|
+
// Scanning + VS Code SARIF Viewer + Azure DevOps would warn or fail to
|
|
886
|
+
// display rule context. Now we emit one rule per framework gap.
|
|
887
|
+
const gapRules = (analyze.framework_gap_mapping || []).map((g, idx) => ({
|
|
888
|
+
id: `framework-gap-${idx}`,
|
|
889
|
+
shortDescription: { text: `${g.framework}: ${g.claimed_control || `gap-${idx}`}` },
|
|
890
|
+
fullDescription: { text: g.actual_gap || `Framework gap in ${g.framework}` },
|
|
891
|
+
defaultConfiguration: { level: 'note' },
|
|
892
|
+
help: g.required_control ? { text: `Required control: ${g.required_control}` } : undefined,
|
|
893
|
+
}));
|
|
866
894
|
return {
|
|
867
895
|
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
868
896
|
version: '2.1.0',
|
|
869
897
|
runs: [{
|
|
870
898
|
tool: { driver: {
|
|
871
899
|
name: 'exceptd', version: playbook._meta.version, informationUri: 'https://exceptd.com',
|
|
872
|
-
rules: [...cveRules, ...indicatorRules],
|
|
900
|
+
rules: [...cveRules, ...indicatorRules, ...gapRules],
|
|
873
901
|
} },
|
|
874
902
|
results: [...cveResults, ...indicatorResults, ...gapResults],
|
|
875
903
|
invocations: [{ executionSuccessful: true, properties: {
|
|
@@ -900,13 +928,23 @@ function buildEvidenceBundle(format, playbook, analyze, validate, agentSignals)
|
|
|
900
928
|
action_statement: validate.selected_remediation?.description || `Run \`exceptd brief ${playbook._meta.id}\` for context.`,
|
|
901
929
|
impact_statement: `Indicator ${i.id} fired (${i.confidence}${i.deterministic ? '/deterministic' : ''}) in playbook ${playbook._meta.id}.`,
|
|
902
930
|
}));
|
|
931
|
+
// v0.11.6 (#91): framework gaps → OpenVEX statements. Each gap becomes
|
|
932
|
+
// a statement with a pseudo-CVE id under the exceptd:framework-gap
|
|
933
|
+
// namespace so VEX downstreams ingest them cleanly.
|
|
934
|
+
const gapStatements = (analyze.framework_gap_mapping || []).map((g, idx) => ({
|
|
935
|
+
vulnerability: { '@id': `exceptd:framework-gap:${g.framework}:${g.claimed_control || idx}`, name: `${g.framework} ${g.claimed_control || `gap-${idx}`}` },
|
|
936
|
+
status: 'under_investigation',
|
|
937
|
+
timestamp: issued,
|
|
938
|
+
action_statement: g.required_control || null,
|
|
939
|
+
impact_statement: g.actual_gap || `Framework gap in ${g.framework}.`,
|
|
940
|
+
}));
|
|
903
941
|
return {
|
|
904
942
|
'@context': 'https://openvex.dev/ns/v0.2.0',
|
|
905
943
|
'@id': `https://exceptd.com/vex/${playbook._meta.id}/${Date.now()}`,
|
|
906
944
|
author: 'exceptd',
|
|
907
945
|
timestamp: issued,
|
|
908
946
|
version: 1,
|
|
909
|
-
statements: [...cveStatements, ...indicatorStatements],
|
|
947
|
+
statements: [...cveStatements, ...indicatorStatements, ...gapStatements],
|
|
910
948
|
};
|
|
911
949
|
}
|
|
912
950
|
|
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-12T17:43:06.691Z",
|
|
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.7",
|
|
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",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"RFC-7296"
|
|
52
52
|
],
|
|
53
53
|
"last_threat_review": "2026-05-01",
|
|
54
|
-
"signature": "
|
|
55
|
-
"signed_at": "2026-05-
|
|
54
|
+
"signature": "Xk593pj7my6wPJbQBE47khpIUrPsp6N1lW7cE2T/VPPF5T+8C1yGKc9B8VphD7Q08yWFcbwF6HoWpA/+4uG9DA==",
|
|
55
|
+
"signed_at": "2026-05-12T17:43:06.252Z",
|
|
56
56
|
"cwe_refs": [
|
|
57
57
|
"CWE-125",
|
|
58
58
|
"CWE-362",
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
"SOC2-CC6-logical-access"
|
|
116
116
|
],
|
|
117
117
|
"last_threat_review": "2026-05-01",
|
|
118
|
-
"signature": "
|
|
119
|
-
"signed_at": "2026-05-
|
|
118
|
+
"signature": "nOgUu+LK9fy6ASTCoRGtx3ttgjZCl7WIkKu2wu06JEKVSpL2cKU3ex2tmVAvv11LBmpTH+b/0zvqXlzcxzHnCw==",
|
|
119
|
+
"signed_at": "2026-05-12T17:43:06.255Z",
|
|
120
120
|
"cwe_refs": [
|
|
121
121
|
"CWE-1039",
|
|
122
122
|
"CWE-1426",
|
|
@@ -178,8 +178,8 @@
|
|
|
178
178
|
"RFC-9700"
|
|
179
179
|
],
|
|
180
180
|
"last_threat_review": "2026-05-01",
|
|
181
|
-
"signature": "
|
|
182
|
-
"signed_at": "2026-05-
|
|
181
|
+
"signature": "7FH1J9PlOyvcRCzRmggmenX9fIR0pi/veXihb3TeStcq1Rpuz1KHdOcJLqA9su4t2goYukKKCXHV6hx8hzplAA==",
|
|
182
|
+
"signed_at": "2026-05-12T17:43:06.255Z",
|
|
183
183
|
"cwe_refs": [
|
|
184
184
|
"CWE-22",
|
|
185
185
|
"CWE-345",
|
|
@@ -224,8 +224,8 @@
|
|
|
224
224
|
"attack_refs": [],
|
|
225
225
|
"framework_gaps": [],
|
|
226
226
|
"last_threat_review": "2026-05-01",
|
|
227
|
-
"signature": "
|
|
228
|
-
"signed_at": "2026-05-
|
|
227
|
+
"signature": "FqTRjHfEgw56pyHnyWzNtnhzDMEePBtmuamtW/iyX+h4yqbvP4Fyr7NRjRs3EgqT4j7oHuEZhV9Jt6ZTBgN4AA==",
|
|
228
|
+
"signed_at": "2026-05-12T17:43:06.255Z"
|
|
229
229
|
},
|
|
230
230
|
{
|
|
231
231
|
"name": "compliance-theater",
|
|
@@ -255,8 +255,8 @@
|
|
|
255
255
|
"CMMC-2.0-Level-2"
|
|
256
256
|
],
|
|
257
257
|
"last_threat_review": "2026-05-01",
|
|
258
|
-
"signature": "
|
|
259
|
-
"signed_at": "2026-05-
|
|
258
|
+
"signature": "3fN4yotiIIq76PVTHwozCu28TzDZvWule6vX8SXUT3XXbIBSuvAO0M/euvc3pw3TdZ2UNf78dI18lOCNdJ0aAg==",
|
|
259
|
+
"signed_at": "2026-05-12T17:43:06.256Z"
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"name": "exploit-scoring",
|
|
@@ -284,8 +284,8 @@
|
|
|
284
284
|
"CIS-Controls-v8-Control7"
|
|
285
285
|
],
|
|
286
286
|
"last_threat_review": "2026-05-01",
|
|
287
|
-
"signature": "
|
|
288
|
-
"signed_at": "2026-05-
|
|
287
|
+
"signature": "yZfpk4lQMRXegj2ADWjMmZTchUN6Lxpv587O/0JMzbNkXQtD6FrSAQOBWjx8S7uQ/sTntxgGN7aQQDLxL9RWAA==",
|
|
288
|
+
"signed_at": "2026-05-12T17:43:06.257Z"
|
|
289
289
|
},
|
|
290
290
|
{
|
|
291
291
|
"name": "rag-pipeline-security",
|
|
@@ -321,8 +321,8 @@
|
|
|
321
321
|
"OWASP-LLM-Top-10-2025-LLM08"
|
|
322
322
|
],
|
|
323
323
|
"last_threat_review": "2026-05-01",
|
|
324
|
-
"signature": "
|
|
325
|
-
"signed_at": "2026-05-
|
|
324
|
+
"signature": "ABHkoqee67KdUyDZ3bvF+/DNxjGhPR/ehT6pfOnmUIMmkcQFHpZ0OUVXKiFUANaLgKLP1vg0VEmHOoxpNA3vAA==",
|
|
325
|
+
"signed_at": "2026-05-12T17:43:06.257Z",
|
|
326
326
|
"cwe_refs": [
|
|
327
327
|
"CWE-1395",
|
|
328
328
|
"CWE-1426"
|
|
@@ -378,8 +378,8 @@
|
|
|
378
378
|
"RFC-9000"
|
|
379
379
|
],
|
|
380
380
|
"last_threat_review": "2026-05-01",
|
|
381
|
-
"signature": "
|
|
382
|
-
"signed_at": "2026-05-
|
|
381
|
+
"signature": "+Nd/2tgBnW+mEGX84QvkgR2To2J7kA+lB63BsADDKeCXeebFv6Vo9H1P4vyUkKHfe4fP0ndpy3agIZcUO/e/Dg==",
|
|
382
|
+
"signed_at": "2026-05-12T17:43:06.257Z",
|
|
383
383
|
"d3fend_refs": [
|
|
384
384
|
"D3-CA",
|
|
385
385
|
"D3-CSPP",
|
|
@@ -413,8 +413,8 @@
|
|
|
413
413
|
"attack_refs": [],
|
|
414
414
|
"framework_gaps": [],
|
|
415
415
|
"last_threat_review": "2026-05-01",
|
|
416
|
-
"signature": "
|
|
417
|
-
"signed_at": "2026-05-
|
|
416
|
+
"signature": "VMNGFvowXLbBjZp5nvWloKkqyqHKhnSzbVRU3gX9quOZJHH56w2M4id+oDsXIjR0CfRRb7eXl/so0Hq4xLBuBQ==",
|
|
417
|
+
"signed_at": "2026-05-12T17:43:06.257Z",
|
|
418
418
|
"cwe_refs": [
|
|
419
419
|
"CWE-1188"
|
|
420
420
|
]
|
|
@@ -441,8 +441,8 @@
|
|
|
441
441
|
"attack_refs": [],
|
|
442
442
|
"framework_gaps": [],
|
|
443
443
|
"last_threat_review": "2026-05-01",
|
|
444
|
-
"signature": "
|
|
445
|
-
"signed_at": "2026-05-
|
|
444
|
+
"signature": "5MaJs7gPCuFlK4oAttLulAPOA1noeV+xD/UqVWaVyRedXZgebBGKjnlE2t1qmTugvxlNIfeAnBZapk+Wz3VAAg==",
|
|
445
|
+
"signed_at": "2026-05-12T17:43:06.258Z"
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
"name": "global-grc",
|
|
@@ -473,8 +473,8 @@
|
|
|
473
473
|
"attack_refs": [],
|
|
474
474
|
"framework_gaps": [],
|
|
475
475
|
"last_threat_review": "2026-05-01",
|
|
476
|
-
"signature": "
|
|
477
|
-
"signed_at": "2026-05-
|
|
476
|
+
"signature": "S/YXUpI/mcG2FpdUTgMsccWBtTaR5A4Ph4QFQw31S9w9Hn/z3sOFHLkb1B5YSwlg+mMOtSIxMdet1eLGSZkTDg==",
|
|
477
|
+
"signed_at": "2026-05-12T17:43:06.258Z"
|
|
478
478
|
},
|
|
479
479
|
{
|
|
480
480
|
"name": "zeroday-gap-learn",
|
|
@@ -500,8 +500,8 @@
|
|
|
500
500
|
"attack_refs": [],
|
|
501
501
|
"framework_gaps": [],
|
|
502
502
|
"last_threat_review": "2026-05-01",
|
|
503
|
-
"signature": "
|
|
504
|
-
"signed_at": "2026-05-
|
|
503
|
+
"signature": "AKS+JsmhhBtytY2eIMuydjkZOYprWCmQ+RqxyxcVG9XcEI29ZSM/JbVIINQHozFl7OPPrOu1ouiTnk7LOJ86Bg==",
|
|
504
|
+
"signed_at": "2026-05-12T17:43:06.258Z"
|
|
505
505
|
},
|
|
506
506
|
{
|
|
507
507
|
"name": "pqc-first",
|
|
@@ -552,8 +552,8 @@
|
|
|
552
552
|
"CRQC timeline estimate changes"
|
|
553
553
|
],
|
|
554
554
|
"last_threat_review": "2026-05-01",
|
|
555
|
-
"signature": "
|
|
556
|
-
"signed_at": "2026-05-
|
|
555
|
+
"signature": "oEkK5bLS/G5RIHnxlNFJYdzhTJbKZnkJv+W4iS9UJ/uszZHgZGoxygELPc4kn3FowV5eE988SQYG4WKlXtNzCg==",
|
|
556
|
+
"signed_at": "2026-05-12T17:43:06.259Z",
|
|
557
557
|
"cwe_refs": [
|
|
558
558
|
"CWE-327"
|
|
559
559
|
],
|
|
@@ -599,8 +599,8 @@
|
|
|
599
599
|
"Framework publication updates"
|
|
600
600
|
],
|
|
601
601
|
"last_threat_review": "2026-05-01",
|
|
602
|
-
"signature": "
|
|
603
|
-
"signed_at": "2026-05-
|
|
602
|
+
"signature": "nPV6YTo1rsNH49qUnZpfoNLEQZXuLNyV05QMUOgXKHYeVDjotYpWhLgyVXlRhjV/fStiA2sWQ0MOnEJ4FBIfDg==",
|
|
603
|
+
"signed_at": "2026-05-12T17:43:06.259Z"
|
|
604
604
|
},
|
|
605
605
|
{
|
|
606
606
|
"name": "security-maturity-tiers",
|
|
@@ -636,8 +636,8 @@
|
|
|
636
636
|
"PQC tooling maturity shifting overkill to practical"
|
|
637
637
|
],
|
|
638
638
|
"last_threat_review": "2026-05-01",
|
|
639
|
-
"signature": "
|
|
640
|
-
"signed_at": "2026-05-
|
|
639
|
+
"signature": "7rirSEONz6O9Yyf46eTyuwkGizCj9FRcNHe5p7Qz6nhJoZQRW5FwW7n9opL0WlbIw8FDBYn1f22zgNUV87L5AQ==",
|
|
640
|
+
"signed_at": "2026-05-12T17:43:06.260Z",
|
|
641
641
|
"cwe_refs": [
|
|
642
642
|
"CWE-1188"
|
|
643
643
|
]
|
|
@@ -671,8 +671,8 @@
|
|
|
671
671
|
"attack_refs": [],
|
|
672
672
|
"framework_gaps": [],
|
|
673
673
|
"last_threat_review": "2026-05-11",
|
|
674
|
-
"signature": "
|
|
675
|
-
"signed_at": "2026-05-
|
|
674
|
+
"signature": "+evehnd2wSBb8uMTlTr5/aTN4bfLjsKzZJk/+OMLMOJrjCt+OuMU7EQC6xMUGeSc4cPEGajghDvq3xVaacV2Dw==",
|
|
675
|
+
"signed_at": "2026-05-12T17:43:06.260Z"
|
|
676
676
|
},
|
|
677
677
|
{
|
|
678
678
|
"name": "attack-surface-pentest",
|
|
@@ -742,8 +742,8 @@
|
|
|
742
742
|
"OWASP WSTG v5.x AI/MCP test cases (currently in working-group draft)",
|
|
743
743
|
"PTES revision incorporating AI-surface enumeration"
|
|
744
744
|
],
|
|
745
|
-
"signature": "
|
|
746
|
-
"signed_at": "2026-05-
|
|
745
|
+
"signature": "KHOXxloAYf7xqXjm2BaL3HVAZOmb7rMiMh20H/oaIkjN0WD1CnKCrRGPJn867uSFhCh/timkXolaiqD1L/h8Dg==",
|
|
746
|
+
"signed_at": "2026-05-12T17:43:06.260Z"
|
|
747
747
|
},
|
|
748
748
|
{
|
|
749
749
|
"name": "fuzz-testing-strategy",
|
|
@@ -802,8 +802,8 @@
|
|
|
802
802
|
"syzkaller eBPF and io_uring surface expansion as new kernel attack surfaces ship",
|
|
803
803
|
"OSS-Fuzz-Gen / AI-assisted harness generation becoming the default expectation for OSS maintainers"
|
|
804
804
|
],
|
|
805
|
-
"signature": "
|
|
806
|
-
"signed_at": "2026-05-
|
|
805
|
+
"signature": "+ELdD+1AY5DymBitH7wU65CS60NY1nDoLowJAFn7cE5Gr/5jy9BTkyxsm7PEXaSlXWMOkTf/HQ+uyzyxUVD/Bw==",
|
|
806
|
+
"signed_at": "2026-05-12T17:43:06.261Z"
|
|
807
807
|
},
|
|
808
808
|
{
|
|
809
809
|
"name": "dlp-gap-analysis",
|
|
@@ -877,8 +877,8 @@
|
|
|
877
877
|
"MCP gateway / proxy standardisation (Anthropic enterprise MCP gateway, Portkey MCP) — tool-call argument inspection is the missing primary control",
|
|
878
878
|
"Quebec Law 25, India DPDPA, KSA PDPL enforcement actions naming AI-tool prompt data as in-scope personal information"
|
|
879
879
|
],
|
|
880
|
-
"signature": "
|
|
881
|
-
"signed_at": "2026-05-
|
|
880
|
+
"signature": "8tFAhXAS8zZN3SUOdn+ZIu7lQ48JMOyBQ8SaObR3L/fDyFmDhufqleY2VzI3yigqlT/D4Y8FYxZHKmzXiALjDw==",
|
|
881
|
+
"signed_at": "2026-05-12T17:43:06.261Z"
|
|
882
882
|
},
|
|
883
883
|
{
|
|
884
884
|
"name": "supply-chain-integrity",
|
|
@@ -954,8 +954,8 @@
|
|
|
954
954
|
"EU CRA (Regulation 2024/2847) — implementing acts for technical documentation and SBOM submission expected through 2027",
|
|
955
955
|
"OpenSSF model-signing — emerging Sigstore-based signing standard for ML model weights; track for production adoption"
|
|
956
956
|
],
|
|
957
|
-
"signature": "
|
|
958
|
-
"signed_at": "2026-05-
|
|
957
|
+
"signature": "8xlk5ZfTKVYqTE2+ifkjTBu/RPqs4MIvX7SpOHl22YDHi7nzJ1ywPhSNYJzoPdPV4AUuWG518EldQJsEIuyuAA==",
|
|
958
|
+
"signed_at": "2026-05-12T17:43:06.261Z"
|
|
959
959
|
},
|
|
960
960
|
{
|
|
961
961
|
"name": "defensive-countermeasure-mapping",
|
|
@@ -1011,8 +1011,8 @@
|
|
|
1011
1011
|
"D3-SCP"
|
|
1012
1012
|
],
|
|
1013
1013
|
"last_threat_review": "2026-05-11",
|
|
1014
|
-
"signature": "
|
|
1015
|
-
"signed_at": "2026-05-
|
|
1014
|
+
"signature": "AMdLkDx/e3ESI4NAnJhhcaas+Ru8VjrSn6v6RBbmmzoLCGo/vFxGraa1p/qF9udhVG+DdkbwHfbfKK5Im19KDw==",
|
|
1015
|
+
"signed_at": "2026-05-12T17:43:06.262Z"
|
|
1016
1016
|
},
|
|
1017
1017
|
{
|
|
1018
1018
|
"name": "identity-assurance",
|
|
@@ -1078,8 +1078,8 @@
|
|
|
1078
1078
|
],
|
|
1079
1079
|
"d3fend_refs": [],
|
|
1080
1080
|
"last_threat_review": "2026-05-11",
|
|
1081
|
-
"signature": "
|
|
1082
|
-
"signed_at": "2026-05-
|
|
1081
|
+
"signature": "pSMHKkyWoZvRIuVtN7Vue51sP5MIy9lSaQa2YSAMhxjptx81cUnPt3S11/Tb9Ea1/eluMNQ+5F25eF2njr4mBQ==",
|
|
1082
|
+
"signed_at": "2026-05-12T17:43:06.262Z"
|
|
1083
1083
|
},
|
|
1084
1084
|
{
|
|
1085
1085
|
"name": "ot-ics-security",
|
|
@@ -1134,8 +1134,8 @@
|
|
|
1134
1134
|
],
|
|
1135
1135
|
"d3fend_refs": [],
|
|
1136
1136
|
"last_threat_review": "2026-05-11",
|
|
1137
|
-
"signature": "
|
|
1138
|
-
"signed_at": "2026-05-
|
|
1137
|
+
"signature": "qjky+ZTX1DP7uRRMQZq7S7P9/uaJEoB1dy4RZ1l37Q4OO3k2ryfL+7o0Cgm/piuafJfH+dqUeNCRrVefj4r8Dw==",
|
|
1138
|
+
"signed_at": "2026-05-12T17:43:06.262Z"
|
|
1139
1139
|
},
|
|
1140
1140
|
{
|
|
1141
1141
|
"name": "coordinated-vuln-disclosure",
|
|
@@ -1186,8 +1186,8 @@
|
|
|
1186
1186
|
"UK NCSC Vulnerability Disclosure Toolkit revisions and AU ISM CVD guidance updates",
|
|
1187
1187
|
"NYDFS 23 NYCRR 500 amendments potentially adding explicit CVD program requirements"
|
|
1188
1188
|
],
|
|
1189
|
-
"signature": "
|
|
1190
|
-
"signed_at": "2026-05-
|
|
1189
|
+
"signature": "F86Zl/I+dBzHYRUuGWsjDQI2F/I/vhzwZUFMqhNfKUzRbMf6mafOX2APCPYTp3eP1DvvvfL3Yc0hb1R5Q4nOAg==",
|
|
1190
|
+
"signed_at": "2026-05-12T17:43:06.262Z"
|
|
1191
1191
|
},
|
|
1192
1192
|
{
|
|
1193
1193
|
"name": "threat-modeling-methodology",
|
|
@@ -1236,8 +1236,8 @@
|
|
|
1236
1236
|
"LINDDUN-GO and LINDDUN-PRO updates incorporating LLM privacy threats",
|
|
1237
1237
|
"PASTA v2 updates incorporating AI/ML application threats"
|
|
1238
1238
|
],
|
|
1239
|
-
"signature": "
|
|
1240
|
-
"signed_at": "2026-05-
|
|
1239
|
+
"signature": "D/4d5NcJScNH58ADXsSrVzTmLSWZpUZTdyhtDkJlC0twSMNczOiDsXgYFitBaZgGdv5nVd00viR45mNrsaZ4BQ==",
|
|
1240
|
+
"signed_at": "2026-05-12T17:43:06.263Z"
|
|
1241
1241
|
},
|
|
1242
1242
|
{
|
|
1243
1243
|
"name": "webapp-security",
|
|
@@ -1310,8 +1310,8 @@
|
|
|
1310
1310
|
],
|
|
1311
1311
|
"d3fend_refs": [],
|
|
1312
1312
|
"last_threat_review": "2026-05-11",
|
|
1313
|
-
"signature": "
|
|
1314
|
-
"signed_at": "2026-05-
|
|
1313
|
+
"signature": "UOXaUtpcFjXyDQ70z2PaGu6K3pABtXp+7YzO6eGVGpN1CxXpPq/xW/CnTng6B7wk9WSsqD0OORBJp4VCjiVfAQ==",
|
|
1314
|
+
"signed_at": "2026-05-12T17:43:06.263Z"
|
|
1315
1315
|
},
|
|
1316
1316
|
{
|
|
1317
1317
|
"name": "ai-risk-management",
|
|
@@ -1360,8 +1360,8 @@
|
|
|
1360
1360
|
],
|
|
1361
1361
|
"d3fend_refs": [],
|
|
1362
1362
|
"last_threat_review": "2026-05-11",
|
|
1363
|
-
"signature": "
|
|
1364
|
-
"signed_at": "2026-05-
|
|
1363
|
+
"signature": "IVKygsrFjiM64fQVbd2PT6jDjs6fm5nKwJSqGfK53gG0S9wdHC4QYuh+LWlI/2ftvIKjjedLQ6FRyTrqpDEuDw==",
|
|
1364
|
+
"signed_at": "2026-05-12T17:43:06.263Z"
|
|
1365
1365
|
},
|
|
1366
1366
|
{
|
|
1367
1367
|
"name": "sector-healthcare",
|
|
@@ -1420,8 +1420,8 @@
|
|
|
1420
1420
|
],
|
|
1421
1421
|
"d3fend_refs": [],
|
|
1422
1422
|
"last_threat_review": "2026-05-11",
|
|
1423
|
-
"signature": "
|
|
1424
|
-
"signed_at": "2026-05-
|
|
1423
|
+
"signature": "P+CdSu8ZJCNUU4nTa09Voh2PcYF3y/AFJn4v7cjVIGo9FbbqO7MwvGN7cJ+aSRs2/3NMUXX4eupcODslxYyJDw==",
|
|
1424
|
+
"signed_at": "2026-05-12T17:43:06.264Z"
|
|
1425
1425
|
},
|
|
1426
1426
|
{
|
|
1427
1427
|
"name": "sector-financial",
|
|
@@ -1501,8 +1501,8 @@
|
|
|
1501
1501
|
"OSFI B-13 (Technology and Cyber Risk Management) post-2024 examination findings",
|
|
1502
1502
|
"TIBER-EU framework v2.0 alignment with DORA TLPT RTS (JC 2024/40); cross-recognition with CBEST and iCAST"
|
|
1503
1503
|
],
|
|
1504
|
-
"signature": "
|
|
1505
|
-
"signed_at": "2026-05-
|
|
1504
|
+
"signature": "zpEfh181Sc0b0cvRf/31Ir1f8lD4V5tehTogO3TJMxdKmXu06IAK7hrhBcLA/jFBv3xDDwrWW3sHzChVhWDeDA==",
|
|
1505
|
+
"signed_at": "2026-05-12T17:43:06.264Z"
|
|
1506
1506
|
},
|
|
1507
1507
|
{
|
|
1508
1508
|
"name": "sector-federal-government",
|
|
@@ -1570,8 +1570,8 @@
|
|
|
1570
1570
|
"EU Cybersecurity Certification Scheme on Common Criteria (EUCC) operational — first certificates issued 2024; high-assurance level for government use cases ramping",
|
|
1571
1571
|
"Australia PSPF 2024 revision and ISM quarterly updates — track for Essential Eight Maturity Level requirements for federal entities"
|
|
1572
1572
|
],
|
|
1573
|
-
"signature": "
|
|
1574
|
-
"signed_at": "2026-05-
|
|
1573
|
+
"signature": "7NpQlPu1DkpY9f+Frv/LLBHWUUe/qTM80c+xeYDxOzweXhvJGE/dnDCjglYHTjxT82L9cVxzBezvLEne20UpBg==",
|
|
1574
|
+
"signed_at": "2026-05-12T17:43:06.264Z"
|
|
1575
1575
|
},
|
|
1576
1576
|
{
|
|
1577
1577
|
"name": "sector-energy",
|
|
@@ -1635,8 +1635,8 @@
|
|
|
1635
1635
|
"MadIoT-class research on consumer-IoT-driven grid frequency manipulation moving from proof-of-concept to attributed campaigns",
|
|
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
|
-
"signature": "
|
|
1639
|
-
"signed_at": "2026-05-
|
|
1638
|
+
"signature": "4rhyHN5HykK7MQUmhvaTeDGj6Qf5swDd5ry8foh4KBvTkRKxTI/XyxconFGm5FASnySGPLMxX6m4JZAq5wiNBg==",
|
|
1639
|
+
"signed_at": "2026-05-12T17:43:06.265Z"
|
|
1640
1640
|
},
|
|
1641
1641
|
{
|
|
1642
1642
|
"name": "api-security",
|
|
@@ -1704,8 +1704,8 @@
|
|
|
1704
1704
|
],
|
|
1705
1705
|
"d3fend_refs": [],
|
|
1706
1706
|
"last_threat_review": "2026-05-11",
|
|
1707
|
-
"signature": "
|
|
1708
|
-
"signed_at": "2026-05-
|
|
1707
|
+
"signature": "hS1izPhETclITK7fp6R67dhy+wFDti/YsJ2M5I1gDjeWZYK41WuxeYSyt5xEHbCr3WCGDFJe77jkK1MWkxk2BA==",
|
|
1708
|
+
"signed_at": "2026-05-12T17:43:06.265Z"
|
|
1709
1709
|
},
|
|
1710
1710
|
{
|
|
1711
1711
|
"name": "cloud-security",
|
|
@@ -1785,8 +1785,8 @@
|
|
|
1785
1785
|
"eBPF-based runtime detection coverage of confidential-computing enclaves (AWS Nitro Enclaves, Azure Confidential VMs, GCP Confidential Space) — partial visibility is a tracked detection gap",
|
|
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
|
-
"signature": "
|
|
1789
|
-
"signed_at": "2026-05-
|
|
1788
|
+
"signature": "kuatqNZoRnv+oeyrxbnk+m37JRBIgRAWnDp0/IYLnoBOybiG09RzLILJraxjhvdSNCgo7WXTeBO3Y6a3Ji9MAA==",
|
|
1789
|
+
"signed_at": "2026-05-12T17:43:06.265Z"
|
|
1790
1790
|
},
|
|
1791
1791
|
{
|
|
1792
1792
|
"name": "container-runtime-security",
|
|
@@ -1847,8 +1847,8 @@
|
|
|
1847
1847
|
],
|
|
1848
1848
|
"d3fend_refs": [],
|
|
1849
1849
|
"last_threat_review": "2026-05-11",
|
|
1850
|
-
"signature": "
|
|
1851
|
-
"signed_at": "2026-05-
|
|
1850
|
+
"signature": "Btb3/7fjPFopFVdxP7+E6n322gnAAwd7OPrnuqatq6c1rXTD9aXKxiBeCmWxs8zYbIbE/lFoe9R2g6uTp8ZDBg==",
|
|
1851
|
+
"signed_at": "2026-05-12T17:43:06.266Z"
|
|
1852
1852
|
},
|
|
1853
1853
|
{
|
|
1854
1854
|
"name": "mlops-security",
|
|
@@ -1918,8 +1918,8 @@
|
|
|
1918
1918
|
"EU AI Act high-risk technical-file implementing acts (2026-2027) — operational requirements for Article 10 / 13 / 15 documentation may pin ML-BOM or model-signing",
|
|
1919
1919
|
"MITRE ATLAS v5.2 — track AML.T0010 sub-technique expansion and any new MLOps-pipeline-specific TTPs"
|
|
1920
1920
|
],
|
|
1921
|
-
"signature": "
|
|
1922
|
-
"signed_at": "2026-05-
|
|
1921
|
+
"signature": "TBWnlgdllW7K1F10HCJ7p4dbLeS3lyNWm+7mNNtyZu7jB1V5AauG1P7sb1nLLqwKqeGlHS1F0eh/BNiuAvkABg==",
|
|
1922
|
+
"signed_at": "2026-05-12T17:43:06.266Z"
|
|
1923
1923
|
},
|
|
1924
1924
|
{
|
|
1925
1925
|
"name": "incident-response-playbook",
|
|
@@ -1980,8 +1980,8 @@
|
|
|
1980
1980
|
"IL INCD Incident Response Process v4 (slated for 2026-2027) consolidating AI-incident sub-class",
|
|
1981
1981
|
"NYDFS 23 NYCRR 500.17 amendments tightening ransom-payment 24h disclosure operationalization"
|
|
1982
1982
|
],
|
|
1983
|
-
"signature": "
|
|
1984
|
-
"signed_at": "2026-05-
|
|
1983
|
+
"signature": "FVAXpD6sIoOLQSPtZSLLsXQnc2o2hRwiFj4xK8zEWJVkUWGqvAWRrngie7O2DRKIbWqjO5h9EevVYSzhwYHCAA==",
|
|
1984
|
+
"signed_at": "2026-05-12T17:43:06.267Z"
|
|
1985
1985
|
},
|
|
1986
1986
|
{
|
|
1987
1987
|
"name": "email-security-anti-phishing",
|
|
@@ -2033,8 +2033,8 @@
|
|
|
2033
2033
|
"cwe_refs": [],
|
|
2034
2034
|
"d3fend_refs": [],
|
|
2035
2035
|
"last_threat_review": "2026-05-11",
|
|
2036
|
-
"signature": "
|
|
2037
|
-
"signed_at": "2026-05-
|
|
2036
|
+
"signature": "0HDt3Qklee4FQeKoZfwr+8qdq2pVDS0a+c7JxVw1hV/bl8+YTPaPjPTAhQUnbhUCa5cGo7G4MBQ1AifQTMJdDA==",
|
|
2037
|
+
"signed_at": "2026-05-12T17:43:06.267Z"
|
|
2038
2038
|
},
|
|
2039
2039
|
{
|
|
2040
2040
|
"name": "age-gates-child-safety",
|
|
@@ -2101,8 +2101,8 @@
|
|
|
2101
2101
|
"France SREN (Securing and Regulating the Digital Space) Act 2024 — ARCOM age-verification referential for adult content services; double-anonymity model under deployment",
|
|
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
|
-
"signature": "
|
|
2105
|
-
"signed_at": "2026-05-
|
|
2104
|
+
"signature": "UyPSKUztZI/daHCRTnAh6ryoKLX4xyjuG+EaNMPRVuCz2gANGl1F/NozDsw7R2koMUwSFoiYTzwqDvo1tpuKAg==",
|
|
2105
|
+
"signed_at": "2026-05-12T17:43:06.267Z"
|
|
2106
2106
|
}
|
|
2107
2107
|
]
|
|
2108
2108
|
}
|
package/orchestrator/index.js
CHANGED
|
@@ -305,6 +305,19 @@ function runCurrency() {
|
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
async function runReport(format) {
|
|
308
|
+
// v0.11.6 (#98): validate format positional. Pre-0.11.6 unknown formats
|
|
309
|
+
// emitted a generic "# exceptd Report" header — silently accepted any
|
|
310
|
+
// string. Now: reject with structured JSON error matching other verbs.
|
|
311
|
+
const VALID_REPORT_FORMATS = ['executive', 'technical', 'compliance', 'csaf'];
|
|
312
|
+
if (!VALID_REPORT_FORMATS.includes(format)) {
|
|
313
|
+
process.stderr.write(JSON.stringify({
|
|
314
|
+
ok: false,
|
|
315
|
+
error: `report: format "${format}" not in accepted set ${JSON.stringify(VALID_REPORT_FORMATS)}.`,
|
|
316
|
+
verb: 'report',
|
|
317
|
+
}) + '\n');
|
|
318
|
+
process.exit(2);
|
|
319
|
+
}
|
|
320
|
+
|
|
308
321
|
// v0.11.1 feature #55: `report csaf` emits a CSAF 2.0 envelope covering
|
|
309
322
|
// every scanned finding + dispatched plan + currency posture. Useful for
|
|
310
323
|
// VEX downstreams that ingest CSAF JSON.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamejs/exceptd-skills",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
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",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"validate-package": "node lib/validate-package.js",
|
|
85
85
|
"refresh-sbom": "node scripts/refresh-sbom.js",
|
|
86
86
|
"predeploy": "node scripts/predeploy.js",
|
|
87
|
-
"prepublishOnly": "node scripts/predeploy.js && node lib/validate-package.js",
|
|
87
|
+
"prepublishOnly": "node -e \"if(process.env.EXCEPTD_SKIP_PREPUBLISH_PREDEPLOY!=='1'){const r=require('child_process').spawnSync(process.execPath,['scripts/predeploy.js'],{stdio:'inherit'});if(r.status){process.exit(r.status)}}\" && node lib/validate-package.js",
|
|
88
88
|
"test:docker": "docker build --target predeploy -t exceptd-test:predeploy -f docker/test.Dockerfile . && docker run --rm exceptd-test:predeploy",
|
|
89
89
|
"test:docker:fresh": "docker build --target fresh-bootstrap -t exceptd-test:fresh-bootstrap -f docker/test.Dockerfile . && docker run --rm exceptd-test:fresh-bootstrap",
|
|
90
90
|
"scan": "node orchestrator/index.js scan",
|
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:ff8b6540-9c5a-497c-90cb-0b6a012bab82",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-05-
|
|
7
|
+
"timestamp": "2026-05-12T17:43:07.123Z",
|
|
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.7",
|
|
17
17
|
"type": "application",
|
|
18
18
|
"name": "@blamejs/exceptd-skills",
|
|
19
|
-
"version": "0.11.
|
|
19
|
+
"version": "0.11.7",
|
|
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.7",
|
|
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.7"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"type": "vcs",
|