@blamejs/exceptd-skills 0.16.22 → 0.16.24
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/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +42 -0
- package/CONTEXT.md +9 -9
- package/README.md +3 -3
- package/agents/report-generator.md +2 -2
- package/agents/skill-updater.md +1 -1
- package/agents/source-validator.md +3 -4
- package/agents/threat-researcher.md +1 -1
- package/bin/exceptd.js +91 -32
- package/data/_indexes/_meta.json +10 -10
- package/data/_indexes/activity-feed.json +12 -12
- package/data/_indexes/chains.json +70435 -4026
- package/data/_indexes/frequency.json +492 -163
- package/data/_indexes/section-offsets.json +51 -51
- package/data/_indexes/summary-cards.json +272 -106
- package/data/_indexes/token-budget.json +10 -10
- package/data/_indexes/trigger-table.json +15 -6
- package/data/_indexes/xref.json +218 -26
- package/data/cve-catalog.json +10 -10
- package/data/cwe-catalog.json +1 -0
- package/lib/auto-discovery.js +39 -1
- package/lib/collectors/ai-api.js +112 -7
- package/lib/collectors/citation-hygiene.js +27 -0
- package/lib/collectors/crypto-codebase.js +25 -0
- package/lib/collectors/kernel.js +32 -2
- package/lib/collectors/library-author.js +30 -0
- package/lib/collectors/runtime.js +38 -3
- package/lib/collectors/sbom.js +21 -2
- package/lib/collectors/scan-excludes.js +4 -1
- package/lib/collectors/secrets.js +125 -0
- package/lib/cve-cli.js +9 -1
- package/lib/cve-curation.js +8 -1
- package/lib/cve-regression-watcher.js +5 -2
- package/lib/exit-codes.js +2 -0
- package/lib/flag-suggest.js +1 -1
- package/lib/lint-skills.js +70 -0
- package/lib/playbook-runner.js +75 -14
- package/lib/prefetch.js +24 -1
- package/lib/refresh-external.js +32 -3
- package/lib/rfc-cli.js +8 -1
- package/lib/scoring.js +36 -8
- package/lib/validate-cve-catalog.js +36 -14
- package/lib/validate-package.js +8 -0
- package/lib/validate-playbooks.js +42 -0
- package/lib/verify.js +4 -3
- package/manifest-snapshot.json +4 -2
- package/manifest-snapshot.sha256 +1 -1
- package/manifest.json +57 -54
- package/orchestrator/README.md +1 -1
- package/orchestrator/index.js +65 -7
- package/orchestrator/scanner.js +53 -5
- package/package.json +1 -1
- package/sbom.cdx.json +110 -110
- package/scripts/build-indexes.js +42 -8
- package/scripts/builders/cwe-chains.js +1 -0
- package/scripts/builders/section-offsets.js +10 -2
- package/scripts/builders/token-budget.js +3 -3
- package/scripts/check-changelog-extract.js +38 -1
- package/scripts/check-sbom-currency.js +72 -0
- package/scripts/check-version-tags.js +5 -0
- package/scripts/release.js +22 -15
- package/skills/exploit-scoring/skill.md +8 -8
package/ARCHITECTURE.md
CHANGED
|
@@ -176,7 +176,7 @@ Tracks PoC status, weaponization stage, and AI-assist factor per CVE. Updated wh
|
|
|
176
176
|
|
|
177
177
|
### `data/cwe-catalog.json`
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
177 CWE entries pinned to **CWE v4.20**. 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.
|
|
180
180
|
|
|
181
181
|
`_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`.
|
|
182
182
|
|
|
@@ -213,7 +213,7 @@ cisa_kev +25 (binary)
|
|
|
213
213
|
poc_available +20 (binary)
|
|
214
214
|
ai_assisted_weapon +15 (binary)
|
|
215
215
|
active_exploitation +20 (binary)
|
|
216
|
-
blast_radius +
|
|
216
|
+
blast_radius +30 (0–30 scaled)
|
|
217
217
|
patch_available -15 (binary)
|
|
218
218
|
live_patch_available -10 (binary: additional reduction if no reboot required)
|
|
219
219
|
reboot_required +5 (binary penalty: patch exists but requires reboot)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.24 — 2026-06-05
|
|
4
|
+
|
|
5
|
+
Attestation replay tamper-gating, collector detection integrity, and lock-lifecycle fixes.
|
|
6
|
+
|
|
7
|
+
### Bugs
|
|
8
|
+
|
|
9
|
+
A host whose `keys/public.pem` fails the `EXPECTED_FINGERPRINT` pin is now a first-class tamper state on the replay path: `reattest` refuses with exit 6 (TAMPERED) unless `--force-replay`, the same way `attest verify` already refused. Previously the pin mismatch — the exact key-swap attack the pin exists to surface — fell through to a benign "unsigned attestations are an operator config issue" note and the replay proceeded. The sidecar audit classification gains a matching `fingerprint-mismatch` label, and the replay-refusal predicate now lives in one shared helper so a future tamper class has exactly one place to extend.
|
|
10
|
+
|
|
11
|
+
Piping a collector into a run — `exceptd collect secrets | exceptd run secrets --evidence -` — now classifies real findings as detected instead of silently inconclusive. Collectors flipped false-positive-gated indicators to "hit" without attesting the FP checks they had actually performed, so the engine's honest-downgrade gate demoted every such hit. Eight collectors now perform and attest their deterministic FP checks (example-key demotion, placeholder and entropy floors, context co-occurrence, test-path exclusion, kernel-config reads, sticky-bit/socket classification, registry-tarball and mutable-ref analysis); indicators whose remaining checks genuinely require a network probe or operator judgement stay inconclusive by design, and a guard test enforces that any collector flipping a gated indicator either attests its checks or documents why it abstains.
|
|
12
|
+
|
|
13
|
+
A run invoked with `--bundle-deterministic` against pathologically nested evidence no longer leaks the playbook's cross-process mutex: the depth-limit refusal threw after the lock was taken but before the release path was armed, so every later run of that playbook on the same host was blocked with a phantom "concurrent run holds the mutex" until the process exited. Attestation creation no longer strands an orphaned, unsigned attestation body holding the session slot when the body lands but its signature sidecar cannot be placed — the slot is released and the create can be retried; the unsafe-filename refusal also now returns its structured envelope instead of crashing. `doctor --ai-config --fix` no longer builds an `icacls` grant from an unset `USERNAME` (which stripped ACL inheritance and then failed on the unresolvable account, locking the file out); it resolves the user via the OS and withholds the automatic fix when no name is resolvable. The watch daemon's lockfile stale-reclaim race now exits 75 (WATCH_LOCK_CONTENTION) like every other contention path instead of a generic failure, and the CVE regression watcher stamps reports with the run's clock rather than a module-load timestamp.
|
|
14
|
+
|
|
15
|
+
End-to-end detection scenarios now assert that the staged indicator actually fired instead of accepting the operator-supplied verdict alone, one scenario no longer masks the engine's computed classification with an override, and RWEP floors are pinned at the computed values so a per-indicator weight regression trips them. The derived-index builders count `###` headings fence-aware (code-block lines no longer inflate section offsets), emit the documented `dlp_refs` key on CWE chains, and describe their real output shape; the zero-day response report template and remaining docs now state the blast-radius ceiling as 0-30.
|
|
16
|
+
|
|
17
|
+
## 0.16.23 — 2026-06-05
|
|
18
|
+
|
|
19
|
+
A broad correctness and hardening pass across the runtime, the data-refresh pipeline, the catalogs, and the release tooling.
|
|
20
|
+
|
|
21
|
+
### Bugs
|
|
22
|
+
|
|
23
|
+
Escalation conditions written against the analyze result — `analyze.compliance_theater_check.verdict == 'theater'` and `finding.*` paths — now actually fire: the escalation evaluator previously resolved only the flat context keys, so a playbook's theater-verdict or finding-keyed escalation (for example cloud-iam-incident's notify_legal) could never trigger. The evaluation context now exposes the assembled analyze result and the derived finding shape — the same roots feeds_into conditions already resolve — and the playbook validator rejects any condition rooted at a phase result that can never resolve in its context.
|
|
24
|
+
|
|
25
|
+
A declared regulatory clock can no longer drop out of a run's close-phase output. A govern obligation with a notification duty but no matching close notification action — fourteen playbooks carried at least one, such as cloud-iam-incident's AU Privacy Act NDB 720-hour window — now yields a synthesized, fully enriched record in `jurisdiction_notifications`, marked `synthesized_from_obligation` and carrying the computed deadline and the obligation's evidence checklist.
|
|
26
|
+
|
|
27
|
+
`refresh --from-cache` is now actually offline: the GHSA and OSV sources, which have no cache layer, fell through to live network fetches; they now return a structured skip instead. The cache-consume signature gate additionally pins the publisher key fingerprint like every other verification site, auto-discovery verifies each cached payload's sha256 against the signed cache index before deriving draft fields from it, and a KEV de-listing now clears the stale `cisa_kev_date` alongside zeroing the factor.
|
|
28
|
+
|
|
29
|
+
CLI failure paths emit the documented structured envelope instead of raw stack traces: `exceptd cve` and `exceptd rfc` against a corrupt catalog, curation apply-path write failures (disk full, read-only filesystem), the orchestrator's fatal handler, and framework-gap catalog-read failures under `--json`. `ci --max-rwep` with a missing value now errors instead of silently setting the gate cap to 1. `collect` failure bodies no longer advertise an exit code different from the one the process exits with. The watch daemon's lock-contention exit (75) is documented in `doctor --exit-codes`. Collector artifact summaries emit forward-slash paths on Windows, matching their SARIF evidence locations. Attestation diff and reattest regain their signal-side comparison fallback — it read a playbook path that never exists, so the fallback was dead. The typo-suggester's flag inventory for `run` now includes `--directive`, `--explain`, and `--signal-list`.
|
|
30
|
+
|
|
31
|
+
The MCP-config scanner now redacts secret-shaped values recursively before anything is emitted or persisted; previously only top-level fields were redacted, so an API key nested inside a server's config object leaked into scan output verbatim.
|
|
32
|
+
|
|
33
|
+
Four catalog entries carried RWEP factor values contradicting their own flags (CVE-2026-46333, CVE-2009-3459, CVE-2023-43472, and CVE-2026-31635 — the last with two compensating errors that cancelled in the aggregate tolerance). Their factors and scores are corrected, and the validator now checks every stored factor against the weight its source field implies, so compensating per-factor drift can no longer ship. The factor-shape detector also recognizes the catalog's canonical `ai_factor` and `reboot_required` keys, closing a gap where a mixed-shape factor block on exactly those keys passed undetected, and the required-field list the scorer validates against is loaded from the catalog schema itself (it had drifted: five schema-required fields unchecked, one optional field over-required).
|
|
34
|
+
|
|
35
|
+
The documented RWEP formula understated its heaviest factor: blast radius contributes up to 30 points, not the 15 stated in the README, the architecture notes, and the exploit-scoring skill's formula text. The docs now match the scorer, which was always correct — its worked examples already used 30. The derived skill indexes are rebuilt from skill frontmatter rather than a stale manifest cache, restoring dropped UK CAF and AU ISM control mappings and D3FEND references to the chain and cross-reference surfaces, and the ai-c2-detection skill's CWE-918 link is restored to the manifest and every index built from it. The pinned CWE catalog version in `sources/index.json` was three minor versions behind the shipped catalog and is corrected, with the source-currency guard extended to cover the CWE and D3FEND rows the way it already covered ATT&CK and ATLAS.
|
|
36
|
+
|
|
37
|
+
The release orchestrator could report a release complete while the publish was unconfirmed — a failed registry query short-circuited the final verification, and a non-success publish workflow only warned. Both now fail the phase: success requires the registry to positively report the just-published version.
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
The secret-scanning configuration no longer allowlists the entire playbooks directory by path — targeted patterns cover the legitimate example values instead, so a real credential committed into a playbook file is detectable again. Workflow version comments now match their pinned action SHAs, the publish workflow pins the same exact Node version the gates run on (held in lockstep by a three-way test against CI and the Docker harness), the ATLAS-currency workflow detects stale skills structurally instead of grepping a prose string, and the automated data-refresh PR body describes the structural gate it actually runs. The packaging gate pins every module the CLI requires at launch, the SBOM gate verifies the entry counts embedded in the published description against the live catalogs, skill frontmatter is validated against the shipped schema (previously decorative), and a new guard asserts that every byte-hashed shipped file type carries an LF-forcing `.gitattributes` rule. The catalog inventory in CONTEXT.md and the counts in the package description are pinned by tests so they fail loudly instead of rotting silently.
|
|
42
|
+
|
|
3
43
|
## 0.16.22 — 2026-06-05
|
|
4
44
|
|
|
5
45
|
When the automated external-data refresh applies a CISA KEV listing change to a catalog entry, it now updates the entry's RWEP factor and score in the same write, honouring whichever factor shape the entry stores. Previously only the flag flipped, leaving the stored score failing the factor-sum invariant the catalog enforces — the first real KEV listing the refresh applied surfaced this as a 25-point mismatch. A first listing also now carries its KEV date: the drift check emitted a date change only when the local entry already had one, so a newly listed CVE arrived dated null. The refresh workflow's post-apply gate now runs structural validation (catalog schema and RWEP coherence, cross-references, index freshness, skill lint) instead of the full test suite: the suite's curation-completeness checks assert guarantees that human curation supplies after import, so they gate the automated data PR's merge rather than its creation. The workflow also regenerates the SBOM over freshly applied data and consumes its own just-built prefetch cache on runners that have no signing key.
|
|
@@ -2128,6 +2168,8 @@ Documentation accuracy pass. README.md + ARCHITECTURE.md were still pinning ATLA
|
|
|
2128
2168
|
**`tests/docs-catalog-counts-pinned.test.js`** — new contract test asserts that README.md and ARCHITECTURE.md text matches the live catalog state for: ATLAS version (`data/atlas-ttps.json._meta.atlas_version`), ATT&CK version (`data/attack-techniques.json._meta.attack_version`), skill count (`manifest.json.skills.length`), D3FEND entry count, CVE catalog count, framework-gap entry count. Any future PR that bumps a catalog without updating the operator-facing docs fails the gate at CI time — eliminates the silent-drift class that v0.12.34 cleaned up.
|
|
2129
2169
|
|
|
2130
2170
|
|
|
2171
|
+
## 0.12.33 — 2026-05-15
|
|
2172
|
+
|
|
2131
2173
|
Same-day CVE intake (node-ipc supply-chain compromise) + cleanup of the long-standing `cred-stores` skill-vs-playbook semantic confusion.
|
|
2132
2174
|
|
|
2133
2175
|
### Features
|
package/CONTEXT.md
CHANGED
|
@@ -113,18 +113,18 @@ Skills and playbooks read from `data/`. Authoritative catalog inventory:
|
|
|
113
113
|
|
|
114
114
|
| File | Entries | Purpose |
|
|
115
115
|
|------|---------|---------|
|
|
116
|
-
| `cve-catalog.json` |
|
|
117
|
-
| `atlas-ttps.json` |
|
|
118
|
-
| `attack-techniques.json` |
|
|
119
|
-
| `framework-control-gaps.json` |
|
|
120
|
-
| `exploit-availability.json` |
|
|
116
|
+
| `cve-catalog.json` | 439 | CVEs with CVSS, RWEP score, EPSS estimates, CISA KEV flags, PoC and live-patch availability |
|
|
117
|
+
| `atlas-ttps.json` | 170 | MITRE ATLAS v5.6.0 (May 2026) techniques with framework gap flags |
|
|
118
|
+
| `attack-techniques.json` | 805 | MITRE ATT&CK techniques with framework coverage mappings |
|
|
119
|
+
| `framework-control-gaps.json` | 194 | Framework control gap entries: designed-for vs. what each control misses |
|
|
120
|
+
| `exploit-availability.json` | 28 | Per-CVE PoC locations, weaponization stage, AI-acceleration factor, live-patch status |
|
|
121
121
|
| `global-frameworks.json` | 35 jurisdictions | Patch SLAs and notification windows across global regulatory regimes |
|
|
122
|
-
| `zeroday-lessons.json` |
|
|
123
|
-
| `cwe-catalog.json` |
|
|
122
|
+
| `zeroday-lessons.json` | 439 | Learning-loop entries: zero-day → attack vector → control gap → framework gap → new control |
|
|
123
|
+
| `cwe-catalog.json` | 177 | CWE v4.20 entries (Top 25 2024 plus AI- and supply-chain-relevant weaknesses) |
|
|
124
124
|
| `d3fend-catalog.json` | 468 | MITRE D3FEND v1.3.0 defensive techniques for offensive → defensive mapping |
|
|
125
|
-
| `rfc-references.json` |
|
|
125
|
+
| `rfc-references.json` | 8888 | IETF RFC / Internet-Draft references with status, errata count, replaces / replaced-by, `last_verified` dates |
|
|
126
126
|
| `dlp-controls.json` | 22 | DLP control entries indexed by channel, classifier, surface, enforcement mode, evidence type |
|
|
127
|
-
| `playbooks/` |
|
|
127
|
+
| `playbooks/` | 33 | Playbook specifications (see above) |
|
|
128
128
|
| `_indexes/` | 17 derived files | Pre-computed indexes built by `npm run build-indexes` |
|
|
129
129
|
|
|
130
130
|
---
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
<picture>
|
|
4
|
-
<source media="(prefers-color-scheme: dark)" srcset="public/img/logo/exceptd-logo-dark.svg">
|
|
5
|
-
<img src="public/img/logo/exceptd-logo-primary.svg" alt="exceptd" width="220" />
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/blamejs/exceptd-skills/main/public/img/logo/exceptd-logo-dark.svg">
|
|
5
|
+
<img src="https://raw.githubusercontent.com/blamejs/exceptd-skills/main/public/img/logo/exceptd-logo-primary.svg" alt="exceptd" width="220" />
|
|
6
6
|
</picture>
|
|
7
7
|
|
|
8
8
|
# exceptd Security
|
|
@@ -77,7 +77,7 @@ Generate defensible policy exceptions for architectural realities frameworks don
|
|
|
77
77
|
### Risk Intelligence
|
|
78
78
|
|
|
79
79
|
**[exploit-scoring](skills/exploit-scoring/skill.md)**
|
|
80
|
-
Real-World Exploit Priority (RWEP) scoring beyond CVSS. Factors: CISA KEV status (0.25), public PoC (0.20), AI-assisted weaponization (0.15), active exploitation (0.20), patch availability (-0.15), live-patch availability (-0.10), blast radius (0.
|
|
80
|
+
Real-World Exploit Priority (RWEP) scoring beyond CVSS. Factors: CISA KEV status (0.25), public PoC (0.20), AI-assisted weaponization (0.15), active exploitation (0.20), patch availability (-0.15), live-patch availability (-0.10), blast radius (0.30). Pre-calculated RWEP scores for all CVEs in `data/cve-catalog.json`. Outputs RWEP alongside CVSS with plain-language priority guidance.
|
|
81
81
|
|
|
82
82
|
**[threat-model-currency](skills/threat-model-currency/skill.md)**
|
|
83
83
|
Score how current an organization's threat model is against 2026 threat reality. Checklist of 14 current threat classes against documented model coverage. Outputs: currency percentage, specific missing threat classes, recommended additions with ATLAS/ATT&CK references, prioritized update roadmap.
|
|
@@ -36,7 +36,7 @@ Generate structured, audience-appropriate reports from skill outputs. Translates
|
|
|
36
36
|
### 2. Technical Assessment Report
|
|
37
37
|
|
|
38
38
|
**Audience:** Security engineers, DevOps, Platform teams
|
|
39
|
-
**Template:**
|
|
39
|
+
**Template:** No canonical template — assemble from the content list below.
|
|
40
40
|
**Content:**
|
|
41
41
|
- Full CVE inventory with CVSS + RWEP
|
|
42
42
|
- Specific remediation commands and configurations
|
|
@@ -74,7 +74,7 @@ Generate structured, audience-appropriate reports from skill outputs. Translates
|
|
|
74
74
|
### 4. Threat Model Update Report
|
|
75
75
|
|
|
76
76
|
**Audience:** Security architects, threat modeling teams
|
|
77
|
-
**Template:**
|
|
77
|
+
**Template:** No canonical template — assemble from the content list below.
|
|
78
78
|
**Content:**
|
|
79
79
|
- Currency score before and after update
|
|
80
80
|
- Specific threat classes added
|
package/agents/skill-updater.md
CHANGED
|
@@ -96,7 +96,7 @@ For each affected skill:
|
|
|
96
96
|
## Quality Rules
|
|
97
97
|
|
|
98
98
|
- Never write to data files without a source-validator approval
|
|
99
|
-
- Never modify the interpretation of a framework control without a
|
|
99
|
+
- Never modify the interpretation of a framework control without a threat-researcher handoff package containing the framework gap update, verified by source-validator
|
|
100
100
|
- Never delete entries from zeroday-lessons.json or framework-control-gaps.json — mark as superseded, not deleted
|
|
101
101
|
- Always update `last_threat_review` in skill frontmatter after changes
|
|
102
102
|
- If a RWEP score changes by more than 15 points: flag for review rather than auto-applying
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Role
|
|
4
4
|
|
|
5
|
-
Cross-check all claims in a threat-researcher
|
|
5
|
+
Cross-check all claims in a threat-researcher handoff package against primary sources. Flag unverifiable claims. Produce a verification report that the skill-updater uses to decide what to accept.
|
|
6
6
|
|
|
7
7
|
This agent is the quality gate. It prevents bad data from entering the skill catalog.
|
|
8
8
|
|
|
9
9
|
## When to spawn
|
|
10
10
|
|
|
11
|
-
- After threat-researcher produces an intelligence package
|
|
12
|
-
- After framework-analyst produces a gap update
|
|
11
|
+
- After threat-researcher produces an intelligence package (including a framework gap update)
|
|
13
12
|
- On-demand audit of existing data/cve-catalog.json entries
|
|
14
13
|
- Before any new skill that contains specific CVE or TTP claims is merged
|
|
15
14
|
|
|
@@ -53,7 +52,7 @@ This agent is the quality gate. It prevents bad data from entering the skill cat
|
|
|
53
52
|
"agent": "source-validator",
|
|
54
53
|
"run_id": "[matches threat-researcher run_id]",
|
|
55
54
|
"timestamp": "[ISO 8601]",
|
|
56
|
-
"input_from": "threat-researcher
|
|
55
|
+
"input_from": "threat-researcher",
|
|
57
56
|
"verification_results": {
|
|
58
57
|
"passed": [
|
|
59
58
|
{
|
|
@@ -145,5 +145,5 @@ Research and validate new threat intelligence — CVEs, attack campaigns, new AT
|
|
|
145
145
|
|
|
146
146
|
- Does not write directly to data files — that is the skill-updater's job after source-validator approval
|
|
147
147
|
- Does not include direct exploit links in output
|
|
148
|
-
- Does not
|
|
148
|
+
- Does not apply framework gap updates itself — gap findings ship as part of the handoff package; the skill-updater applies them after source-validator approval
|
|
149
149
|
- Does not score risk — RWEP calculation is in lib/scoring.js
|
package/bin/exceptd.js
CHANGED
|
@@ -1776,8 +1776,9 @@ function dispatchPlaybook(cmd, argv) {
|
|
|
1776
1776
|
} catch (e) {
|
|
1777
1777
|
// v0.11.14 (#131): when the operator typed a skill name (kernel-lpe-triage)
|
|
1778
1778
|
// and got "Playbook not found," surface the playbooks that load that skill.
|
|
1779
|
-
//
|
|
1780
|
-
// the two because the website (and AGENTS.md) describe both as
|
|
1779
|
+
// Playbooks and skills are a many-to-many mapping: operators routinely
|
|
1780
|
+
// confuse the two because the website (and AGENTS.md) describe both as
|
|
1781
|
+
// runnable.
|
|
1781
1782
|
const m = e && e.message && e.message.match(/^Playbook not found: ([^\s(]+)/);
|
|
1782
1783
|
if (m) {
|
|
1783
1784
|
const wanted = m[1];
|
|
@@ -2448,10 +2449,10 @@ async function cmdCollect(runner, args, runOpts, pretty) {
|
|
|
2448
2449
|
let mod;
|
|
2449
2450
|
try { mod = require(collectorPath); }
|
|
2450
2451
|
catch (e) {
|
|
2451
|
-
return emitError(`collect: failed to load collector ${path.relative(PKG_ROOT, collectorPath)}: ${e.message}`, { verb: "collect", playbook_id: playbookId
|
|
2452
|
+
return emitError(`collect: failed to load collector ${path.relative(PKG_ROOT, collectorPath)}: ${e.message}`, { verb: "collect", playbook_id: playbookId }, pretty);
|
|
2452
2453
|
}
|
|
2453
2454
|
if (typeof mod.collect !== "function") {
|
|
2454
|
-
return emitError(`collect: collector at ${path.relative(PKG_ROOT, collectorPath)} does not export a collect() function`, { verb: "collect", playbook_id: playbookId
|
|
2455
|
+
return emitError(`collect: collector at ${path.relative(PKG_ROOT, collectorPath)} does not export a collect() function`, { verb: "collect", playbook_id: playbookId }, pretty);
|
|
2455
2456
|
}
|
|
2456
2457
|
|
|
2457
2458
|
// --cwd <path> overrides process.cwd(). Validated as an existing
|
|
@@ -2476,7 +2477,7 @@ async function cmdCollect(runner, args, runOpts, pretty) {
|
|
|
2476
2477
|
} catch (e) {
|
|
2477
2478
|
return emitError(
|
|
2478
2479
|
`collect: collector for "${playbookId}" threw an unhandled exception: ${e.message}. File a bug — collectors must catch their own errors and surface them via collector_errors[].`,
|
|
2479
|
-
{ verb: "collect", playbook_id: playbookId, stack: e.stack || null
|
|
2480
|
+
{ verb: "collect", playbook_id: playbookId, stack: e.stack || null },
|
|
2480
2481
|
pretty,
|
|
2481
2482
|
);
|
|
2482
2483
|
}
|
|
@@ -2538,7 +2539,7 @@ async function cmdCollect(runner, args, runOpts, pretty) {
|
|
|
2538
2539
|
} catch (e) {
|
|
2539
2540
|
return emitError(
|
|
2540
2541
|
`collect: --resolve failed for "${playbookId}": ${e.message}`,
|
|
2541
|
-
{ verb: "collect", playbook_id: playbookId
|
|
2542
|
+
{ verb: "collect", playbook_id: playbookId },
|
|
2542
2543
|
pretty,
|
|
2543
2544
|
);
|
|
2544
2545
|
}
|
|
@@ -4448,7 +4449,7 @@ function persistAttestation(args) {
|
|
|
4448
4449
|
if (!/^[A-Za-z0-9._-]{1,64}\.json$/.test(filename || "")) {
|
|
4449
4450
|
return {
|
|
4450
4451
|
ok: false,
|
|
4451
|
-
error: `Refusing to persist attestation with unsafe filename: ${JSON.stringify(filename).slice(0, 80)}.`,
|
|
4452
|
+
error: `Refusing to persist attestation with unsafe filename: ${String(JSON.stringify(filename)).slice(0, 80)}.`,
|
|
4452
4453
|
existingPath: null,
|
|
4453
4454
|
};
|
|
4454
4455
|
}
|
|
@@ -4525,7 +4526,17 @@ function persistAttestation(args) {
|
|
|
4525
4526
|
fs.renameSync(jsonTmp, filePath);
|
|
4526
4527
|
}
|
|
4527
4528
|
// Slot won — place the sidecar (sigPath is fresh on a create).
|
|
4528
|
-
|
|
4529
|
+
try {
|
|
4530
|
+
fs.renameSync(sigTmp, sigPath);
|
|
4531
|
+
} catch (sigErr) {
|
|
4532
|
+
// The body landed but its sidecar did not. Left in place, the
|
|
4533
|
+
// orphaned unsigned body would hold the slot forever: every
|
|
4534
|
+
// retry collides with EEXIST and verification reports the
|
|
4535
|
+
// attestation unsigned. Release the slot before rethrowing so
|
|
4536
|
+
// the create can be retried cleanly.
|
|
4537
|
+
try { fs.unlinkSync(filePath); } catch { /* best-effort slot release */ }
|
|
4538
|
+
throw sigErr;
|
|
4539
|
+
}
|
|
4529
4540
|
try { fs.unlinkSync(jsonTmp); } catch { /* hard-link path leaves a second name */ }
|
|
4530
4541
|
} else {
|
|
4531
4542
|
// Force-overwrite, under the persist lock: atomic replace of both.
|
|
@@ -4912,7 +4923,12 @@ function verifyAttestationSidecar(attFile) {
|
|
|
4912
4923
|
if (pubKey) {
|
|
4913
4924
|
const pinError = assertExpectedFingerprint(pubKey);
|
|
4914
4925
|
if (pinError) {
|
|
4915
|
-
|
|
4926
|
+
// A pin mismatch means the host's public key is NOT the published
|
|
4927
|
+
// one — verification against it would prove nothing. Carry a tamper
|
|
4928
|
+
// class so consumers (reattest's refusal predicate, the sidecar
|
|
4929
|
+
// classifier) treat this as tamper evidence, not a benign
|
|
4930
|
+
// unsigned-attestation config state.
|
|
4931
|
+
return { file: attFile, signed: false, verified: false, tamper_class: "fingerprint-mismatch", reason: pinError };
|
|
4916
4932
|
}
|
|
4917
4933
|
}
|
|
4918
4934
|
if (!fs.existsSync(sigPath)) {
|
|
@@ -5136,25 +5152,7 @@ function cmdReattest(runner, args, runOpts, pretty) {
|
|
|
5136
5152
|
// tampering. `verified === false && signed === true` is the real tamper
|
|
5137
5153
|
// signal.
|
|
5138
5154
|
const verify = verifyAttestationSidecar(attFile);
|
|
5139
|
-
|
|
5140
|
-
// (signed-but-invalid, sidecar-corrupt, unsigned-substitution) refuses
|
|
5141
|
-
// replay unless --force-replay is set. A predicate of only
|
|
5142
|
-
// `verify.signed && !verify.verified` would miss corrupt-JSON sidecars
|
|
5143
|
-
// and substituted "unsigned" sidecars on a host WITH a private key —
|
|
5144
|
-
// both of which let replay proceed against forged input.
|
|
5145
|
-
const isSignedTamper = verify.signed && !verify.verified;
|
|
5146
|
-
const isClassTamper = !verify.signed && (
|
|
5147
|
-
verify.tamper_class === "sidecar-corrupt"
|
|
5148
|
-
|| verify.tamper_class === "unsigned-substitution"
|
|
5149
|
-
// Extend tamper-class refusal to algorithm-unsupported sidecars —
|
|
5150
|
-
// anything other than "Ed25519" or "unsigned". Without explicit
|
|
5151
|
-
// refusal, a sidecar that throws inside crypto.verify (e.g.
|
|
5152
|
-
// signature_base64 missing on a downgrade-bait shape) emerges as
|
|
5153
|
-
// signed:true + verified:false through the catch block by accident.
|
|
5154
|
-
// The strict pre-check surfaces the class directly; refuse on it too.
|
|
5155
|
-
|| verify.tamper_class === "algorithm-unsupported"
|
|
5156
|
-
);
|
|
5157
|
-
if ((isSignedTamper || isClassTamper) && !args["force-replay"]) {
|
|
5155
|
+
if (isTamperedSidecarVerify(verify) && !args["force-replay"]) {
|
|
5158
5156
|
process.stderr.write(`[exceptd reattest] TAMPERED: attestation at ${attFile} failed Ed25519 verification (${verify.reason}). Refusing to replay against forged input. Pass --force-replay to override (the replay output records sidecar_verify so the audit trail captures the override).\n`);
|
|
5159
5157
|
const body = {
|
|
5160
5158
|
ok: false,
|
|
@@ -5169,7 +5167,7 @@ function cmdReattest(runner, args, runOpts, pretty) {
|
|
|
5169
5167
|
process.exitCode = EXIT_CODES.TAMPERED;
|
|
5170
5168
|
return;
|
|
5171
5169
|
}
|
|
5172
|
-
if ((
|
|
5170
|
+
if (isTamperedSidecarVerify(verify) && args["force-replay"]) {
|
|
5173
5171
|
process.stderr.write(`[exceptd reattest] WARNING: --force-replay overriding failed signature verification on ${attFile} (${verify.reason}). The replay output records sidecar_verify so the override is audit-visible.\n`);
|
|
5174
5172
|
} else if (!verify.signed && verify.reason && verify.reason.includes("no .sig sidecar") && !args["force-replay"]) {
|
|
5175
5173
|
// missing-sidecar is NOT benign. The previous flow accepted
|
|
@@ -5455,6 +5453,39 @@ function cmdReattest(runner, args, runOpts, pretty) {
|
|
|
5455
5453
|
* sidecar_verify object so auditors can filter override events by class
|
|
5456
5454
|
* without regexing the human-readable reason string.
|
|
5457
5455
|
*/
|
|
5456
|
+
/**
|
|
5457
|
+
* Tamper predicate over a verifyAttestationSidecar() result. Collapses
|
|
5458
|
+
* tamper-class detection: any non-benign sidecar state refuses replay
|
|
5459
|
+
* unless --force-replay is set. A predicate of only
|
|
5460
|
+
* `verify.signed && !verify.verified` would miss corrupt-JSON sidecars,
|
|
5461
|
+
* substituted "unsigned" sidecars on a host WITH a private key,
|
|
5462
|
+
* downgrade-bait algorithm shapes, and a keys/public.pem failing the
|
|
5463
|
+
* EXPECTED_FINGERPRINT pin — each of which would let replay proceed
|
|
5464
|
+
* against forged input. Keeping the class list HERE (one shared helper)
|
|
5465
|
+
* means a new tamper class added to the verifier has exactly one refusal
|
|
5466
|
+
* site to extend.
|
|
5467
|
+
*/
|
|
5468
|
+
function isTamperedSidecarVerify(verify) {
|
|
5469
|
+
if (!verify || typeof verify !== "object") return false;
|
|
5470
|
+
const isSignedTamper = verify.signed && !verify.verified;
|
|
5471
|
+
const isClassTamper = !verify.signed && (
|
|
5472
|
+
verify.tamper_class === "sidecar-corrupt"
|
|
5473
|
+
|| verify.tamper_class === "unsigned-substitution"
|
|
5474
|
+
// Anything other than "Ed25519" or "unsigned": a sidecar that throws
|
|
5475
|
+
// inside crypto.verify (e.g. signature_base64 missing on a
|
|
5476
|
+
// downgrade-bait shape) would otherwise emerge as signed:true +
|
|
5477
|
+
// verified:false through the catch block by accident. The strict
|
|
5478
|
+
// pre-check surfaces the class directly; refuse on it too.
|
|
5479
|
+
|| verify.tamper_class === "algorithm-unsupported"
|
|
5480
|
+
// A keys/public.pem that fails the EXPECTED_FINGERPRINT pin is the
|
|
5481
|
+
// key-swap attack the pin exists for — verification "against" the
|
|
5482
|
+
// swapped key proves nothing, so replay refuses exactly like the
|
|
5483
|
+
// other tamper classes (attest verify already refuses on this).
|
|
5484
|
+
|| verify.tamper_class === "fingerprint-mismatch"
|
|
5485
|
+
);
|
|
5486
|
+
return Boolean(isSignedTamper || isClassTamper);
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5458
5489
|
function classifySidecarVerify(verify) {
|
|
5459
5490
|
if (!verify || typeof verify !== "object") return "unknown";
|
|
5460
5491
|
if (verify.signed && verify.verified) return "verified";
|
|
@@ -5464,6 +5495,7 @@ function classifySidecarVerify(verify) {
|
|
|
5464
5495
|
// `algorithm-unsupported` is its own class label so log scrapers /
|
|
5465
5496
|
// dashboards can filter downgrade-bait events without parsing the reason.
|
|
5466
5497
|
if (verify.tamper_class === "algorithm-unsupported") return "algorithm-unsupported";
|
|
5498
|
+
if (verify.tamper_class === "fingerprint-mismatch") return "fingerprint-mismatch";
|
|
5467
5499
|
if (typeof verify.reason === "string" && verify.reason.startsWith("attestation explicitly unsigned")) return "explicitly-unsigned";
|
|
5468
5500
|
if (typeof verify.reason === "string" && verify.reason.includes("no .sig sidecar")) return "no-sidecar";
|
|
5469
5501
|
if (typeof verify.reason === "string" && verify.reason.includes("no public key")) return "no-public-key";
|
|
@@ -6045,7 +6077,7 @@ function _playbookSignalCatalog(runner, playbookId) {
|
|
|
6045
6077
|
try {
|
|
6046
6078
|
const pb = runner.loadPlaybook ? runner.loadPlaybook(playbookId) : null;
|
|
6047
6079
|
if (!pb) return null;
|
|
6048
|
-
const inds = (pb.phases?.
|
|
6080
|
+
const inds = (pb.phases?.detect?.indicators || []).filter(i => i && i.id);
|
|
6049
6081
|
if (inds.length === 0) return null;
|
|
6050
6082
|
return Object.fromEntries(inds.map(i => [i.id, 'inconclusive']));
|
|
6051
6083
|
} catch { return null; }
|
|
@@ -6943,6 +6975,14 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
6943
6975
|
// /grant:r <USER>:F` to strip inherited entries.
|
|
6944
6976
|
const aclCheck = checkWindowsAcl(childAbs);
|
|
6945
6977
|
if (aclCheck.ok) continue;
|
|
6978
|
+
// Resolve the grant principal defensively: an unset USERNAME
|
|
6979
|
+
// would otherwise interpolate "undefined:F", and icacls applies
|
|
6980
|
+
// /inheritance:r BEFORE failing on the unresolvable account —
|
|
6981
|
+
// stripping every inherited entry and locking the file out.
|
|
6982
|
+
// os.userInfo() works even when the env var is absent.
|
|
6983
|
+
const aclUser = process.env.USERNAME || (() => {
|
|
6984
|
+
try { return require('os').userInfo().username; } catch { return null; }
|
|
6985
|
+
})();
|
|
6946
6986
|
findings.push({
|
|
6947
6987
|
path: `${displayRoot}/${childRel}`,
|
|
6948
6988
|
mode: null,
|
|
@@ -6950,7 +6990,9 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
6950
6990
|
issue: 'broader_than_user_only_acl',
|
|
6951
6991
|
acl_extra_principals: aclCheck.extraPrincipals,
|
|
6952
6992
|
hint: `icacls "${childAbs}" /inheritance:r /grant:r %USERNAME%:F # NEW-CTRL-050: AI-assistant configs holding MCP tokens / API keys must restrict ACL to the workstation user`,
|
|
6953
|
-
|
|
6993
|
+
...(aclUser
|
|
6994
|
+
? { fix_command: ['icacls', childAbs, '/inheritance:r', '/grant:r', `${aclUser}:F`] }
|
|
6995
|
+
: { fix_unavailable: 'current user name unresolvable (USERNAME unset); apply the hint manually' }),
|
|
6954
6996
|
});
|
|
6955
6997
|
continue;
|
|
6956
6998
|
}
|
|
@@ -8369,6 +8411,17 @@ function cmdAsk(runner, args, runOpts, pretty) {
|
|
|
8369
8411
|
*/
|
|
8370
8412
|
function cmdCi(runner, args, runOpts, pretty) {
|
|
8371
8413
|
const scope = args.scope;
|
|
8414
|
+
// A value-less `--max-rwep` parses as boolean true and would coerce to
|
|
8415
|
+
// Number(true) === 1 — a finite, non-negative cap that slips past the
|
|
8416
|
+
// numeric guard below and silently sets an extraordinarily strict gate.
|
|
8417
|
+
// Treat the forgotten value as a usage error, same as a non-numeric one.
|
|
8418
|
+
if (args["max-rwep"] === true) {
|
|
8419
|
+
return emitError(
|
|
8420
|
+
"ci: --max-rwep requires a non-negative number.",
|
|
8421
|
+
{ verb: "ci", flag: "max-rwep" },
|
|
8422
|
+
pretty,
|
|
8423
|
+
);
|
|
8424
|
+
}
|
|
8372
8425
|
const maxRwep = args["max-rwep"] !== undefined ? Number(args["max-rwep"]) : null;
|
|
8373
8426
|
// Reject a non-numeric / negative cap rather than silently coercing it.
|
|
8374
8427
|
// `--max-rwep abc` previously became Number→NaN→0, degenerating the gate to
|
|
@@ -9009,4 +9062,10 @@ function cmdCi(runner, args, runOpts, pretty) {
|
|
|
9009
9062
|
|
|
9010
9063
|
if (require.main === module) main();
|
|
9011
9064
|
|
|
9012
|
-
module.exports = {
|
|
9065
|
+
module.exports = {
|
|
9066
|
+
COMMANDS, PKG_ROOT, PLAYBOOK_VERBS, persistAttestation,
|
|
9067
|
+
// internal helpers exposed for tests
|
|
9068
|
+
_isTamperedSidecarVerify: isTamperedSidecarVerify,
|
|
9069
|
+
_classifySidecarVerify: classifySidecarVerify,
|
|
9070
|
+
_verifyAttestationSidecar: verifyAttestationSidecar,
|
|
9071
|
+
};
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-06-
|
|
3
|
+
"generated_at": "2026-06-05T22:20:42.479Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 63,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "fe65c62f46bbb3c069247eb19169ac4ab99bf0354e3e9cfe432782bf402a918a",
|
|
8
8
|
"data/atlas-ttps.json": "f66b456cf82a3c20575d8479de41f7b11b7ee5693eb1fcf64a67e162ae1b88a2",
|
|
9
9
|
"data/attack-techniques.json": "c39f28e3402ef13ad9b7076819f63fda67a22f97e3e375cfe01c4a4e0beff7c9",
|
|
10
|
-
"data/cve-catalog.json": "
|
|
11
|
-
"data/cwe-catalog.json": "
|
|
10
|
+
"data/cve-catalog.json": "51d8425a49e5cc0375d0a154a83a16816e99c3141a5bbafe6383607ca11be240",
|
|
11
|
+
"data/cwe-catalog.json": "b398003b68b0d9539d13a5536e933005149fd05f3d33978297c870987542cd86",
|
|
12
12
|
"data/d3fend-catalog.json": "9a54bccb9f24f84b32024216cc3f53819a053721ac8ab43c326859e68fc0ffaf",
|
|
13
13
|
"data/dlp-controls.json": "d2406c482dddd30e49203879999dc4b3a7fd4d0494d6a61d86b91ee76415df19",
|
|
14
14
|
"data/exploit-availability.json": "ec2656f0d9a893610e27b43eb6035fe9b18e057c9f6dfaac7e7d4959bbcbb795",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"skills/mcp-agent-trust/skill.md": "0752834acde0303d6d1e36be4b320eac3d34fde715bb8d71f3ad9e801d701482",
|
|
22
22
|
"skills/framework-gap-analysis/skill.md": "c2da8cc184ac4277309896bf4fe6afe23c419575b297734a8fc168f42d1805e9",
|
|
23
23
|
"skills/compliance-theater/skill.md": "02b51b932ded4b9b74844ecf842ee3f4d6b09699ebe9cdba457e2c9b7da10ebd",
|
|
24
|
-
"skills/exploit-scoring/skill.md": "
|
|
24
|
+
"skills/exploit-scoring/skill.md": "628f4853e8d6b6ac2ad8ad159f1c9d74c392727f4ebf9bc1d7d8e43074b08a3a",
|
|
25
25
|
"skills/rag-pipeline-security/skill.md": "49b1910e996f01df1382714f9307ead98028fb5b6911bb9022cb8e5c0edf0723",
|
|
26
26
|
"skills/ai-c2-detection/skill.md": "08fee5607e4972a3c0e1e31d58d4ddfeffeae1302df22416f3e24c20229fb782",
|
|
27
27
|
"skills/policy-exception-gen/skill.md": "c549f48c2e44759a74c2c6306f0eb34ea26152fb2a3b0e7e96505d5b174f1bd4",
|
|
@@ -73,21 +73,21 @@
|
|
|
73
73
|
"index_stats": {
|
|
74
74
|
"xref_entries": {
|
|
75
75
|
"cwe_refs": 53,
|
|
76
|
-
"d3fend_refs":
|
|
77
|
-
"framework_gaps":
|
|
76
|
+
"d3fend_refs": 22,
|
|
77
|
+
"framework_gaps": 96,
|
|
78
78
|
"atlas_refs": 10,
|
|
79
79
|
"attack_refs": 51,
|
|
80
|
-
"rfc_refs":
|
|
80
|
+
"rfc_refs": 35,
|
|
81
81
|
"dlp_refs": 0
|
|
82
82
|
},
|
|
83
|
-
"trigger_table_entries":
|
|
83
|
+
"trigger_table_entries": 688,
|
|
84
84
|
"chains_cve_entries": 426,
|
|
85
85
|
"chains_cwe_entries": 177,
|
|
86
86
|
"jurisdictions_indexed": 29,
|
|
87
87
|
"handoff_dag_nodes": 51,
|
|
88
88
|
"summary_cards": 51,
|
|
89
89
|
"section_offsets_skills": 51,
|
|
90
|
-
"token_budget_total_approx":
|
|
90
|
+
"token_budget_total_approx": 435955,
|
|
91
91
|
"recipes": 8,
|
|
92
92
|
"jurisdiction_clocks": 29,
|
|
93
93
|
"did_ladders": 8,
|
|
@@ -112,35 +112,35 @@
|
|
|
112
112
|
"type": "skill_review",
|
|
113
113
|
"artifact": "compliance-theater",
|
|
114
114
|
"path": "skills/compliance-theater/skill.md",
|
|
115
|
-
"note": "Detect where an organization passes an audit but remains exposed — seven documented compliance theater patterns"
|
|
115
|
+
"note": "Detect where an organization passes an audit but remains exposed — seven documented compliance theater patterns with specific detection tests"
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"date": "2026-05-22",
|
|
119
119
|
"type": "skill_review",
|
|
120
120
|
"artifact": "rag-pipeline-security",
|
|
121
121
|
"path": "skills/rag-pipeline-security/skill.md",
|
|
122
|
-
"note": "RAG-specific threat model — embedding manipulation, vector store poisoning, retrieval filter bypass, indirect prompt injection"
|
|
122
|
+
"note": "RAG-specific threat model — embedding manipulation, vector store poisoning, retrieval filter bypass, indirect prompt injection — no current framework coverage"
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"date": "2026-05-22",
|
|
126
126
|
"type": "skill_review",
|
|
127
127
|
"artifact": "policy-exception-gen",
|
|
128
128
|
"path": "skills/policy-exception-gen/skill.md",
|
|
129
|
-
"note": "Generate defensible policy exceptions for architectural realities — ephemeral infra, AI pipelines, ZTA, no-reboot patching"
|
|
129
|
+
"note": "Generate defensible policy exceptions for architectural realities — ephemeral infra, AI pipelines, ZTA, no-reboot patching, with compensating controls and auditor-ready justification"
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
"date": "2026-05-22",
|
|
133
133
|
"type": "skill_review",
|
|
134
134
|
"artifact": "pqc-first",
|
|
135
135
|
"path": "skills/pqc-first/skill.md",
|
|
136
|
-
"note": "Post-quantum cryptography first mentality — hard version gates
|
|
136
|
+
"note": "Post-quantum cryptography first mentality — hard version gates, algorithm sunset tracking, loopback learning for NIST/IETF standards evolution"
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"date": "2026-05-22",
|
|
140
140
|
"type": "skill_review",
|
|
141
141
|
"artifact": "skill-update-loop",
|
|
142
142
|
"path": "skills/skill-update-loop/skill.md",
|
|
143
|
-
"note": "Meta-skill for keeping all exceptd skills current —
|
|
143
|
+
"note": "Meta-skill for keeping all exceptd skills current — fires on new CVEs, ATLAS updates, framework changes, and forward_watch triggers"
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
"date": "2026-05-22",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"type": "skill_review",
|
|
162
162
|
"artifact": "ransomware-response",
|
|
163
163
|
"path": "skills/ransomware-response/skill.md",
|
|
164
|
-
"note": "Ransomware-specific incident response — OFAC
|
|
164
|
+
"note": "Ransomware-specific incident response — OFAC sanctions screening as payment-posture blocker, EU Reg 2014/833 + UK OFSI + AU DFAT + JP MOF cross-jurisdiction sanctions lookups, decryptor availability via No More Ransom + vendor-specific catalogs, cyber-insurance carrier 24h notification, negotiator-engagement legal posture, immutable-backup viability test, PHI exfil-before-encrypt as distinct breach class, parallel jurisdiction clocks (NIS2 24h / DORA 4h / GDPR 72h / SEC 8-K 96h / HIPAA 60d / CIRCIA 72h / NYDFS 500.17 24h ransom-payment)"
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"date": "2026-05-19",
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"type": "skill_review",
|
|
236
236
|
"artifact": "ai-attack-surface",
|
|
237
237
|
"path": "skills/ai-attack-surface/skill.md",
|
|
238
|
-
"note": "Comprehensive AI/ML attack surface assessment mapped to MITRE ATLAS v5.6.0 with gap flags"
|
|
238
|
+
"note": "Comprehensive AI/ML attack surface assessment mapped to MITRE ATLAS v5.6.0 with explicit framework gap flags"
|
|
239
239
|
},
|
|
240
240
|
{
|
|
241
241
|
"date": "2026-05-17",
|
|
@@ -249,14 +249,14 @@
|
|
|
249
249
|
"type": "skill_review",
|
|
250
250
|
"artifact": "ai-c2-detection",
|
|
251
251
|
"path": "skills/ai-c2-detection/skill.md",
|
|
252
|
-
"note": "Detect adversary use of AI APIs as covert C2 — SesameOp pattern, PROMPTFLUX/PROMPTSTEAL behavioral signatures"
|
|
252
|
+
"note": "Detect adversary use of AI APIs as covert C2 — SesameOp pattern, PROMPTFLUX/PROMPTSTEAL behavioral signatures, response playbook"
|
|
253
253
|
},
|
|
254
254
|
{
|
|
255
255
|
"date": "2026-05-15",
|
|
256
256
|
"type": "skill_review",
|
|
257
257
|
"artifact": "kernel-lpe-triage",
|
|
258
258
|
"path": "skills/kernel-lpe-triage/skill.md",
|
|
259
|
-
"note": "Assess Linux kernel LPE exposure — Copy Fail, Dirty Frag, live-patch vs. reboot remediation"
|
|
259
|
+
"note": "Assess Linux kernel LPE exposure — Copy Fail, Dirty Frag, Fragnesia, live-patch vs. reboot remediation paths, framework gap declarations"
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"date": "2026-05-15",
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
"type": "skill_review",
|
|
292
292
|
"artifact": "sector-telecom",
|
|
293
293
|
"path": "skills/sector-telecom/skill.md",
|
|
294
|
-
"note": "Telecom and 5G security for mid-2026 — Salt Typhoon, Volt Typhoon, CALEA / IPA-LI gateway compromise, signaling-protocol abuse (SS7 / Diameter / GTP), 5G N6 / N9 isolation, gNB / DU / CU integrity, OEM-equipment supply-chain compromise, AI-RAN / O-RAN security"
|
|
294
|
+
"note": "Telecom and 5G security for mid-2026 — Salt Typhoon, Volt Typhoon, CALEA / IPA-LI gateway compromise, signaling-protocol abuse (SS7 / Diameter / GTP), 5G N6 / N9 isolation, gNB / DU / CU integrity, OEM-equipment supply-chain compromise, AI-RAN / O-RAN security; FCC CPNI + 4-business-day notification, NIS2 Annex I telecom essential entities, UK TSA 2021 + Ofcom, AU SOCI / TSSR, GSMA NESAS, 3GPP TR 33.926 + TS 33.501, ITU-T X.805."
|
|
295
295
|
},
|
|
296
296
|
{
|
|
297
297
|
"date": "2026-05-15",
|
|
@@ -305,7 +305,7 @@
|
|
|
305
305
|
"type": "skill_review",
|
|
306
306
|
"artifact": "cloud-iam-incident",
|
|
307
307
|
"path": "skills/cloud-iam-incident/skill.md",
|
|
308
|
-
"note": "Cloud-IAM incident response for AWS / GCP / Azure — account takeover, IAM role assumption abuse, access-key compromise, cross-account assume-role chains, federated-trust attacks, IMDS metadata exfiltration, and Snowflake-AA24-class IdP-to-cloud credential reuse"
|
|
308
|
+
"note": "Cloud-IAM incident response for AWS / GCP / Azure — account takeover, IAM role assumption abuse, access-key compromise, cross-account assume-role chains, federated-trust attacks (IAM Identity Center / Workload Identity Federation / Azure managed identity), IMDS metadata exfiltration, and Snowflake-AA24-class IdP-to-cloud credential reuse"
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
"date": "2026-05-15",
|
|
@@ -455,7 +455,7 @@
|
|
|
455
455
|
"type": "skill_review",
|
|
456
456
|
"artifact": "security-maturity-tiers",
|
|
457
457
|
"path": "skills/security-maturity-tiers/skill.md",
|
|
458
|
-
"note": "Three-tier implementation roadmap — MVP
|
|
458
|
+
"note": "Three-tier implementation roadmap — MVP you can ship today, practical best practices useable now, overkill gold standard for defense-in-depth"
|
|
459
459
|
}
|
|
460
460
|
]
|
|
461
461
|
}
|