@blamejs/exceptd-skills 0.15.44 → 0.15.46
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/AGENTS.md +1 -1
- package/CHANGELOG.md +65 -39
- package/CONTEXT.md +1 -1
- package/README.md +5 -5
- package/bin/exceptd.js +166 -124
- package/data/_indexes/_meta.json +7 -7
- package/data/_indexes/activity-feed.json +8 -8
- package/data/_indexes/catalog-summaries.json +1 -1
- package/data/_indexes/section-offsets.json +41 -41
- package/data/_indexes/token-budget.json +32 -32
- package/data/cve-catalog.json +2 -2
- package/lib/flag-suggest.js +6 -10
- package/lib/lint-skills.js +18 -19
- package/lib/playbook-runner.js +11 -3
- package/lib/refresh-external.js +4 -2
- package/lib/source-osv.js +3 -1
- package/lib/validate-catalog-meta.js +6 -5
- package/lib/validate-cve-catalog.js +8 -8
- package/lib/validate-playbooks.js +14 -14
- package/manifest.json +47 -47
- package/orchestrator/index.js +26 -4
- package/orchestrator/scanner.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +50 -50
- package/scripts/check-test-count.js +11 -4
- package/skills/attack-surface-pentest/skill.md +6 -6
- package/skills/cloud-iam-incident/skill.md +2 -2
- package/skills/sector-financial/skill.md +1 -1
package/bin/exceptd.js
CHANGED
|
@@ -28,13 +28,10 @@
|
|
|
28
28
|
* Seven-phase playbook contract (govern → direct → look → detect →
|
|
29
29
|
* analyze → validate → close):
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* direct <playbook> Phase 2: scope the investigation.
|
|
34
|
-
* look <playbook> Phase 3: emit artifact-collection spec for agent.
|
|
31
|
+
* brief [--all] Phases 1-3 (govern/direct/look) in one info doc.
|
|
32
|
+
* <playbook> Add --phase govern|direct|look for a single phase.
|
|
35
33
|
* run <playbook> Phases 4-7 (detect/analyze/validate/close) from
|
|
36
34
|
* agent submission JSON.
|
|
37
|
-
* ingest Alias for `run` matching AGENTS.md terminology.
|
|
38
35
|
* reattest <session> Re-run a prior session and diff evidence_hash.
|
|
39
36
|
*
|
|
40
37
|
* help, --help, -h This help.
|
|
@@ -169,7 +166,7 @@ const ORCHESTRATOR_PASSTHROUGH = new Set([
|
|
|
169
166
|
"framework-gap", "framework-gap-analysis",
|
|
170
167
|
]);
|
|
171
168
|
|
|
172
|
-
//
|
|
169
|
+
// Levenshtein-1 did-you-mean for unknown verbs.
|
|
173
170
|
// Catches common single-char / transposition typos against the COMMANDS
|
|
174
171
|
// table without false-positive flood: only suggests verbs within distance
|
|
175
172
|
// 1 (one insert / delete / substitute / transpose). For typed-distance 2+
|
|
@@ -385,6 +382,7 @@ Canonical verbs
|
|
|
385
382
|
--all every playbook
|
|
386
383
|
--scope <type> system | code | service | cross-cutting
|
|
387
384
|
--directives expand directive metadata
|
|
385
|
+
--flat ungrouped list (omit scope grouping)
|
|
388
386
|
--phase <name> emit only one phase (legacy compat)
|
|
389
387
|
|
|
390
388
|
run [playbook] Phases 4-7. Auto-detects cwd context when no
|
|
@@ -433,7 +431,7 @@ Canonical verbs
|
|
|
433
431
|
2 detected/escalate, 3 ran-but-no-evidence,
|
|
434
432
|
4 blocked (ok:false), 5 jurisdiction clock started.
|
|
435
433
|
(Codes 6/7/8/9 surface on attest verify / run /
|
|
436
|
-
ai-run
|
|
434
|
+
ai-run, not ci.)
|
|
437
435
|
--all | --scope <type> | (auto-detect)
|
|
438
436
|
--max-rwep <n> cap below playbook default
|
|
439
437
|
--block-on-jurisdiction-clock
|
|
@@ -564,6 +562,31 @@ function main() {
|
|
|
564
562
|
};
|
|
565
563
|
}
|
|
566
564
|
|
|
565
|
+
// --quiet: suppress advisory stderr chatter — the "[exceptd] note:" and
|
|
566
|
+
// "[exceptd] tip:" lines, the deprecation banner, and the unsigned-
|
|
567
|
+
// attestation warning — while keeping the actual result on stdout and all
|
|
568
|
+
// errors on stderr. Narrower than --json-stdout-only, which silences ALL
|
|
569
|
+
// stderr and forces JSON output; --quiet preserves human-readable output and
|
|
570
|
+
// exit codes and only drops the non-essential advisories. Skipped when
|
|
571
|
+
// --json-stdout-only is also present (that flag already silenced everything
|
|
572
|
+
// and patched stderr first; double-wrapping would be redundant).
|
|
573
|
+
if (argv.includes("--quiet") && !argv.includes("--json-stdout-only")) {
|
|
574
|
+
global.__exceptdQuiet = true;
|
|
575
|
+
process.env.EXCEPTD_DEPRECATION_SHOWN = "1";
|
|
576
|
+
process.env.EXCEPTD_UNSIGNED_WARNED = "1";
|
|
577
|
+
const origStderrWrite = process.stderr.write.bind(process.stderr);
|
|
578
|
+
process.stderr.write = (chunk, encoding, cb) => {
|
|
579
|
+
// Drop only the advisory-prefixed lines. Contract-violation notes
|
|
580
|
+
// ("[exceptd run] ..."), error frames, and uncaught exceptions still
|
|
581
|
+
// surface so --quiet never hides why a run failed or exited non-zero.
|
|
582
|
+
if (typeof chunk === "string" && /^\[exceptd\] (note|tip):/.test(chunk)) {
|
|
583
|
+
if (typeof cb === "function") cb();
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
return origStderrWrite(chunk, encoding, cb);
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
567
590
|
if (argv.length === 0) {
|
|
568
591
|
printWelcome();
|
|
569
592
|
process.exit(0);
|
|
@@ -572,13 +595,25 @@ function main() {
|
|
|
572
595
|
const rest = argv.slice(1);
|
|
573
596
|
|
|
574
597
|
if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
575
|
-
//
|
|
598
|
+
// `exceptd help <verb>` previously dropped the
|
|
576
599
|
// verb argument and printed the top-level help. Route through the same
|
|
577
600
|
// printPlaybookVerbHelp() that `exceptd <verb> --help` already uses so
|
|
578
601
|
// operators get a consistent verb-specific help surface regardless of
|
|
579
602
|
// which way they reached it.
|
|
580
603
|
if (rest.length > 0 && typeof rest[0] === 'string' && rest[0].length > 0) {
|
|
581
604
|
const verb = rest[0];
|
|
605
|
+
// A removed verb has no live help. Refuse with the same structured
|
|
606
|
+
// removal error the bare verb emits, so `help <removed>` and
|
|
607
|
+
// `<removed> --help` agree (both exit non-zero, both name the
|
|
608
|
+
// replacement) instead of printing stale help for a verb that no
|
|
609
|
+
// longer dispatches.
|
|
610
|
+
if (REMOVED_VERBS[verb]) {
|
|
611
|
+
emitError(
|
|
612
|
+
`'${verb}' was removed in v0.13.0. Use \`exceptd ${REMOVED_VERBS[verb]}\` instead.`,
|
|
613
|
+
{ verb, removed_in: "0.13.0", replacement: REMOVED_VERBS[verb] }
|
|
614
|
+
);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
582
617
|
if (printPlaybookVerbHelp(verb)) {
|
|
583
618
|
process.exit(0);
|
|
584
619
|
}
|
|
@@ -705,7 +740,7 @@ function main() {
|
|
|
705
740
|
// UNKNOWN_COMMAND (10) afterwards. Cycle 9 split this away from
|
|
706
741
|
// DETECTED_ESCALATE (2) — the two semantics had collided since v0.12.24.
|
|
707
742
|
//
|
|
708
|
-
//
|
|
743
|
+
// add a did-you-mean suggestion when the
|
|
709
744
|
// unknown verb is within Levenshtein-1 of a real verb (catches the
|
|
710
745
|
// common single-char typos: `discoer` → `discover`, `attst` → `attest`,
|
|
711
746
|
// `valdiate-cves` → `validate-cves`).
|
|
@@ -752,7 +787,7 @@ function main() {
|
|
|
752
787
|
// verbs that lack their own help handler, so spawns that do (refresh,
|
|
753
788
|
// prefetch) keep their detailed usage.
|
|
754
789
|
const SPAWN_HELP_USAGE = {
|
|
755
|
-
skill: "exceptd skill <name> Show the full context document for one skill.",
|
|
790
|
+
skill: "exceptd skill <name> Show the full context document for one skill. Run `exceptd skill` with no arguments to list all skill IDs.",
|
|
756
791
|
"framework-gap": "exceptd framework-gap <framework> <cve-or-scenario> One-framework gap analysis.",
|
|
757
792
|
"framework-gap-analysis": "exceptd framework-gap <framework> <cve-or-scenario> One-framework gap analysis.",
|
|
758
793
|
cve: "exceptd cve <CVE-ID> [--json] [--air-gap|--no-network] Resolve a CVE: published/rejected/disputed/fabricated/nonexistent (catalog -> cache -> NVD). Exit 2 when the citation won't stand up (rejected/fabricated/nonexistent/withdrawn).",
|
|
@@ -761,7 +796,7 @@ function main() {
|
|
|
761
796
|
// to spawning the blocking daemon, hanging the operator's terminal.
|
|
762
797
|
watch: "exceptd watch Long-running forward-watch daemon (blocks; Ctrl-C to stop). For a one-shot aggregator use `exceptd watchlist`.",
|
|
763
798
|
watchlist: "exceptd watchlist [--alerts] [--org-scan --org <login>] [--by-skill] [--json] One-shot forward-watch aggregator across skills.",
|
|
764
|
-
report: "exceptd report [executive] [--json] Structured posture report.",
|
|
799
|
+
report: "exceptd report [executive] [--json] Structured posture report. Markdown by default; pass --json for machine-readable output.",
|
|
765
800
|
scan: "exceptd scan [--json] [legacy] Working-directory CVE/KEV scan (orchestrator). See `exceptd discover`.",
|
|
766
801
|
dispatch: "exceptd dispatch [--json] [legacy] Scan + route findings to skills (orchestrator). See `exceptd discover`.",
|
|
767
802
|
currency: "exceptd currency [--json] [legacy] Skill threat-currency report. See `exceptd doctor --currency`.",
|
|
@@ -981,7 +1016,7 @@ function asEvidenceObject(parsed) {
|
|
|
981
1016
|
return parsed;
|
|
982
1017
|
}
|
|
983
1018
|
|
|
984
|
-
function readEvidence(evidenceFlag) {
|
|
1019
|
+
function readEvidence(evidenceFlag, opts = {}) {
|
|
985
1020
|
if (!evidenceFlag) return {};
|
|
986
1021
|
// v0.12.12: file-path branch enforces a max size to defend against an
|
|
987
1022
|
// operator accidentally passing a multi-gigabyte file (binary, log, or
|
|
@@ -1009,7 +1044,7 @@ function readEvidence(evidenceFlag) {
|
|
|
1009
1044
|
}
|
|
1010
1045
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
1011
1046
|
if (!text.trim()) {
|
|
1012
|
-
//
|
|
1047
|
+
// pre-fix empty stdin silently became {}
|
|
1013
1048
|
// — operator got a "successful" run on no evidence with no warning,
|
|
1014
1049
|
// and the evidence_hash for `{}` is deterministic so subsequent
|
|
1015
1050
|
// runs didn't even reveal the mistake. Emit a stderr nudge so the
|
|
@@ -1017,11 +1052,20 @@ function readEvidence(evidenceFlag) {
|
|
|
1017
1052
|
// certainly meant to pipe something. Don't change exit semantics;
|
|
1018
1053
|
// the empty-payload path is still legitimately useful for posture-
|
|
1019
1054
|
// only playbooks (govern + direct + look-only walks).
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1055
|
+
//
|
|
1056
|
+
// Only nudge when `--evidence -` was EXPLICITLY requested. On the stdin
|
|
1057
|
+
// auto-promotion path (no --evidence flag, just a non-TTY handle such as
|
|
1058
|
+
// `run kernel </dev/null` or a CI runner) the operator never asked to
|
|
1059
|
+
// read stdin, so an empty read is not a mistake to flag — and emitting to
|
|
1060
|
+
// stderr there corrupted `run ... 2>&1 | jq` pipelines that worked at a
|
|
1061
|
+
// TTY but broke in CI.
|
|
1062
|
+
if (opts.explicit !== false) {
|
|
1063
|
+
process.stderr.write(
|
|
1064
|
+
`[exceptd] note: --evidence - read 0 bytes from stdin. Treating as empty evidence {}. ` +
|
|
1065
|
+
`If you meant to pipe a submission, run \`exceptd brief <playbook>\` to see the expected shape; ` +
|
|
1066
|
+
`if you wanted a posture-only walk, this message is informational and the run will proceed.\n`,
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1025
1069
|
return {};
|
|
1026
1070
|
}
|
|
1027
1071
|
return asEvidenceObject(JSON.parse(text));
|
|
@@ -1104,7 +1148,7 @@ function hasReadableStdin() {
|
|
|
1104
1148
|
// PowerShell / MSYS pipes working (isTTY === false when piped). Do NOT
|
|
1105
1149
|
// gate on size > 0 here: a Windows pipe with bytes queued reports as
|
|
1106
1150
|
// a regular file with size 0, and gating would silently skip every
|
|
1107
|
-
// `echo {...} | exceptd run|
|
|
1151
|
+
// `echo {...} | exceptd run|ai-run` invocation.
|
|
1108
1152
|
if (process.platform === "win32" && process.stdin.isTTY === false) return true;
|
|
1109
1153
|
return false;
|
|
1110
1154
|
}
|
|
@@ -1527,16 +1571,15 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
1527
1571
|
}
|
|
1528
1572
|
|
|
1529
1573
|
// --csaf-status and --publisher-namespace shape the CSAF bundle emitted by
|
|
1530
|
-
// phases 5-7. Verbs that don't drive those phases (brief,
|
|
1531
|
-
//
|
|
1532
|
-
//
|
|
1533
|
-
//
|
|
1534
|
-
//
|
|
1535
|
-
//
|
|
1536
|
-
//
|
|
1537
|
-
// to run.
|
|
1574
|
+
// phases 5-7. Verbs that don't drive those phases (brief, attest,
|
|
1575
|
+
// list-attestations, discover, doctor, lint, ask, verify-attestation,
|
|
1576
|
+
// reattest) never assemble a bundle, so silently consuming these flags is
|
|
1577
|
+
// a UX trap. Refuse on those verbs so the operator knows the flag was
|
|
1578
|
+
// discarded — same pattern as --ack. Error message templates and emitError
|
|
1579
|
+
// prefixes use the in-scope `cmd` verb so a brief invocation says "brief:"
|
|
1580
|
+
// rather than misattributing the flag to run.
|
|
1538
1581
|
const BUNDLE_FLAG_RELEVANT_VERBS = new Set([
|
|
1539
|
-
"run", "ci", "run-all", "ai-run",
|
|
1582
|
+
"run", "ci", "run-all", "ai-run",
|
|
1540
1583
|
]);
|
|
1541
1584
|
|
|
1542
1585
|
// --publisher-namespace <url> threads into the CSAF
|
|
@@ -1688,15 +1731,14 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
1688
1731
|
// consent was explicit vs. implicit. AGENTS.md says the AI should surface
|
|
1689
1732
|
// and wait for ack — this is how the ack gets recorded.
|
|
1690
1733
|
//
|
|
1691
|
-
// --ack only makes sense on verbs that drive phases 5-7 (run /
|
|
1692
|
-
//
|
|
1693
|
-
//
|
|
1694
|
-
//
|
|
1695
|
-
//
|
|
1696
|
-
//
|
|
1697
|
-
// is irrelevant.
|
|
1734
|
+
// --ack only makes sense on verbs that drive phases 5-7 (run / ai-run /
|
|
1735
|
+
// ci / run-all / reattest). Info-only verbs (brief, attest,
|
|
1736
|
+
// list-attestations, discover, doctor, lint, ask, verify-attestation)
|
|
1737
|
+
// never consume an attestation clock — accepting --ack silently is a UX
|
|
1738
|
+
// trap where operators believe they have recorded consent. Refuse on those
|
|
1739
|
+
// verbs so the operator knows the flag is irrelevant.
|
|
1698
1740
|
const ACK_RELEVANT_VERBS = new Set([
|
|
1699
|
-
"run", "
|
|
1741
|
+
"run", "ai-run", "ci", "run-all", "reattest",
|
|
1700
1742
|
]);
|
|
1701
1743
|
if (args.ack) {
|
|
1702
1744
|
if (!ACK_RELEVANT_VERBS.has(cmd)) {
|
|
@@ -1853,44 +1895,13 @@ function editDistance(a, b) {
|
|
|
1853
1895
|
|
|
1854
1896
|
function printPlaybookVerbHelp(verb) {
|
|
1855
1897
|
const cmds = {
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
Flags:
|
|
1859
|
-
--playbook <id> ... Filter to one or more playbook IDs.
|
|
1860
|
-
--scope <type> Filter by scope: system | code | service | cross-cutting | all
|
|
1861
|
-
--flat Disable grouped-by-scope output; emit flat list.
|
|
1862
|
-
--directives Include directive id + title + applies_to per playbook.
|
|
1863
|
-
--session-id <id> Reuse a specific session ID for the planning output.
|
|
1864
|
-
--mode <m> Investigation mode forwarded into govern.
|
|
1865
|
-
--pretty Indented JSON output.`,
|
|
1866
|
-
govern: `govern <playbook> — phase 1, load GRC context for a playbook.
|
|
1867
|
-
|
|
1868
|
-
Args / flags:
|
|
1869
|
-
<playbook> Playbook ID. Required positional.
|
|
1870
|
-
--directive <id> Specific directive (default: first one).
|
|
1871
|
-
--mode <m> Investigation mode forwarded into govern policy.
|
|
1872
|
-
--air-gap Honor _meta.air_gap_mode + air_gap_alternative paths.
|
|
1873
|
-
--pretty Indented JSON output.
|
|
1874
|
-
|
|
1875
|
-
Output: jurisdiction_obligations, theater_fingerprints, framework_context, skill_preload.`,
|
|
1876
|
-
direct: `direct <playbook> — phase 2, threat context + skill chain + token budget.
|
|
1898
|
+
recipes: `recipes [<id>] — curated multi-skill workflows (use-case → ordered skill chain).
|
|
1877
1899
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
--directive <id> Specific directive (default: first one).
|
|
1881
|
-
--pretty Indented JSON output.`,
|
|
1882
|
-
look: `look <playbook> — phase 3, artifact-collection spec the host AI executes.
|
|
1883
|
-
|
|
1884
|
-
Args / flags:
|
|
1885
|
-
<playbook> Required positional.
|
|
1886
|
-
--directive <id> Specific directive (default: first one).
|
|
1887
|
-
--air-gap Honor air_gap_alternative paths.
|
|
1888
|
-
--pretty Indented JSON output.
|
|
1900
|
+
With no id: lists every recipe with its "when to use" guidance.
|
|
1901
|
+
With <id>: expands that recipe's ordered skill_chain and notes.
|
|
1889
1902
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
{ "precondition_checks": { "<id>": true | false } }
|
|
1893
|
-
The runner refuses the run if a precondition with on_fail=halt is unverified.`,
|
|
1903
|
+
Flags:
|
|
1904
|
+
--json Machine-readable output.`,
|
|
1894
1905
|
run: `run [playbook] — phases 4-7 (detect → analyze → validate → close).
|
|
1895
1906
|
|
|
1896
1907
|
Invocation modes:
|
|
@@ -2006,36 +2017,6 @@ Other operator-facing flags (full list in source; surfaced here for grep):
|
|
|
2006
2017
|
--attestation-root <p> Override .exceptd/ root for this run.
|
|
2007
2018
|
--mode <m> Investigation mode (self_service | authorized_pentest
|
|
2008
2019
|
| ir_response | ctf | research | compliance_audit).`,
|
|
2009
|
-
ingest: `ingest — alias for 'run' matching AGENTS.md terminology.
|
|
2010
|
-
|
|
2011
|
-
Flags:
|
|
2012
|
-
--domain <id> Playbook ID (overrides submission.playbook_id).
|
|
2013
|
-
--directive <id> Directive ID (overrides submission.directive_id).
|
|
2014
|
-
--evidence <file|-> Submission JSON. May include playbook_id/directive_id.
|
|
2015
|
-
--session-id <id> Reuse a specific session id (must satisfy
|
|
2016
|
-
/^[A-Za-z0-9._-]{1,64}$/).
|
|
2017
|
-
--force-overwrite Override session-id collision refusal.
|
|
2018
|
-
--operator <name> Bind attestation to a specific identity.
|
|
2019
|
-
--ack Explicit operator consent for jurisdiction clock.
|
|
2020
|
-
--attestation-root <p> Override .exceptd/ root for this ingest.
|
|
2021
|
-
--mode <m> Investigation mode (self_service | authorized_pentest
|
|
2022
|
-
| ir_response | ctf | research | compliance_audit).
|
|
2023
|
-
--air-gap Honor air_gap_alternative paths.
|
|
2024
|
-
--force-stale Override threat_currency_score<50 gate.
|
|
2025
|
-
--csaf-status <s> CSAF tracking.status for the close.evidence_package
|
|
2026
|
-
bundle. One of: draft | interim (default) | final.
|
|
2027
|
-
'final' commits to CSAF §3.1.11.3.5.1 immutability —
|
|
2028
|
-
set this only after operator review of the advisory.
|
|
2029
|
-
--publisher-namespace <url>
|
|
2030
|
-
CSAF document.publisher.namespace (§3.1.7.4). The
|
|
2031
|
-
operator's organisation URL, NOT the tooling vendor.
|
|
2032
|
-
Must be an http://… or https://… URL, ≤256 chars.
|
|
2033
|
-
--bundle-deterministic Emit byte-stable bundles (frozen timestamps).
|
|
2034
|
-
--bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.
|
|
2035
|
-
--pretty Indented JSON output.
|
|
2036
|
-
|
|
2037
|
-
Exit codes: 0 PASS, 1 framework, 4 blocked, 7 SESSION_ID_COLLISION,
|
|
2038
|
-
8 LOCK_CONTENTION, 9 STORAGE_EXHAUSTED.`,
|
|
2039
2020
|
reattest: `reattest [<session-id> | --latest] — replay a prior session and diff the evidence_hash.
|
|
2040
2021
|
|
|
2041
2022
|
Args / flags:
|
|
@@ -2061,7 +2042,7 @@ Lists every attestation under .exceptd/attestations/<session_id>/, sorted
|
|
|
2061
2042
|
newest-first, with truncated evidence_hash + capture timestamp + file path.`,
|
|
2062
2043
|
attest: `attest <subverb> <session-id> — auditor-facing attestation operations.
|
|
2063
2044
|
|
|
2064
|
-
Subverbs (list | show | export | verify | diff):
|
|
2045
|
+
Subverbs (list | show | export | verify | diff | prune):
|
|
2065
2046
|
attest show <sid> Emit the full (unredacted) attestation.
|
|
2066
2047
|
attest list Inventory every prior attestation under
|
|
2067
2048
|
~/.exceptd/attestations/ (or EXCEPTD_HOME when set).
|
|
@@ -2085,6 +2066,9 @@ Subverbs (list | show | export | verify | diff):
|
|
|
2085
2066
|
for the same playbook, or against --against <other-sid>
|
|
2086
2067
|
for an explicit pair. Reports unchanged | drifted |
|
|
2087
2068
|
resolved per evidence_hash + classification deltas.
|
|
2069
|
+
attest prune GC stale sessions: delete attestations older than
|
|
2070
|
+
--all-older-than <ISO>. --dry-run previews the set
|
|
2071
|
+
without deleting.
|
|
2088
2072
|
|
|
2089
2073
|
All subverbs honor --pretty for indented JSON output.
|
|
2090
2074
|
|
|
@@ -2200,7 +2184,7 @@ Flags:
|
|
|
2200
2184
|
Exit codes:
|
|
2201
2185
|
0 done Run completed; emitted {"event":"done","ok":true}.
|
|
2202
2186
|
1 framework error Engine threw or stdin parse failure.
|
|
2203
|
-
|
|
2187
|
+
7 SESSION_ID_COLLISION --session-id duplicate; pass --force-overwrite or fresh id.
|
|
2204
2188
|
8 LOCK_CONTENTION Concurrent persistAttestation lock held.
|
|
2205
2189
|
9 STORAGE_EXHAUSTED Disk/quota/RO filesystem on attestation write.
|
|
2206
2190
|
|
|
@@ -2311,7 +2295,7 @@ Exit codes:
|
|
|
2311
2295
|
etc.) and the operator has not acked.
|
|
2312
2296
|
|
|
2313
2297
|
(ci does not persist attestations per-run; exit codes 6/7/8/9 surface on
|
|
2314
|
-
\`attest verify\` and on \`run\` / \`ai-run
|
|
2298
|
+
\`attest verify\` and on \`run\` / \`ai-run\`, not on \`ci\`.)
|
|
2315
2299
|
|
|
2316
2300
|
Output: verb, session_id, playbooks_run, summary{total, detected,
|
|
2317
2301
|
max_rwep_observed, jurisdiction_clocks_started, verdict, fail_reasons[]},
|
|
@@ -2332,9 +2316,10 @@ Flags:
|
|
|
2332
2316
|
submission, not a human digest).`,
|
|
2333
2317
|
brief: `brief [playbook] — unified info doc (v0.11.0).
|
|
2334
2318
|
|
|
2335
|
-
Collapses the
|
|
2336
|
-
|
|
2337
|
-
informational; brief reads them in one
|
|
2319
|
+
Collapses the info-only phases govern + direct + look into a single document,
|
|
2320
|
+
and replaces the removed plan / govern / direct / look verbs. Phases 1-3 of
|
|
2321
|
+
the seven-phase contract are entirely informational; brief reads them in one
|
|
2322
|
+
CLI invocation instead of three.
|
|
2338
2323
|
|
|
2339
2324
|
Modes:
|
|
2340
2325
|
brief Auto-detect playbooks for the cwd. Returns a list.
|
|
@@ -2348,6 +2333,8 @@ Modes:
|
|
|
2348
2333
|
|
|
2349
2334
|
Flags:
|
|
2350
2335
|
--directives Expand directive metadata per playbook.
|
|
2336
|
+
--flat Ungrouped playbook list (omit grouped_by_scope +
|
|
2337
|
+
scope_summary). Use with --all / --scope.
|
|
2351
2338
|
--pretty Indented JSON output.
|
|
2352
2339
|
--json Force single-line JSON.
|
|
2353
2340
|
|
|
@@ -2390,7 +2377,7 @@ Flags (selected — see \`exceptd run --help\` for the full list):
|
|
|
2390
2377
|
--bundle-deterministic Emit byte-stable bundles across the multi-run set.
|
|
2391
2378
|
--bundle-epoch <ISO> Frozen epoch for --bundle-deterministic.`,
|
|
2392
2379
|
};
|
|
2393
|
-
//
|
|
2380
|
+
// return whether a verb-specific help block was
|
|
2394
2381
|
// found so the `exceptd help <verb>` caller can decide whether to fall
|
|
2395
2382
|
// through to the top-level help (verb unknown) or stop here (verb known).
|
|
2396
2383
|
if (cmds[verb]) {
|
|
@@ -2884,7 +2871,8 @@ function cmdBrief(runner, args, runOpts, pretty) {
|
|
|
2884
2871
|
lines.push(` ${p.id} (${p.on_fail}): ${pdesc.length > 80 ? pdesc.slice(0, 80) + "…" : pdesc}`);
|
|
2885
2872
|
}
|
|
2886
2873
|
}
|
|
2887
|
-
lines.push(`\
|
|
2874
|
+
lines.push(`\nCollect evidence: exceptd collect ${obj.playbook_id} | exceptd run ${obj.playbook_id} --evidence -`);
|
|
2875
|
+
lines.push(`Run with your own evidence: exceptd run ${obj.playbook_id} --evidence <file|-> --json`);
|
|
2888
2876
|
lines.push(`Full structured doc: --json or --pretty`);
|
|
2889
2877
|
return lines.join("\n");
|
|
2890
2878
|
});
|
|
@@ -2946,7 +2934,7 @@ function cmdPlan(runner, args, runOpts, pretty) {
|
|
|
2946
2934
|
}
|
|
2947
2935
|
}
|
|
2948
2936
|
emit(plan, pretty, (obj) => {
|
|
2949
|
-
// Human renderer for `brief` / `brief --all
|
|
2937
|
+
// Human renderer for `brief` / `brief --all`. Pre-fix this
|
|
2950
2938
|
// verb dumped 36+ KB of JSON to the terminal — operators running
|
|
2951
2939
|
// `exceptd brief` to explore had no scannable view.
|
|
2952
2940
|
const lines = [];
|
|
@@ -3223,6 +3211,19 @@ function cmdRun(runner, args, runOpts, pretty) {
|
|
|
3223
3211
|
// Single-playbook path (existing behavior).
|
|
3224
3212
|
const playbookId = positional;
|
|
3225
3213
|
if (refuseInvalidPlaybookId("run", playbookId, pretty)) return;
|
|
3214
|
+
// --evidence-dir is a contract input: cmdRunMulti reads one
|
|
3215
|
+
// <playbook-id>.json per playbook in an --all / --scope run. With a single
|
|
3216
|
+
// named playbook it was silently ignored, so `run secrets --evidence-dir ./ev`
|
|
3217
|
+
// ran against EMPTY evidence and reported a clean "not_detected" verdict — a
|
|
3218
|
+
// falsely-reassuring result from a security tool. Refuse loudly and point the
|
|
3219
|
+
// operator at the flag that actually loads evidence for one playbook.
|
|
3220
|
+
if (args["evidence-dir"]) {
|
|
3221
|
+
return emitError(
|
|
3222
|
+
`run ${playbookId}: --evidence-dir applies to contract runs (exceptd run --all / --scope <type>), where it reads one <playbook-id>.json per playbook. For a single playbook, pass its evidence directly: exceptd collect ${playbookId} | exceptd run ${playbookId} --evidence - (or --evidence ${playbookId}.json).`,
|
|
3223
|
+
{ playbook: playbookId, provided: "--evidence-dir", use_instead: "--evidence <file|->" },
|
|
3224
|
+
pretty
|
|
3225
|
+
);
|
|
3226
|
+
}
|
|
3226
3227
|
const pb = runner.loadPlaybook(playbookId);
|
|
3227
3228
|
const directiveId = args.directive || (pb.directives[0] && pb.directives[0].id);
|
|
3228
3229
|
if (!directiveId) return refuseNoDirectives("run", playbookId, pretty);
|
|
@@ -3286,12 +3287,16 @@ function cmdRun(runner, args, runOpts, pretty) {
|
|
|
3286
3287
|
// first, then falls back to a strict isTTY===false check only on Windows
|
|
3287
3288
|
// (where fstat on a pipe is unreliable). MSYS-bash on win32 reports
|
|
3288
3289
|
// isTTY === false for genuine piped input, so that path still works.
|
|
3289
|
-
|
|
3290
|
+
const autoStdin = !args.evidence && hasReadableStdin();
|
|
3291
|
+
if (autoStdin) {
|
|
3290
3292
|
args.evidence = "-";
|
|
3291
3293
|
}
|
|
3292
3294
|
if (args.evidence) {
|
|
3293
3295
|
try {
|
|
3294
|
-
|
|
3296
|
+
// explicit:false on the auto-promotion path suppresses the empty-stdin
|
|
3297
|
+
// nudge (which otherwise writes to stderr and breaks `run ... 2>&1 | jq`
|
|
3298
|
+
// on every no-evidence CI run); an explicit `--evidence -` still nudges.
|
|
3299
|
+
submission = readEvidence(args.evidence, { explicit: !autoStdin });
|
|
3295
3300
|
} catch (e) {
|
|
3296
3301
|
return emitError(`run: failed to read evidence: ${e.message}`, { evidence: args.evidence }, pretty);
|
|
3297
3302
|
}
|
|
@@ -3531,7 +3536,40 @@ function cmdRun(runner, args, runOpts, pretty) {
|
|
|
3531
3536
|
// Set exitCode BEFORE emit(): emit's ok:false fallback only fires when
|
|
3532
3537
|
// exitCode is not already set, so the BLOCKED override survives.
|
|
3533
3538
|
process.exitCode = args.ci ? EXIT_CODES.BLOCKED : EXIT_CODES.GENERIC_FAILURE;
|
|
3534
|
-
emit(result, pretty)
|
|
3539
|
+
emit(result, pretty, (obj) => {
|
|
3540
|
+
// Human renderer for a halted run. Without this, a blocked verdict
|
|
3541
|
+
// (preflight precondition unmet, mutex conflict, stale currency,
|
|
3542
|
+
// corrupt catalog) dumped the raw ok:false JSON envelope even in human
|
|
3543
|
+
// mode — so a non-Linux operator's first `run` against any Linux-gated
|
|
3544
|
+
// playbook was a wall of JSON instead of one line saying why it stopped
|
|
3545
|
+
// and what to do. --json / --pretty still return the full envelope.
|
|
3546
|
+
const v = obj.verdict || "error";
|
|
3547
|
+
const tag = v === "blocked" ? "[blocked]" : "[error]";
|
|
3548
|
+
const lines = [`${tag} ${obj.playbook_id || "run"}${obj.directive_id ? ` (${obj.directive_id})` : ""}`];
|
|
3549
|
+
// summary_line is already a complete sentence ("<pb>: blocked at
|
|
3550
|
+
// preflight (<cause>) — <reason>"); prefer it, else fall back to reason.
|
|
3551
|
+
const detail = obj.summary_line || obj.reason;
|
|
3552
|
+
if (detail) lines.push(` ${detail}`);
|
|
3553
|
+
// remediation is the engine's own actionable next step when it has one;
|
|
3554
|
+
// otherwise synthesize a hint from blocked_by so the operator never hits
|
|
3555
|
+
// a dead end. Hints reference only current verbs (plan/direct were
|
|
3556
|
+
// removed in v0.13.0; brief --all is the replacement listing verb).
|
|
3557
|
+
if (obj.remediation) {
|
|
3558
|
+
lines.push(` → ${obj.remediation}`);
|
|
3559
|
+
} else {
|
|
3560
|
+
const hints = {
|
|
3561
|
+
precondition: "→ Preconditions are not met on this host (often a platform gate, e.g. a Linux-only playbook). List playbooks that fit your platform: exceptd brief --all",
|
|
3562
|
+
mutex: "→ Another run holds this playbook's mutex. Wait for it to finish, then retry.",
|
|
3563
|
+
currency: "→ Threat intel is stale. Refresh sources (exceptd refresh) or re-run with --force-stale to override.",
|
|
3564
|
+
catalog_corrupt: "→ The CVE catalog failed to load. Reinstall the package or run: exceptd doctor",
|
|
3565
|
+
playbook_not_found: "→ Unknown playbook. List available playbooks: exceptd brief --all",
|
|
3566
|
+
directive_not_found: `→ Unknown directive for this playbook. See its directives: exceptd brief ${obj.playbook_id || "<playbook>"}`,
|
|
3567
|
+
};
|
|
3568
|
+
if (obj.blocked_by && hints[obj.blocked_by]) lines.push(` ${hints[obj.blocked_by]}`);
|
|
3569
|
+
}
|
|
3570
|
+
lines.push(" Full envelope: re-run with --json");
|
|
3571
|
+
return lines.join("\n");
|
|
3572
|
+
});
|
|
3535
3573
|
return;
|
|
3536
3574
|
}
|
|
3537
3575
|
|
|
@@ -4288,9 +4326,11 @@ function cmdRunMulti(runner, ids, args, runOpts, pretty, meta) {
|
|
|
4288
4326
|
// the aggregate JSON emitted above is allowed to fully drain.
|
|
4289
4327
|
//
|
|
4290
4328
|
// Aggregate exit-code precedence: LOCK_CONTENTION > STORAGE_EXHAUSTED >
|
|
4291
|
-
//
|
|
4292
|
-
// storage exhaustion is an infra event
|
|
4293
|
-
//
|
|
4329
|
+
// SESSION_ID_COLLISION > GENERIC_FAILURE. Lock contention is transient
|
|
4330
|
+
// (retry-from-outside fixes it); storage exhaustion is an infra event
|
|
4331
|
+
// requiring operator action; a session-id collision mirrors the single-run
|
|
4332
|
+
// code; any remaining ok:false per-playbook result yields GENERIC_FAILURE
|
|
4333
|
+
// (exit 1) — distinct from the single-run BLOCKED (4) path. Surfacing the
|
|
4294
4334
|
// most-specific code first means a CI gate can branch on the right
|
|
4295
4335
|
// remediation without parsing the body.
|
|
4296
4336
|
const anyLockBusy = results.some(r => r.attestation_persist && r.attestation_persist.lock_contention === true);
|
|
@@ -6386,8 +6426,10 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
6386
6426
|
"json", "pretty", "fix", "air-gap",
|
|
6387
6427
|
"signatures", "currency", "cves", "rfcs", "registry-check",
|
|
6388
6428
|
"ai-config", "collectors", "exit-codes", "shipped-tarball",
|
|
6389
|
-
// Global flags the parser may inject regardless of verb.
|
|
6390
|
-
|
|
6429
|
+
// Global flags the parser may inject regardless of verb. Keep in sync
|
|
6430
|
+
// with VERB_FLAG_ALLOWLIST._global in lib/flag-suggest.js — quiet/verbose
|
|
6431
|
+
// are accepted on every verb, so doctor must not refuse them as typos.
|
|
6432
|
+
"_", "json-stdout-only", "_jsonMode", "quiet", "verbose",
|
|
6391
6433
|
]);
|
|
6392
6434
|
const unknownFlags = Object.keys(args).filter(k => !KNOWN_DOCTOR_FLAGS.has(k));
|
|
6393
6435
|
if (unknownFlags.length > 0) {
|
|
@@ -7285,7 +7327,7 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
7285
7327
|
if (c.ahead) {
|
|
7286
7328
|
return `npm registry: local v${c.local_version ?? "?"} AHEAD of published v${c.published_version ?? "?"} (unreleased / dev install)`;
|
|
7287
7329
|
}
|
|
7288
|
-
return
|
|
7330
|
+
return "npm registry: could not compare versions (registry unreachable, offline, or no published version yet). Run `npm view @blamejs/exceptd-skills version` to see the latest, then `npm install -g @blamejs/exceptd-skills@latest` if you are behind.";
|
|
7289
7331
|
});
|
|
7290
7332
|
// v0.12.9 (P3 #10): surface shipped_tarball sub-check when --shipped-tarball was used.
|
|
7291
7333
|
if (checks.signatures?.shipped_tarball) {
|
|
@@ -7412,7 +7454,7 @@ function cmdListAttestations(runner, args, runOpts, pretty) {
|
|
|
7412
7454
|
}
|
|
7413
7455
|
// Enumerate sessions across both v0.11.0 default root and legacy cwd-
|
|
7414
7456
|
// relative root, so operators with prior attestations still see them.
|
|
7415
|
-
//
|
|
7457
|
+
// also track candidate roots that didn't exist
|
|
7416
7458
|
// so operators can tell whether the directory was scanned-and-empty or
|
|
7417
7459
|
// simply never created. Pre-fix the human output said "(no attestations
|
|
7418
7460
|
// under )" with no path — operators couldn't see where the verb looked.
|
|
@@ -7499,7 +7541,7 @@ function cmdListAttestations(runner, args, runOpts, pretty) {
|
|
|
7499
7541
|
limit: limitN,
|
|
7500
7542
|
filter: { playbook: playbookFilter ? [...playbookFilter] : null, since: args.since || null },
|
|
7501
7543
|
roots_searched: [...seenRoots],
|
|
7502
|
-
//
|
|
7544
|
+
// every candidate root + whether it existed,
|
|
7503
7545
|
// so JSON consumers can distinguish scanned-and-empty from never-created.
|
|
7504
7546
|
// The human renderer below also surfaces this rather than printing
|
|
7505
7547
|
// "(no attestations under )" with an empty path list.
|
|
@@ -8301,7 +8343,7 @@ function cmdCi(runner, args, runOpts, pretty) {
|
|
|
8301
8343
|
// --scope and --all. Operators specifying an explicit set get exactly that
|
|
8302
8344
|
// set, no more, no less. Pre-0.11.9 the flag was silently ignored.
|
|
8303
8345
|
let ids;
|
|
8304
|
-
//
|
|
8346
|
+
// positional args (`exceptd ci kernel cred-stores`)
|
|
8305
8347
|
// were silently ignored and the cwd-autodetect path ran instead. Operators
|
|
8306
8348
|
// got a green PASS for playbooks that were never actually executed. Treat
|
|
8307
8349
|
// positional args as an inline --required, with the same unknown-id refusal.
|
|
@@ -8437,7 +8479,7 @@ function cmdCi(runner, args, runOpts, pretty) {
|
|
|
8437
8479
|
let clockStartedReasons = [];
|
|
8438
8480
|
|
|
8439
8481
|
for (const id of ids) {
|
|
8440
|
-
//
|
|
8482
|
+
// defense-in-depth — validate id even though the catalog-iter
|
|
8441
8483
|
// upstream is trusted. A corrupt catalog returning a malformed id would
|
|
8442
8484
|
// otherwise reach loadPlaybook unchecked. Matches the cmdRunMulti pattern.
|
|
8443
8485
|
const idCheck = validateIdComponent(id, "playbook");
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-30T18:00:00.612Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 54,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "cfe4088da8f1fdddb4218f88bbadce04004046ad7105c5e16cc58fdf1aa958b8",
|
|
8
8
|
"data/atlas-ttps.json": "878b4a08bb73c8d20396d85cf433a88f2bc5e7a8cbf7f6ab773ce7ede0a11251",
|
|
9
9
|
"data/attack-techniques.json": "84fad74c8497cab922ed64b814752f54aa4620c2a938cb06642ff1510e1c5cb3",
|
|
10
|
-
"data/cve-catalog.json": "
|
|
10
|
+
"data/cve-catalog.json": "7a5f4e31401505e53330cdc4b54b39f8a8b04459d6b9411676d291c583ae535f",
|
|
11
11
|
"data/cwe-catalog.json": "0fd275c2a61754958d68cea03a92794a67cf1c1d4d609f81a5728334df013ee3",
|
|
12
12
|
"data/d3fend-catalog.json": "9a54bccb9f24f84b32024216cc3f53819a053721ac8ab43c326859e68fc0ffaf",
|
|
13
13
|
"data/dlp-controls.json": "d2406c482dddd30e49203879999dc4b3a7fd4d0494d6a61d86b91ee76415df19",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"skills/skill-update-loop/skill.md": "16cd5a61ccd87c61901e9b209fff0e26ca6540c0cfcb8e231ac17917c50d56bb",
|
|
33
33
|
"skills/security-maturity-tiers/skill.md": "de7a67b1f6ae79be490656939ac59b5772aa648dae4759733d80d6bf4595c278",
|
|
34
34
|
"skills/researcher/skill.md": "9f1211d177c64e4c465407a45ad9e2901c5c6c0af410a0d0a51cc8fb780420d4",
|
|
35
|
-
"skills/attack-surface-pentest/skill.md": "
|
|
35
|
+
"skills/attack-surface-pentest/skill.md": "8d1137c3270763f1c90a3fa8c1c19ab5dc769623c1a35d6a71859bdb8cca2a3e",
|
|
36
36
|
"skills/fuzz-testing-strategy/skill.md": "07e2ee5f773a3f0e82bd21b8a7e8cf6d5b1a8bf3ac6f71602f16550561ade553",
|
|
37
37
|
"skills/dlp-gap-analysis/skill.md": "89dedc6c062fa2afd2284e608f4a51effda819e9288fbf38ab16a7891ccd8a10",
|
|
38
38
|
"skills/supply-chain-integrity/skill.md": "7c568ee9805f4c822c16c266348e35fa6f2d7a3c76135fa34b0cfa77f003a878",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"skills/webapp-security/skill.md": "9dc8c0e51c78ad93ef9de91dd9054370dfebeea2161a87f909202ecacfad1504",
|
|
45
45
|
"skills/ai-risk-management/skill.md": "3e116dc6f03f31e32f1ee885516d72d9c11d3ff67d2184108b13dcbdf5f417bf",
|
|
46
46
|
"skills/sector-healthcare/skill.md": "148520af64959a60018a24f4368670925980db3e73aa09af73194f8ea61f1fcc",
|
|
47
|
-
"skills/sector-financial/skill.md": "
|
|
47
|
+
"skills/sector-financial/skill.md": "ad33faa8dddbeb23fed88f464205c630e3fa50c669d3e1ba7ed54f23719efd55",
|
|
48
48
|
"skills/sector-federal-government/skill.md": "870dead2eae1b2664b1e151dd73d8fa240a62a297bdbcddee37bd1cb60e5e5f4",
|
|
49
49
|
"skills/sector-energy/skill.md": "432213dfc9ee271631ce3171daf62a103a010b27a51911dd1112bd5d8bc6c152",
|
|
50
50
|
"skills/sector-telecom/skill.md": "4b80771e78a474e3f43227ecc730ddda1684bff98d7e6e53f5ec373e1e886f34",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"skills/ransomware-response/skill.md": "d0f456f1c31ec2968bb4c2cea67eb628d5baf857f17650ab204cf7931b3317ef",
|
|
57
57
|
"skills/email-security-anti-phishing/skill.md": "0965eca982e8fc633b85e70c0ba6becb8c0f5ee7bdd0be96ad73a9a222bb8816",
|
|
58
58
|
"skills/age-gates-child-safety/skill.md": "6d4d29e54a115314c3c0ea9f5df47bdc2828f3b226fff4b5974d898b56c0cd73",
|
|
59
|
-
"skills/cloud-iam-incident/skill.md": "
|
|
59
|
+
"skills/cloud-iam-incident/skill.md": "6aab2e400d1e87df7ac2b6f0a17dac6aa99723b217258c4a7b446703d1521775",
|
|
60
60
|
"skills/idp-incident-response/skill.md": "cb2f2c5b90de4592bfd66dcd55f9bf2004f370746d519cad577fcbaf36125878"
|
|
61
61
|
},
|
|
62
62
|
"skill_count": 42,
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"handoff_dag_nodes": 42,
|
|
79
79
|
"summary_cards": 42,
|
|
80
80
|
"section_offsets_skills": 42,
|
|
81
|
-
"token_budget_total_approx":
|
|
81
|
+
"token_budget_total_approx": 418426,
|
|
82
82
|
"recipes": 8,
|
|
83
83
|
"jurisdiction_clocks": 29,
|
|
84
84
|
"did_ladders": 8,
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
"event_count": 54
|
|
6
6
|
},
|
|
7
7
|
"events": [
|
|
8
|
+
{
|
|
9
|
+
"date": "2026-05-30",
|
|
10
|
+
"type": "catalog_update",
|
|
11
|
+
"artifact": "data/cve-catalog.json",
|
|
12
|
+
"path": "data/cve-catalog.json",
|
|
13
|
+
"schema_version": "1.0.0",
|
|
14
|
+
"entry_count": 427
|
|
15
|
+
},
|
|
8
16
|
{
|
|
9
17
|
"date": "2026-05-27",
|
|
10
18
|
"type": "catalog_update",
|
|
@@ -143,14 +151,6 @@
|
|
|
143
151
|
"path": "skills/email-security-anti-phishing/skill.md",
|
|
144
152
|
"note": "Email security + anti-phishing for mid-2026 — SPF/DKIM/DMARC/BIMI/ARC/MTA-STS/TLSRPT, AI-augmented phishing (vishing, deepfake video, hyperpersonalized email), Business Email Compromise, secure email gateways"
|
|
145
153
|
},
|
|
146
|
-
{
|
|
147
|
-
"date": "2026-05-18",
|
|
148
|
-
"type": "catalog_update",
|
|
149
|
-
"artifact": "data/cve-catalog.json",
|
|
150
|
-
"path": "data/cve-catalog.json",
|
|
151
|
-
"schema_version": "1.0.0",
|
|
152
|
-
"entry_count": 427
|
|
153
|
-
},
|
|
154
154
|
{
|
|
155
155
|
"date": "2026-05-18",
|
|
156
156
|
"type": "catalog_update",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"path": "data/cve-catalog.json",
|
|
54
54
|
"purpose": "Per-CVE record (CVSS, EPSS, CISA KEV, RWEP, AI-discovery, vendor advisories, framework gaps, ATLAS/ATT&CK mappings). Cross-validated against NVD + CISA KEV + FIRST EPSS via validate-cves.",
|
|
55
55
|
"schema_version": "1.0.0",
|
|
56
|
-
"last_updated": "2026-05-
|
|
56
|
+
"last_updated": "2026-05-30",
|
|
57
57
|
"tlp": "CLEAR",
|
|
58
58
|
"source_confidence_default": "A1",
|
|
59
59
|
"freshness_policy": {
|