@blamejs/exceptd-skills 0.12.7 → 0.12.9
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 +15 -1
- package/ARCHITECTURE.md +21 -5
- package/CHANGELOG.md +150 -0
- package/README.md +1 -1
- package/bin/exceptd.js +416 -69
- package/data/_indexes/_meta.json +44 -44
- package/data/_indexes/activity-feed.json +34 -34
- package/data/_indexes/catalog-summaries.json +9 -9
- package/data/_indexes/chains.json +249 -11
- package/data/_indexes/frequency.json +63 -5
- package/data/_indexes/jurisdiction-map.json +13 -3
- package/data/_indexes/section-offsets.json +1171 -1027
- package/data/_indexes/summary-cards.json +2 -2
- package/data/_indexes/token-budget.json +232 -152
- package/data/atlas-ttps.json +189 -1
- package/data/cve-catalog.json +34 -22
- package/data/cwe-catalog.json +290 -1
- package/data/d3fend-catalog.json +163 -1
- package/data/framework-control-gaps.json +243 -0
- package/data/playbooks/containers.json +23 -5
- package/data/playbooks/cred-stores.json +9 -9
- package/data/playbooks/crypto.json +8 -8
- package/data/playbooks/hardening.json +46 -10
- package/data/playbooks/library-author.json +16 -20
- package/data/playbooks/mcp.json +1 -0
- package/data/playbooks/runtime.json +7 -7
- package/data/playbooks/sbom.json +11 -11
- package/data/playbooks/secrets.json +4 -4
- package/data/rfc-references.json +144 -0
- package/lib/playbook-runner.js +119 -35
- package/lib/prefetch.js +27 -6
- package/lib/refresh-external.js +32 -9
- package/lib/schemas/skill-frontmatter.schema.json +2 -2
- package/manifest-snapshot.json +1 -1
- package/manifest.json +73 -73
- package/orchestrator/index.js +1 -1
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
- package/scripts/check-sbom-currency.js +87 -0
- package/scripts/check-test-coverage.README.md +148 -0
- package/scripts/check-test-coverage.js +476 -0
- package/scripts/hooks/pre-commit.sh +19 -0
- package/scripts/predeploy.js +14 -30
- package/skills/age-gates-child-safety/skill.md +3 -0
- package/skills/ai-attack-surface/skill.md +29 -1
- package/skills/ai-c2-detection/skill.md +30 -1
- package/skills/ai-risk-management/skill.md +3 -0
- package/skills/api-security/skill.md +3 -0
- package/skills/attack-surface-pentest/skill.md +3 -0
- package/skills/cloud-security/skill.md +3 -0
- package/skills/compliance-theater/skill.md +6 -0
- package/skills/container-runtime-security/skill.md +3 -0
- package/skills/coordinated-vuln-disclosure/skill.md +8 -1
- package/skills/defensive-countermeasure-mapping/skill.md +1 -1
- package/skills/dlp-gap-analysis/skill.md +3 -0
- package/skills/email-security-anti-phishing/skill.md +9 -1
- package/skills/exploit-scoring/skill.md +6 -0
- package/skills/identity-assurance/skill.md +6 -1
- package/skills/incident-response-playbook/skill.md +8 -2
- package/skills/kernel-lpe-triage/skill.md +24 -4
- package/skills/mcp-agent-trust/skill.md +28 -1
- package/skills/mlops-security/skill.md +3 -0
- package/skills/ot-ics-security/skill.md +3 -0
- package/skills/policy-exception-gen/skill.md +6 -0
- package/skills/rag-pipeline-security/skill.md +30 -1
- package/skills/researcher/skill.md +6 -0
- package/skills/sector-energy/skill.md +3 -0
- package/skills/sector-federal-government/skill.md +3 -0
- package/skills/sector-financial/skill.md +3 -0
- package/skills/sector-healthcare/skill.md +3 -0
- package/skills/security-maturity-tiers/skill.md +25 -1
- package/skills/skill-update-loop/skill.md +38 -0
- package/skills/supply-chain-integrity/skill.md +3 -0
- package/skills/threat-model-currency/skill.md +4 -0
- package/skills/threat-modeling-methodology/skill.md +3 -0
- package/skills/webapp-security/skill.md +3 -0
- package/skills/zeroday-gap-learn/skill.md +6 -0
package/AGENTS.md
CHANGED
|
@@ -34,6 +34,20 @@ Also read [CONTEXT.md](CONTEXT.md) for a complete orientation to the skill syste
|
|
|
34
34
|
|
|
35
35
|
14. **Primary-source IoC review** — Any CVE entry in `data/cve-catalog.json` whose `poc_available: true` AND whose exploit code is publicly available (published PoC repo, vendor advisory with attached payload, researcher blog with reproducer) must include `iocs` populated from a line-level cross-reference of the published source — not from secondary-source paraphrase. The `iocs` block records which IoC categories were extracted (`payload_artifacts`, `persistence_artifacts`, `credential_paths_scanned`, `c2_indicators`, `host_recon`, `behavioral`, `runtime_syscall`, `kernel_trace`, `livepatch_gap`, `destructive`, `payload_content_patterns`, `supply_chain_entry_vectors`), and each IoC must be traceable to a specific source URL or commit hash. v0.12.6 audit reviewed CVE-2026-45321 (Mini Shai-Hulud), CVE-2026-31431 (Copy Fail / Dirty Pipe / Dirty COW family), CVE-2026-43284 + CVE-2026-43500 (Dirty Frag pair), CVE-2025-53773 (Copilot YOLO mode), and CVE-2026-30615 (Windsurf MCP) against primary sources from Aikido, StepSecurity, Socket, Wiz, Datadog, Sysdig, Trail of Bits, Invariant Labs, Embrace the Red, NVD, MSRC. Catalog updates landed in v0.12.6 changelog. Skipping this audit is equivalent to shipping "untested security advice" — the IoC list IS the operator-facing detection contract.
|
|
36
36
|
|
|
37
|
+
15. **Test coverage on every diff** — Every feature change (added, removed, or modified) must land with a covering test reference in the same PR. The shapes the gate enforces:
|
|
38
|
+
|
|
39
|
+
| Change | Required test reference |
|
|
40
|
+
| ----------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
41
|
+
| New / removed CLI verb in `bin/exceptd.js` | Quoted verb literal in a `tests/*.test.js` file |
|
|
42
|
+
| New / removed CLI flag | Flag literal (e.g. `--my-flag`) somewhere under `tests/` |
|
|
43
|
+
| New / removed / renamed `module.exports` identifier | `require('…/<lib>')` plus a reference to the identifier in `tests/` |
|
|
44
|
+
| New `phases.detect.indicators[].id` in a playbook | Quoted indicator id literal in `tests/e2e-scenarios/*/expect.json` or `tests/*.test.js` |
|
|
45
|
+
| New / changed `iocs` field on a CVE entry | CVE id and the word `iocs` in the same test file |
|
|
46
|
+
|
|
47
|
+
Mechanical enforcement lives in `scripts/check-test-coverage.js` and runs as the 15th gate of `npm run predeploy` (also the `Diff coverage` job in `ci.yml`). Docs (`*.md`), workflow YAML, and skill body changes are allowlisted — skill bodies are covered by the Ed25519 signature gate (Hard Rule #13), workflows surface a manual-review flag rather than a hard finding. Whitespace-only diffs are ignored.
|
|
48
|
+
|
|
49
|
+
The gate is blocking: a covered surface change without a covering test reference fails the predeploy run and the `Diff coverage` CI job. Never bypass with `--no-verify` or `--warn-only` — add the covering test first. This rule is additive to Hard Rule #11 (no-MVP ban): a new playbook indicator or CLI surface that ships without a regression test is the same shape of incomplete-feature ship that #11 forbids, applied to the test layer.
|
|
50
|
+
|
|
37
51
|
---
|
|
38
52
|
|
|
39
53
|
## Seven-phase playbook contract
|
|
@@ -83,7 +97,7 @@ Schema reference: `lib/schemas/playbook.schema.json`. Reference playbook (read t
|
|
|
83
97
|
|
|
84
98
|
Each playbook's `_meta.feeds_into[]` declares downstream playbooks the host AI should consider chaining into after this run, and the condition that fires the chain. The condition expressions evaluate at `close()` against `analyze` + `validate` + `agentSignals` context. AI assistants surface the suggested next playbook to the operator but never auto-execute; the operator decides.
|
|
85
99
|
|
|
86
|
-
The current
|
|
100
|
+
The current matrix:
|
|
87
101
|
|
|
88
102
|
| From | Triggers | To | Why |
|
|
89
103
|
|---|---|---|---|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -161,7 +161,7 @@ Schema per entry:
|
|
|
161
161
|
|
|
162
162
|
### `data/global-frameworks.json`
|
|
163
163
|
|
|
164
|
-
Maps jurisdiction to framework to current coverage and lag assessment. Currently covers
|
|
164
|
+
Maps jurisdiction to framework to current coverage and lag assessment. Currently covers 35 jurisdictions including EU member states, UK, AU, SG, IN, JP, CA, and major sectoral regulators (DORA, NIS2, EU AI Act, EU CRA at the EU layer; APRA CPS 234, MAS TRM, CERT-In, SEBI, OSFI B-10 at the national layer). See schema in file.
|
|
165
165
|
|
|
166
166
|
### `data/zeroday-lessons.json`
|
|
167
167
|
|
|
@@ -173,23 +173,23 @@ Tracks PoC status, weaponization stage, and AI-assist factor per CVE. Updated wh
|
|
|
173
173
|
|
|
174
174
|
### `data/cwe-catalog.json`
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
51 CWE entries pinned to **CWE v4.17**. Covers the Top 25 Most Dangerous Software Weaknesses (2024 release) plus AI- and supply-chain-relevant weakness classes (prompt-injection-as-trust-boundary failure, training data integrity, dependency confusion, untrusted artifact ingestion). Each entry records root-cause description, common consequences, mitigation patterns, and the CVEs in `cve-catalog.json` that instantiate the weakness. Skills cite CWE IDs in `cwe_refs` to anchor a finding to a stable weakness taxonomy rather than to a single CVE; the CWE provides the durable root-cause lens that survives across exploit generations.
|
|
177
177
|
|
|
178
178
|
`_meta.cwe_version` pins the version; on a CWE release, audit IDs for renames or deprecations, bump `last_threat_review` on affected skills, and update `_meta`.
|
|
179
179
|
|
|
180
180
|
### `data/d3fend-catalog.json`
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
28 MITRE D3FEND defensive technique entries pinned to **D3FEND v1.0.0**. Each entry records the defensive technique ID (e.g., `D3-EAL` Executable Allowlisting), the tactic / artifact it defends, the offensive ATLAS / ATT&CK TTPs it counters, defense-in-depth layer position, least-privilege scope assumptions, zero-trust posture compatibility, and AI-pipeline applicability per Hard Rule #9. Skills cite D3FEND IDs in `d3fend_refs` to map offensive findings to a defensive countermeasure rather than to abstract control language. The `defensive-countermeasure-mapping` skill is the canonical consumer; any skill shipped on or after 2026-05-11 includes a Defensive Countermeasure Mapping section referencing this catalog.
|
|
183
183
|
|
|
184
184
|
`_meta.d3fend_version` pins the version; D3FEND ontology additions are tracked in skill `forward_watch` fields.
|
|
185
185
|
|
|
186
186
|
### `data/rfc-references.json`
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
31 IETF RFC / Internet-Draft references covering authentication and authorization (OAuth 2.0 Security BCP RFC 9700, JWT BCP, FIDO/WebAuthn-related drafts), cryptography (TLS 1.3 RFC 8446, hybrid PQC drafts), disclosure (security.txt RFC 9116), and adjacent IETF standards skills depend on. Each entry tracks: title, status (Proposed Standard / Best Current Practice / Internet-Draft / Historic), errata count, replaces / replaced-by chains, IESG / IRTF stream, and a `last_verified` date. Skills cite RFC IDs in `rfc_refs`. Per Hard Rule #12, RFC references are version-pinned: when an RFC is obsoleted or a draft is published as an RFC, the catalog entry's `replaced_by` field is updated, `last_verified` is refreshed, and affected skills bump `last_threat_review`. Frameworks lag RFCs; RFCs lag attacker innovation — this catalog makes that middle layer auditable.
|
|
189
189
|
|
|
190
190
|
### `data/dlp-controls.json`
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
22 DLP control entries indexed along five axes: **channel** (where data flows — LLM prompt, RAG retrieval, MCP tool response, email, SaaS API, endpoint), **classifier** (how sensitive data is identified — regex, ML, embedding similarity, watermark), **surface** (where enforcement runs — endpoint, network proxy, API gateway, model gateway), **enforcement** mode (block, redact, warn, log-only), and **evidence** type (the audit artifact each control produces). The `dlp-gap-analysis` skill is the canonical consumer; other DLP-relevant skills cite control IDs in `dlp_refs`. Entries explicitly flag classical DLP controls that are architecturally inadequate for LLM/RAG channels (DR-1 framework-as-truth drift applied to DLP).
|
|
193
193
|
|
|
194
194
|
---
|
|
195
195
|
|
|
@@ -232,6 +232,22 @@ Framework lag scoring and gap report generation.
|
|
|
232
232
|
- `gapReport(frameworkId, scope)` — Generate gap report for a framework within a scope (e.g., "kernel LPE", "AI attack surface")
|
|
233
233
|
- `theaterCheck(controlId, orgControls)` — Run compliance theater detection for a specific control
|
|
234
234
|
|
|
235
|
+
### `scripts/check-test-coverage.js`
|
|
236
|
+
|
|
237
|
+
Diff-coverage analyzer. Walks the staged/working-tree diff for the changed-surface shapes Hard Rule #15 enforces (CLI verbs, CLI flags, `module.exports` identifiers, new playbook indicator IDs, CVE `iocs` fields) and asserts that each change has a covering test reference somewhere under `tests/`. Skill bodies, docs, and workflow YAML are allowlisted. Runs as the 15th gate of `npm run predeploy` (and the `Diff coverage` job in `ci.yml`). Direct invocation: `npm run diff-coverage`.
|
|
238
|
+
|
|
239
|
+
### `scripts/check-sbom-currency.js`
|
|
240
|
+
|
|
241
|
+
Compares `sbom.cdx.json` against the live `manifest.json` skill count and `data/*.json` catalog counts. Fails the predeploy gate when the SBOM drifts from the shipped surface. Refresh with `npm run refresh-sbom`.
|
|
242
|
+
|
|
243
|
+
### `scripts/verify-shipped-tarball.js`
|
|
244
|
+
|
|
245
|
+
Packs the project with `npm pack`, extracts the tarball, and runs Ed25519 signature verification against the extracted bytes — the same path a downstream `npm install` exercises. Predeploy gate guaranteeing the shipped tarball verifies, independent of source-tree verification.
|
|
246
|
+
|
|
247
|
+
### `tests/_helpers/cli.js`
|
|
248
|
+
|
|
249
|
+
Shared test harness for spawning the CLI under tempdir-isolated state. Tests that exercise verb dispatch should consume this helper rather than spawning subprocesses ad-hoc — the helper enforces the "no mutation outside the tempdir" contract that prevents CI-vs-local state divergence.
|
|
250
|
+
|
|
235
251
|
---
|
|
236
252
|
|
|
237
253
|
## manifest.json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,155 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.9 — 2026-05-13
|
|
4
|
+
|
|
5
|
+
**Patch: post-v0.12.8 audit pass — Hard Rule #15 gate flips blocking, sbom evidence-correlation fix, CVE catalog freshness corrections, and recovery of two v0.12.8 stash-restore casualties.**
|
|
6
|
+
|
|
7
|
+
### Hard Rule #15 — diff-coverage gate is now blocking
|
|
8
|
+
|
|
9
|
+
`scripts/check-test-coverage.js` flips from `--warn-only` to a blocking gate. The 15th `npm run predeploy` gate and the `Diff coverage` CI job now fail a run if any change to a CLI verb, CLI flag, `module.exports` identifier, playbook indicator, or CVE `iocs` field lands without a covering test reference. Two analyzer bugs that would have made the gate unreliable under blocking are fixed in the same release:
|
|
10
|
+
|
|
11
|
+
- `coversLibExport` now recognises subprocess-based test invocations (e.g. `spawnSync(... "scripts/check-sbom-currency.js" ...)`) alongside `require(...)`-form coverage.
|
|
12
|
+
- `extractLibExports` strips block and line comments before matching `module.exports = {...}`, eliminating the doc-comment shadow bug where the analyzer's regex captured a JSDoc banner and returned an empty export set.
|
|
13
|
+
|
|
14
|
+
`tests/playbook-indicators.test.js` lands as a table-driven test referencing all 12 indicator ids added in v0.12.7 (`mcp.json` × 6) and v0.12.8 (`containers.json` × 2, `hardening.json` × 4). The new tests cover the Hard Rule #15 surface the analyzer flagged.
|
|
15
|
+
|
|
16
|
+
### sbom `matched_cves` now evidence-correlated
|
|
17
|
+
|
|
18
|
+
`exceptd run sbom` previously surfaced every CVE in the playbook's `domain.cve_refs` under `analyze.matched_cves`, regardless of whether the operator's submitted evidence correlated to any of them. Operators reading the output assumed they were affected by the listed CVEs. The analyze phase now splits into two fields:
|
|
19
|
+
|
|
20
|
+
- `analyze.matched_cves` — only CVEs correlated to operator evidence (indicator hit whose `attack_ref`/`atlas_ref` intersects the CVE's refs, or an explicit `signals[cveId]` set to `true`/`hit`/`detected`/`affected`). Each entry carries a `correlated_via` reason.
|
|
21
|
+
- `analyze.catalog_baseline_cves` — the playbook's CVE catalog (informational; not an affected-status list). Each entry carries `correlated_via: null` and a note documenting the distinction.
|
|
22
|
+
|
|
23
|
+
CSAF / SARIF / OpenVEX bundles consume `matched_cves` only — they correctly omit catalog-only CVEs as vulnerabilities. RWEP base now derives from evidence-correlated CVEs rather than the catalog ceiling, so inconclusive runs no longer inherit a misleading high score.
|
|
24
|
+
|
|
25
|
+
The `run` human renderer shows "No CVEs correlated to your evidence. Playbook catalog (informational): N CVE(s) this playbook scans for." when no evidence correlated.
|
|
26
|
+
|
|
27
|
+
### CLI surface — ci verdict / exit reconcile, signing-key resolution, fuzzy matches
|
|
28
|
+
|
|
29
|
+
`ci --scope <type>` with no evidence and all-inconclusive results now emits `verdict: "NO_EVIDENCE"` (was `"PASS"`) so the body and exit code 3 agree. Operators reading either field alone now see the same answer. The verdict computation is hoisted before the result emit so BLOCKED / FAIL / NO_EVIDENCE / PASS are all consistent end-to-end.
|
|
30
|
+
|
|
31
|
+
`ci` result top-level gains `framework_gap_rollup` aggregating per-playbook `framework_gap_mapping` entries across all scoped playbooks. Each rollup entry lists `{framework, claimed_control, why_insufficient, playbooks[]}` so a CI gate surfaces "what gaps did this run uncover" without the operator having to walk every per-playbook result.
|
|
32
|
+
|
|
33
|
+
`maybeSignAttestation()` now resolves `.keys/private.pem` cwd-first, package-root fallback — matching how `doctor --signatures` resolves the same key. Pre-v0.12.9, operators running `exceptd run` from a repo with their private key at the cwd-relative `.keys/private.pem` would see `doctor` report the key as present while attestations from the same directory were silently written UNSIGNED. The two surfaces now agree.
|
|
34
|
+
|
|
35
|
+
`run <typo>` error path adds Levenshtein-distance suggestions for misspelled playbook ids when no substring match fits. `run secrt` now suggests `secrets`; `run cret-stores` suggests `cred-stores`.
|
|
36
|
+
|
|
37
|
+
`brief --phase <value>` rejects unknown phases with a structured JSON error (accepted set: `govern | direct | look`). Pre-v0.12.9 any string was accepted silently and the full brief was emitted.
|
|
38
|
+
|
|
39
|
+
`doctor --signatures --shipped-tarball` runs the `verify-shipped-tarball` round-trip alongside the source-tree signature check, surfacing the integrity layer that closed the v0.11.x → v0.12.4 signature regression class. Opt-in; routine `doctor --signatures` stays fast.
|
|
40
|
+
|
|
41
|
+
`doctor --registry-check` text-mode output now surfaces the registry comparison alongside the other check lines. Pre-v0.12.9 the flag only populated `checks.registry.*` in the JSON output, leaving the text-mode operator with no signal the flag did anything.
|
|
42
|
+
|
|
43
|
+
`run` precondition renderer no longer prints `[undefined]` for preconditions without an `on_fail` field — the bracket is omitted and the description falls back to `check | description | reason` in order.
|
|
44
|
+
|
|
45
|
+
### CVE catalog freshness corrections
|
|
46
|
+
|
|
47
|
+
Five entries reconciled against authoritative public sources as of 2026-05-13:
|
|
48
|
+
|
|
49
|
+
- **CVE-2026-30615** (Windsurf MCP): CVSS corrected 9.8 → 8.0; vector AV:N → AV:L (the attack is local-vector via adversarial HTML content the Windsurf MCP client processes, not a network-vector zero-interaction RCE). Source: NVD authoritative metric block (`vulnStatus: Deferred`, last_modified 2026-04-27).
|
|
50
|
+
- **CVE-2026-31431** (Copy Fail): KEV `dateAdded` corrected 2026-03-15 → 2026-05-01, `dueDate` 2026-04-05 → 2026-05-15. The catalog was running six weeks ahead of the real KEV listing; downstream framework-SLA computations were anchored on a date that hadn't yet been authoritative. CWE-669 added. Source: CISA KEV JSON feed.
|
|
51
|
+
- **CVE-2026-43284** (Dirty Frag ESP): CVSS authoritative is 8.8 / `Scope:C` (kernel→user-namespace breakout — supports container-escape framing); 7.8 / `Scope:U` preserved as `cvss_score_alternate` for compatibility readers. CWE-123 added.
|
|
52
|
+
- **CVE-2026-43500** (Dirty Frag RxRPC): CWE-787 added.
|
|
53
|
+
- **EPSS values refreshed** for four CVEs (CVE-2026-31431, -43284, -43500, -45321) from live FIRST API values. Catalog previously stored cold-start estimates that overstated newly-published-CVE exposure.
|
|
54
|
+
|
|
55
|
+
Each correction carries an inline `*_correction_note` field with the source URL and the rationale for downstream auditors. Two new CVEs surfaced by the freshness sweep (CVE-2026-42208 LiteLLM SQLi on KEV; CVE-2026-39884 mcp-server-kubernetes argument injection) are deferred to a follow-up patch — each warrants its own Hard Rule #14 primary-source IoC review.
|
|
56
|
+
|
|
57
|
+
### v0.12.8 stash-restore casualties recovered
|
|
58
|
+
|
|
59
|
+
Two claims in the v0.12.8 CHANGELOG were not actually on disk in the squash commit, lost during the v0.12.8 recovery flow:
|
|
60
|
+
|
|
61
|
+
- `data/playbooks/mcp.json` `domain.cve_refs` now includes CVE-2025-53773 alongside CVE-2026-30615 and CVE-2026-45321. The Hard Rule #4 mismatch (the `copilot-yolo-mode-flag` / `copilot-chat-experimental-flags` indicators detected this CVE without the playbook claiming it) is now genuinely closed.
|
|
62
|
+
- `tests/operator-bugs.test.js` is now refactored to use `tests/_helpers/cli.js` for `makeCli` / `makeSuiteHome` / `tryJson`. The per-suite `EXCEPTD_HOME` tempdir routing applies to all 80+ tests in the file. Pre-v0.12.9 the inline helper continued writing attestations to the maintainer's real `~/.exceptd/attestations/` — 2,819 leaked attestations cleaned up alongside the refactor.
|
|
63
|
+
|
|
64
|
+
### Two real defects deferred from v0.12.8 fixed
|
|
65
|
+
|
|
66
|
+
- **Libuv `UV_HANDLE_CLOSING` crash on Windows + Node 25.** `lib/prefetch.js` `main()` called `process.exit(N)` after the summary `console.log` — same v0.11.10 #100 class as the run/ci sites already fixed. Replaced with `process.exitCode = N; return;` so undici / AbortController teardown completes before the event loop ends. Strengthened `#65 refresh --no-network` test asserts exit 0 AND no `Assertion failed` / `UV_HANDLE_CLOSING` lines on stderr.
|
|
67
|
+
- **Two 404'd pin sources.** `d3fend/d3fend-data` and `mitre/cwe` were registered as `SOURCES.pins` GitHub-Releases sources, but neither repository publishes Releases via that path (D3FEND distributes from `d3fend.mitre.org`; CWE from `cwe.mitre.org`). Both sources removed from `lib/prefetch.js` and `lib/refresh-external.js` `pinsDiffFromCache()` `PIN_REPOS`. `prefetch summary` now reports `0 error(s)` on a clean cache. A new regression test asserts every pins source URL matches `^https://api.github.com/repos/<org>/<repo>/releases\?`.
|
|
68
|
+
|
|
69
|
+
### Skill body second pass
|
|
70
|
+
|
|
71
|
+
Four priority skills gain a `## Defensive Countermeasure Mapping` body section per Hard Rule #11's post-2026-05-11 grandfathered-skill closeout: `ai-c2-detection`, `ai-attack-surface`, `mcp-agent-trust`, `rag-pipeline-security`. Each maps the skill's offensive findings to 3-7 D3FEND IDs from `data/d3fend-catalog.json` with rationale + ephemeral/serverless-workload alternatives per Hard Rule #9.
|
|
72
|
+
|
|
73
|
+
Eight meta skills (`researcher`, `threat-model-currency`, `skill-update-loop`, `zeroday-gap-learn`, `policy-exception-gen`, `security-maturity-tiers`, `exploit-scoring`, `compliance-theater`) gain a `## Frontmatter Scope` section documenting why their `atlas_refs` / `attack_refs` / `framework_gaps` lists are intentionally empty.
|
|
74
|
+
|
|
75
|
+
`rag-pipeline-security` `framework_gaps` token refined `UK-CAF-A1` → `UK-CAF-B2` — the RAG attack class resolves to retrieval-time access-control failure, which is the B2 (Identity and Access Control) surface, not the A1 (Governance) parent concern.
|
|
76
|
+
|
|
77
|
+
### Repository
|
|
78
|
+
|
|
79
|
+
- README "13 gates" → "15 gates"; ARCHITECTURE catalog counts refreshed (CWE 30→51, D3FEND 21→28, RFC 19→31, jurisdictions "22+" → "35"); ARCHITECTURE Logic Layer gains entries for `scripts/check-test-coverage.js`, `scripts/check-sbom-currency.js`, `scripts/verify-shipped-tarball.js`, `tests/_helpers/cli.js`.
|
|
80
|
+
- AGENTS.md feeds_into matrix heading drops the residual `(v0.10.x)` tag; Hard Rule #15 wording flips from `--warn-only` rollout language to present-tense blocking.
|
|
81
|
+
- CONTRIBUTING.md adds `npm run diff-coverage` to the pre-push gate list so contributors run the same Hard Rule #15 check CI does.
|
|
82
|
+
- Dependabot grouping for github-actions (already landed in v0.12.8) confirmed intact.
|
|
83
|
+
|
|
84
|
+
Test count: 418 → 439. Predeploy gates: 15/15 (gate 15 now blocking). Skills: 38/38 signed and verified.
|
|
85
|
+
|
|
86
|
+
## 0.12.8 — 2026-05-13
|
|
87
|
+
|
|
88
|
+
**Patch: comprehensive audit pass — CLI surface fixes, catalog completeness, test infrastructure hardening, AGENTS.md Hard Rule #15.**
|
|
89
|
+
|
|
90
|
+
### Hard Rule #15 — Test coverage on every diff
|
|
91
|
+
|
|
92
|
+
`AGENTS.md` adds a fifteenth hard rule: every CLI verb, CLI flag, `module.exports` identifier, playbook `phases.detect.indicators[].id`, or CVE `iocs` field change must land with a covering test reference in the same PR. Enforcement lives in `scripts/check-test-coverage.js`, wired as the 15th `npm run predeploy` gate and the `Diff coverage` job in `ci.yml`. Ships `--warn-only` for one release cycle then flips blocking in v0.12.9. Docs, workflow YAML, and skill body changes are allowlisted; whitespace-only diffs are ignored.
|
|
93
|
+
|
|
94
|
+
### CLI surface — exit-code, dispatcher, and ingest
|
|
95
|
+
|
|
96
|
+
`run --ci`, `run --all`, and `ai-run --stream` previously called `process.exit(N)` immediately after `emit()` writes to stdout — the v0.11.10 #100 truncation class. All three sites now use `process.exitCode = N; return;` so buffered async stdout fully drains before the event loop ends. The `ai-run` streaming handler additionally pauses stdin on completion so further callbacks cannot re-enter after the final frame.
|
|
97
|
+
|
|
98
|
+
The deprecation banner for legacy verbs now fires for every alias in `LEGACY_VERB_REPLACEMENTS`, not just the subset routed through `PLAYBOOK_VERBS`. Operators running `scan`, `dispatch`, `currency`, `verify`, `validate-cves`, `validate-rfcs`, `watchlist`, `prefetch`, or `build-indexes` now see the same one-time banner pointing at the v0.11.0 replacement that `plan`, `govern`, `direct`, `look`, `ingest`, `reattest`, and `list-attestations` already surfaced.
|
|
99
|
+
|
|
100
|
+
`ingest` previously wrote its attestation via an inline `writeFileSync` that bypassed both the session-id collision refusal and the Ed25519 sidecar signing layer that `run` and `run --all` go through. Two `ingest` invocations with the same `--session-id` would silently clobber the audit trail and no `.sig` ever landed. Routed through `persistAttestation()` now — collision refusal and `maybeSignAttestation()` both apply.
|
|
101
|
+
|
|
102
|
+
Per-verb `--help` text expanded to cover surface that shipped undocumented: `ci --required <ids>`, `ci --max-rwep`, `ci --block-on-jurisdiction-clock`, `ci --evidence-dir`, `ci --format`, plus the full four-line exit-code matrix (0 PASS / 1 framework error / 2 detected / 3 ran-but-no-evidence / 4 blocked). `attest list` and `attest diff` subverbs added to the `attest --help` enumeration. `run --upstream-check`, `--strict-preconditions`, `--session-key`, `--air-gap`, `--force-overwrite` documented in the `run` block. `doctor --registry-check` and `doctor --fix` documented in the `doctor` block. `brief`, `lint`, `run-all`, `verify-attestation` gain per-verb help entries.
|
|
103
|
+
|
|
104
|
+
### Catalog completeness — 47 new entries close cross-catalog dangling refs
|
|
105
|
+
|
|
106
|
+
Six ATLAS TTPs added to `data/atlas-ttps.json`: T0024 (Exfiltration via ML Inference API), T0044 (Full ML Model Access), T0048 (Erode ML Model Integrity), T0053 (LLM Plugin Compromise), T0055 (Unsecured Credentials), T0057 (LLM Data Leakage). All previously referenced by `data/cve-catalog.json` (CVE-2026-45321) and `data/dlp-controls.json` without a catalog entry.
|
|
107
|
+
|
|
108
|
+
Seventeen CWE entries added to `data/cwe-catalog.json`: CWE-250, 256, 284, 310, 312, 326, 328, 329, 330, 331, 338, 353, 426, 522, 759, 760, 916. All previously referenced by playbook `domain.cwe_refs` across `containers`, `cred-stores`, `crypto`, `crypto-codebase`, `ai-api`, `secrets`, `hardening`, `runtime`, and `library-author` without a catalog entry.
|
|
109
|
+
|
|
110
|
+
Eight D3FEND entries added to `data/d3fend-catalog.json`: D3-ANCI, D3-CAA, D3-CH, D3-EI, D3-FCR, D3-KBPI, D3-SCA, D3-SFA. All previously referenced by playbook `domain.d3fend_refs` without a catalog entry.
|
|
111
|
+
|
|
112
|
+
Ten framework-control-gap entries added to `data/framework-control-gaps.json`: NIS2-Art21-incident-handling, EU-AI-Act-Art-15, UK-CAF-A1/B2/C1/D1, AU-Essential-8-MFA/App-Hardening/Patch/Backup. Closes the Hard Rule #5 (global-first) gap for 23 skills that previously declared US-anchored `framework_gaps` only.
|
|
113
|
+
|
|
114
|
+
Twelve standards entries added to `data/rfc-references.json`: RFC-7489 (DMARC), RFC-6376 (DKIM), RFC-7208 (SPF), RFC-8616 (IDN email auth), RFC-8461 (MTA-STS), ISO-29147 + ISO-30111 (vulnerability disclosure + handling), RFC-9116 (security.txt), CSAF-2.0, RFC-6545 (RID), RFC-6546 (RID transport), RFC-7970 (IODEF v2). Schema (`lib/schemas/skill-frontmatter.schema.json`) + validator (`tests/rfc-refs.test.js`) extended to accept the broader standards-key shape (`RFC-`, `DRAFT-`, `ISO-`, `CSAF-`) alongside RFC numbers.
|
|
115
|
+
|
|
116
|
+
### Playbook integrity — orphan close + indicator wiring
|
|
117
|
+
|
|
118
|
+
`library-author.json` `_meta.feeds_into` removed a dangling `compliance-theater` entry (no such playbook file exists); the remaining `framework` entry handles the same condition. `mcp.json` `domain.cve_refs` now lists CVE-2025-53773 alongside CVE-2026-30615 and CVE-2026-45321 — closes the Hard Rule #4 gap where the existing `copilot-yolo-mode-flag` and `copilot-chat-experimental-flags` indicators detected the CVE without the playbook claiming it.
|
|
119
|
+
|
|
120
|
+
Eight playbooks had artifacts collected in `phases.look.artifacts[]` that no indicator consumed — operator paid the collection cost, no detection ran. Containers (9 orphans), cred-stores (9), runtime (11), crypto (10), hardening (11), library-author (14), sbom (18), secrets (7) all now cite every collected artifact in at least one indicator. Six new indicators added (`psa-policy-permissive-or-absent` and `network-policies-absent-from-workload-namespace` in `containers`; `kernel-lockdown-none`, `sudoers-tty-pty-logging-absent`, `audit-rules-empty-or-skeletal`, `umask-permissive` in `hardening`) where existing detection logic conceptually consumed the artifact but no rule had been written.
|
|
121
|
+
|
|
122
|
+
### Skill files — required-section closures, Hard Rule #5 sweep
|
|
123
|
+
|
|
124
|
+
`kernel-lpe-triage`, `security-maturity-tiers`, and `skill-update-loop` previously failed the Hard Rule #11 required-section contract. `kernel-lpe-triage` had a Compliance Theater Check embedded inside Analysis Procedure Step 5 but no top-level section; `security-maturity-tiers` had no Compliance Theater section at all; `skill-update-loop` was missing Threat Context and TTP Mapping. All three sections promoted to top-level with substantive content.
|
|
125
|
+
|
|
126
|
+
Twenty-three skills had US-anchored `framework_gaps` only (NIST + ISO + SOC2). Each gains EU + UK + AU tokens (`NIS2-Art21-incident-handling` / `EU-AI-Act-Art-15`, `UK-CAF-A1/B2/C1/D1`, `AU-Essential-8-MFA/App-Hardening/Patch/Backup` as the per-skill match dictates). `ai-c2-detection` `cwe_refs` populated with CWE-918. `email-security-anti-phishing` `rfc_refs` populated with RFC-7489/6376/7208/8616/8461. `identity-assurance` `d3fend_refs` populated with D3-MFA + D3-CSPP. `coordinated-vuln-disclosure` `rfc_refs` populated with ISO-29147/30111, RFC-9116, CSAF-2.0. `incident-response-playbook` `rfc_refs` populated with RFC-6545/6546/7970.
|
|
127
|
+
|
|
128
|
+
Four skills bump `last_threat_review` to 2026-05-13 to reflect post-v0.12.6 catalog state: `kernel-lpe-triage`, `ai-attack-surface`, `mcp-agent-trust`, `ai-c2-detection`. Four skills replace literal `xxx` placeholders in body text with explicit angle-bracket placeholders (`<patch-revision>`, `<sub-technique-id>`, `<advisory-number>`) so future Rule #10 audits don't surface false positives.
|
|
129
|
+
|
|
130
|
+
### Test infrastructure
|
|
131
|
+
|
|
132
|
+
The `cli()` test helper now routes attestations to a per-suite tempdir via `EXCEPTD_HOME` instead of writing to `~/.exceptd/attestations/`. Every prior `npm test` run had been accumulating attestations in the maintainer's real home dir without cleanup; tempdir routing fixes the structural class behind the v0.11.x→v0.12.4 sign regression. Helper factored to `tests/_helpers/cli.js` so it can be required by both `operator-bugs.test.js` and the new `cli-coverage.test.js`.
|
|
133
|
+
|
|
134
|
+
Twenty-eight previously-coincidence-passing assertions in `operator-bugs.test.js` strengthened: silent fall-through `if (data?.ok === false)` branches replaced with hard parse + shape checks first; `assert.notEqual(r.status, 0)` replaced with explicit exit-code pins (2 for format-rejected, 4 for blocked, etc.); `assert.ok(data)` replaced with field-shape assertions. Two coincidence-passes that hid real defects became actual findings:
|
|
135
|
+
|
|
136
|
+
- `refresh --no-network` on Windows + Node 25 surfaces a libuv `UV_HANDLE_CLOSING` assertion at worker-pool teardown after the prefetch summary flushes cleanly (exit 3221226505 / 0xC0000409). The summary contract is honored; the teardown crash is a Windows-libuv quirk. Test accepts both 0 and the Windows exit code so long as the stdout summary matches the strict numeric-breakdown regex.
|
|
137
|
+
- `refresh` pin sources `d3fend__d3fend-data__releases` and `mitre__cwe__releases` return HTTP 404 — surfaces as `2 error(s)` in every prefetch summary. Flagged for upstream catalog-pin work; not a regression introduced here.
|
|
138
|
+
|
|
139
|
+
`lib/refresh-external.js` now accepts `--catalog <path>` and honors `EXCEPTD_CVE_CATALOG` so tests can redirect catalog writes to a tempdir instead of mutating the shipped `data/cve-catalog.json`. Eight catalog-mutating tests in `operator-bugs.test.js` can now route to tempdirs.
|
|
140
|
+
|
|
141
|
+
Thirty-one new CLI happy-path tests in `tests/cli-coverage.test.js` exercise `brief` (all/scope/directives/phase), `discover`, `doctor` (all subchecks), `attest show/list/export`, `verify-attestation` alias, `run-all` alias, `framework-gap`, `report executive`, `validate-rfcs`, `ai-run` streaming JSONL (strict in-order assertion across all nine frames), `ci --max-rwep`, `ci --block-on-jurisdiction-clock`, `ci --evidence-dir`, `run --vex`, `run --diff-from-latest`, `run --force-stale`, `run --air-gap`, `run --session-key` (HMAC), and `refresh --indexes-only`.
|
|
142
|
+
|
|
143
|
+
Eight predeploy-gate meta-tests in `tests/predeploy-gates.test.js` stage known-bad state in tempdirs and assert each gate fires: verify-signatures (byte-flipped signature), lint-skills (missing required section), validate-catalog-meta (malformed `tlp`), sbom-currency (drift), validate-indexes (out-of-date entry), validate-vendor (modified vendored file), validate-package (missing file-allowlist entry), verify-shipped-tarball (skill body tampered post-signing — the v0.11.x→v0.12.4 regression class). Gate 10's inline `node -e` checker extracted to `scripts/check-sbom-currency.js` for testability; no behavior change.
|
|
144
|
+
|
|
145
|
+
Twelve new e2e scenarios in `tests/e2e-scenarios/09-secrets-aws-key` through `20-ai-api-openai-dotfile` exercise the twelve playbooks previously without e2e coverage (`secrets`, `kernel`, `library-author`, `crypto-codebase`, `mcp`, `framework`, `cred-stores`, `containers`, `runtime`, `hardening`, `crypto`, `ai-api`). All twenty scenarios pass via `npm run test:e2e`.
|
|
146
|
+
|
|
147
|
+
### Repository
|
|
148
|
+
|
|
149
|
+
Dependabot grouping config added for the github-actions ecosystem: weekly version-update bumps now land as a single grouped PR instead of N parallel PRs against the same 14-gate CI matrix. Security-updates stay ungrouped so a single-action CVE surfaces as its own PR.
|
|
150
|
+
|
|
151
|
+
Test count: 386 → 418 (388 + 31 cli-coverage − accounting note: 8 predeploy-gates + 12 diff-coverage tests landed alongside the +31 CLI surface tests; some pre-existing tests resolved into fewer counted tests on suite reorganization). Predeploy gates: 14 → 15.
|
|
152
|
+
|
|
3
153
|
## 0.12.7 — 2026-05-13
|
|
4
154
|
|
|
5
155
|
**Patch: two follow-on fixes to v0.12.6.**
|
package/README.md
CHANGED
|
@@ -167,7 +167,7 @@ You're adding a skill, updating a catalog, or cutting a release. Clone + bootstr
|
|
|
167
167
|
git clone https://github.com/blamejs/exceptd-skills
|
|
168
168
|
cd exceptd-skills
|
|
169
169
|
npm run bootstrap # auto-detects: verify-only / re-sign / first-init
|
|
170
|
-
npm run predeploy # full
|
|
170
|
+
npm run predeploy # full 15-gate CI sequence locally
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
`bootstrap` auto-detects the right mode based on which keys exist on disk:
|