@blamejs/exceptd-skills 0.13.4 → 0.13.6
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 +16 -0
- package/CHANGELOG.md +58 -0
- package/bin/exceptd.js +145 -17
- package/data/_indexes/_meta.json +8 -8
- package/data/_indexes/activity-feed.json +18 -18
- package/data/_indexes/catalog-summaries.json +6 -6
- package/data/_indexes/chains.json +3943 -0
- package/data/_indexes/frequency.json +3 -0
- package/data/atlas-ttps.json +74 -1
- package/data/attack-techniques.json +125 -8
- package/data/cve-catalog.json +1964 -2
- package/data/framework-control-gaps.json +47 -4
- package/data/playbooks/ai-discovered-cve-triage.json +1146 -0
- package/data/playbooks/cicd-pipeline-compromise.json +3 -0
- package/data/playbooks/cred-stores.json +1 -0
- package/data/playbooks/crypto.json +3 -0
- package/data/playbooks/framework.json +3 -0
- package/data/playbooks/idp-incident.json +2 -1
- package/data/playbooks/kernel.json +1 -0
- package/data/playbooks/mcp.json +27 -2
- package/data/playbooks/post-quantum-migration.json +1268 -0
- package/data/playbooks/runtime.json +1 -0
- package/data/playbooks/sbom.json +3 -0
- package/data/playbooks/supply-chain-recovery.json +1332 -0
- package/data/zeroday-lessons.json +1749 -2
- package/lib/schemas/cve-catalog.schema.json +2 -1
- package/lib/validate-cve-catalog.js +27 -0
- package/manifest.json +44 -44
- package/orchestrator/index.js +69 -2
- package/package.json +1 -1
- package/sbom.cdx.json +62 -29
package/AGENTS.md
CHANGED
|
@@ -228,6 +228,22 @@ Recently added (use the IDs in skill prose and operator briefings; full text in
|
|
|
228
228
|
| `NEW-CTRL-053` | MCP-SERVER-CONFIG-ALLOWLIST | `CVE-2026-30623` (Anthropic MCP SDK stdio injection) | NIST AI RMF MEASURE 2.7, OWASP LLM Top 10 2025 LLM05 |
|
|
229
229
|
| `NEW-CTRL-054` | BACKUP-TIER-NETWORK-ISOLATION | `CVE-2025-59389` (QNAP Hyper Data Protector preauth RCE) | ISO-27001-2022 A.8.13, NIS2 Art.21 business-continuity |
|
|
230
230
|
| `NEW-CTRL-055` | SECURITY-TOOL-INTEGRITY-VERIFICATION | `CVE-2025-11837` (QNAP Malware Remover code-injection) | NIST-800-53 SI-3, ISO-27001-2022 A.8.7, PCI-DSS 4.0 §5.1 |
|
|
231
|
+
| `NEW-CTRL-056` | MOBILE-ENDPOINT-MDM-ENFORCED-KEV-SLA | `CVE-2025-14174` / `CVE-2025-43529` / `CVE-2025-24201` / `CVE-2025-43300` | NIST-800-53 SI-2, ISO-27001-2022 A.8.8, CIS Benchmarks mobile-OS profiles |
|
|
232
|
+
| `NEW-CTRL-057` | BROWSER-MANAGED-UPDATE-NO-DEFERRAL | `CVE-2025-10585` / `CVE-2025-14174` / `CVE-2025-43529` / `CVE-2025-4919` | NIST-800-53 SI-2, CISA KEV SLA, vendor-channel security-release contracts |
|
|
233
|
+
| `NEW-CTRL-058` | CLOUD-CONTROL-PLANE-CROSS-TENANT-CLAIM-VALIDATION | `CVE-2025-55241` (Entra ID Actor-token impersonation) | NIST-800-53 AC-2/AU-2/AC-16, ISO-27001-2022 A.5.16, CIS Cloud Foundations |
|
|
234
|
+
| `NEW-CTRL-059` | SENSITIVE-DATA-IN-LOGS-LINT | `CVE-2025-21085` (Cisco Duo credential leakage) | NIST-800-53 AU-9/SI-12, ISO-27001-2022 A.8.15, PCI-DSS 4.0 §10.5 |
|
|
235
|
+
| `NEW-CTRL-060` | DATABASE-SERVER-SIDE-SCRIPTING-DEFAULT-DENY | `CVE-2025-49844` (Redis RediShell Lua UAF) | NIST-800-53 CM-6/CM-7, ISO-27001-2022 A.8.9, vendor secure-baseline profiles |
|
|
236
|
+
| `NEW-CTRL-061` | IN-MEMORY-DATASTORE-MEMORY-DISCLOSURE-NETWORK-EXPOSURE-AUDIT | `CVE-2025-14847` (MongoBleed) | NIST-800-53 SC-7/SC-28, PCI-DSS 4.0 §1.4, ISO-27001-2022 A.8.20 |
|
|
237
|
+
| `NEW-CTRL-062` | HTTP2-STREAM-RESET-ACCOUNTING | `CVE-2025-8671` (MadeYouReset) | NIST-800-53 SC-5, ISO-27001-2022 A.8.6, vendor HTTP/2 secure-default profiles |
|
|
238
|
+
| `NEW-CTRL-063` | MULTIMODAL-INFERENCE-INPUT-DECODER-ISOLATION | `CVE-2026-22778` (vLLM heap-overflow RCE) | NIST AI RMF MANAGE 4.1, NIST-800-53 SC-39, OWASP LLM Top 10 2025 LLM10 |
|
|
239
|
+
| `NEW-CTRL-064` | LLM-OUTPUT-DESERIALIZATION-TRUST-ZONE | `CVE-2025-68664` (LangChain LangGrinch) | NIST AI RMF MEASURE 2.7, OWASP LLM Top 10 2025 LLM03/LLM08, NIST-800-53 SI-10 |
|
|
240
|
+
| `NEW-CTRL-065` | AI-MODEL-SERVER-DEFAULT-AUTHENTICATION | `CVE-2026-7482` (Ollama Bleeding Llama) | NIST-800-53 IA-2/AC-3, ISO-27001-2022 A.8.5, NIST AI RMF GOVERN 5.1 |
|
|
241
|
+
| `NEW-CTRL-066` | AGENTIC-IDE-HOST-EXECUTION-SANDBOX | `CVE-2025-55319` (VSCode agentic-AI command-injection) | NIST AI RMF MANAGE 4.1, ISO/IEC 42001 §6.1.4, NIST-800-53 SC-39 |
|
|
242
|
+
| `NEW-CTRL-067` | AI-PLATFORM-CONTROL-PLANE-RBAC-OVERLAY-AUDIT | `CVE-2025-10725` (OpenShift AI privilege escalation) | NIST-800-53 AC-2/AC-6, CIS Kubernetes Benchmark §5, NIST AI RMF GOVERN 1.5 |
|
|
243
|
+
| `NEW-CTRL-068` | HYPERVISOR-VM-ESCAPE-TENANCY-ASSUMPTION | `CVE-2025-22224` / `CVE-2025-22225` / `CVE-2025-22226` (VMSA-2025-0004 ESXi chain) | NIST-800-53 SC-7/SI-2, ISO-27001-2022 A.8.20, CIS VMware ESXi Benchmark |
|
|
244
|
+
| `NEW-CTRL-069` | ECOSYSTEM-PACKAGE-TEMPORAL-TRUST-DRIFT-DETECTION | `MAL-2026-RUBYGEMS-BUFFERZONECORP-SLEEPER` | NIST-800-218 SSDF PW.4, EU CRA Annex I §1(2)(b), SLSA Build L3 |
|
|
245
|
+
| `NEW-CTRL-070` | TYPOSQUAT-INSTALL-TIME-NAME-CONFUSION-GUARD | `MAL-2025-PYPI-COLORAMA-SOLANA-STEALER` | NIST-800-218 SSDF PW.4, NIST-800-53 SI-7, EU CRA Annex I §1(2)(c) |
|
|
246
|
+
| `NEW-CTRL-071` | AI-DISCOVERY-CREDIT-IN-COMPLIANCE-EVIDENCE | `MAL-2025-AI-FOUND-FFMPEG-BIGSLEEP` + `CVE-2025-6965` + `CVE-2025-0133` + ZeroPath quartet | NIST AI RMF MEASURE 2.7, ISO/IEC 42001 §6.1.4 (records of AI use), EU AI Act Art.12 (record-keeping) |
|
|
231
247
|
|
|
232
248
|
When you cite a `NEW-CTRL-*` ID in a skill body, the lint reads the upstream `zeroday-lessons.json` entry as the authoritative source for the requirement text — do not paraphrase the description in the skill body, link to the ID instead.
|
|
233
249
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.6 — 2026-05-18
|
|
4
|
+
|
|
5
|
+
CVE catalog expansion (38 → 67 entries) covering threat classes the catalog previously did not address, plus a `doctor` undercount fix.
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
**29 new catalog entries** across the under-represented classes:
|
|
10
|
+
|
|
11
|
+
- **Browsers (4)** — Chrome V8 TAG-disclosed zero-day `CVE-2025-10585`, WebKit DarkSword chain `CVE-2025-14174` + `CVE-2025-43529`, Firefox SpiderMonkey Pwn2Own `CVE-2025-4919`.
|
|
12
|
+
- **Mobile OS (3)** — WebKit Glass Cage iOS chain `CVE-2025-24201`, ImageIO zero-click root `CVE-2025-43300`, Android POSIX-CPU-timer race `CVE-2025-38352`.
|
|
13
|
+
- **Identity providers (2)** — Entra ID cross-tenant Actor-token impersonation `CVE-2025-55241` (CVSS 10.0), Cisco Duo log credential disclosure `CVE-2025-21085`.
|
|
14
|
+
- **Database engines (3)** — PostgreSQL psql ACE `CVE-2025-1094` (BeyondTrust / Treasury breaches), Redis RediShell Lua UAF `CVE-2025-49844` (CVSS 10.0), MongoBleed memory disclosure `CVE-2025-14847`.
|
|
15
|
+
- **HTTP/2 (1)** — MadeYouReset stream-reset DoS `CVE-2025-8671` (Rapid Reset successor, 2.8M+ vulnerable instances).
|
|
16
|
+
- **AI model serving (4)** — vLLM heap-overflow RCE `CVE-2026-22778`, Ollama Bleeding Llama `CVE-2026-7482`, LangChain LangGrinch `CVE-2025-68664`, Big Sleep SQLite zero-day `CVE-2025-6965`.
|
|
17
|
+
- **VMware ESXi (3)** — `CVE-2025-22224` / `CVE-2025-22225` / `CVE-2025-22226` (VMSA-2025-0004, ransomware-active VM-escape chain).
|
|
18
|
+
- **Malicious packages (3)** — ultralytics XMRig `MAL-2024-PYPI-ULTRALYTICS-XMRIG` (60M-download AI library), RubyGems + Go sleeper `MAL-2026-RUBYGEMS-BUFFERZONECORP-SLEEPER`, PyPI colorama Solana stealer `MAL-2025-PYPI-COLORAMA-SOLANA-STEALER`.
|
|
19
|
+
- **AI-discovery anchors (6)** — XBOW Palo Alto GlobalProtect `CVE-2025-0133` (HackerOne #1 Q2 2025), ZeroPath cluster (`CVE-2025-59529` / `CVE-2025-55319` / `CVE-2025-53767` / `CVE-2025-10725`), Big Sleep FFmpeg + ImageMagick tranche `MAL-2025-AI-FOUND-FFMPEG-BIGSLEEP`.
|
|
20
|
+
|
|
21
|
+
Every entry carries the full RWEP factor set, named verification sources, vendor advisory references, and a matching `data/zeroday-lessons.json` lesson. AI-discovered rate climbs 5/38 (0.132) → 12/67 (0.179), clearing the next ladder rung toward the Hard Rule #7 target of 0.40.
|
|
22
|
+
|
|
23
|
+
**16 new control requirements** mint `NEW-CTRL-056` through `NEW-CTRL-071`, named in `AGENTS.md` with the surfacing zero-day and gap-closed framework controls. Coverage spans mobile MDM SLA enforcement, browser managed-update no-deferral, cloud-control-plane cross-tenant claim validation, sensitive-data-in-logs lint, database server-side scripting default-deny, in-memory datastore memory-disclosure exposure audit, HTTP/2 stream-reset accounting, multimodal inference decoder isolation, LLM-output deserialization trust zone, AI-model-server default auth, agentic-IDE host-execution sandbox, AI-platform control-plane RBAC overlay, hypervisor tenancy assumption, ecosystem-package temporal trust drift, typosquat install-time guard, and AI-discovery credit in compliance evidence.
|
|
24
|
+
|
|
25
|
+
**ATT&CK + ATLAS catalogs extended** to back the new entries: 8 new ATT&CK techniques (T1005, T1189, T1496, T1498, T1499.001, T1499.002, T1539, T1657) and 3 new ATLAS TTPs (AML.T0007 Discover ML Artifacts, AML.T0011 User Execution, AML.T0047 LLM Meta Prompt Extraction).
|
|
26
|
+
|
|
27
|
+
### Bugs
|
|
28
|
+
|
|
29
|
+
**`exceptd doctor` no longer undercounts the catalog.** The prior implementation parsed `validate-cves` text output, which only counts `CVE-*` prefixes — `MAL-*` (malicious-package) entries were silently dropped from the total. An operator reading `CVE catalog: 34 entries` on a 38-entry catalog would conclude that the Shai-Hulud / TanStack worm intelligence had been removed when it was present all along. The check now reads `data/cve-catalog.json` directly and reports the combined total with the per-prefix breakdown: `CVE catalog: 67 entries (60 CVE + 7 MAL), drift 0`. The `validate-cves` text output gains a clarifying suffix noting that the count is CVE-IDs queued for NVD validation and that the combined catalog total lives under `exceptd doctor`.
|
|
30
|
+
|
|
31
|
+
## 0.13.5 — 2026-05-18
|
|
32
|
+
|
|
33
|
+
Three new playbooks, two cross-cutting CLI behaviours, and a deterministic schema gate on `active_exploitation` vocabulary.
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
**Three new playbooks bring the canonical set to 23.**
|
|
38
|
+
|
|
39
|
+
- **`post-quantum-migration`** — the operational migration programme (distinct from `crypto`, which is handshake-level). Covers per-asset cryptographic register, vendor-SLA tracking, regulator-deadline orchestration (CNSA 2.0, OMB M-23-02, NIS2 Art.21(2)(h), DORA Art.9, EU CRA Annex I, BSI TR-02102, ACSC ISM-1546), and HNDL exposure-window analysis. Nine indicators including `no-cryptographic-asset-register`, `hsm-firmware-no-pqc` (Thales/Entrust/CloudHSM migration blocker), `long-retention-classical-only-asset`, and `embedded-tls-stack-classical-only`. 13 framework-gap mappings (NIST SC-12/SC-13, ISO A.8.24/A.8.25, PCI 3.6/4.2.1, NIS2/DORA/EU CRA, UK CAF, AU ISM, MAS TRM, JP NISC, HIPAA). Feeds into `crypto` + `framework` + `sbom`.
|
|
40
|
+
|
|
41
|
+
- **`ai-discovered-cve-triage`** — operator-side response to AI-discovered CVE arrival. Anchors on CVE-2026-31431 (Copy Fail, Theori+Xint), CVE-2026-46300 (Fragnesia, Zellic AI-agentic), CVE-2026-42945 (NGINX Rift, depthfirst — first publicly-attributed AI-discovered nginx CVE), the GTIG 41% AI-zero-day statistic, and Hard Rule #7. Seven indicators including `ai-discovery-attribution-band-c-unverified` (don't apply +15 ai_factor on unverified claims), `ai-discovery-feed-coverage-incomplete` (operator pipeline misses Theori/depthfirst/Zellic/GTIG/Project Zero AI sources), and `asset-unpatched-past-rwep-sla` (RWEP-derived SLA: 4h ≥ 90, 24h 75–89, 72h 60–74). Feeds into `framework` + `kernel` + `sbom` + `runtime`.
|
|
42
|
+
|
|
43
|
+
- **`supply-chain-recovery`** — post-compromise recovery workflow (distinct from `sbom`, which is pre-incident hygiene). Anchors on Shai-Hulud (Sep 2025 / Nov 2025 / May 2026 waves), MAL-2026-SHAI-HULUD-OSS (TeamPCP open-sourced 2026-05-12), MAL-2026-TANSTACK-MINI (42 `@tanstack/*` packages), MAL-2026-NODE-IPC-STEALER, and CVE-2026-45321. Encodes NEW-CTRL-050 (exhaustive maintainer-credential rotation), NEW-CTRL-051 (install-window audit), NEW-CTRL-052 (AI-assistant config exfil as first-class — `~/.cursor`, `~/.codeium`, `~/.claude`). Eight indicators including `ai-assistant-config-mutated` (Shai-Hulud startup-hook persistence), `outbound-exfil-during-window`, `operator-published-package-republish` (downstream notification mandatory), `long-lived-token-in-compromised-ci-log`. Feeds into `cred-stores` + `idp-incident-response` + `sbom` + `mcp` + `framework`.
|
|
44
|
+
|
|
45
|
+
**`exceptd watchlist --org-scan --output-format markdown`.** Adds GitHub-flavored markdown table output for PR / issue / advisory body consumption. Accepted values: `json` | `markdown` | `human` (default). The legacy `--json` shorthand remains accepted (equivalent to `--output-format json`). Invalid values exit non-zero with the accepted-set in the error envelope.
|
|
46
|
+
|
|
47
|
+
**`exceptd doctor --ai-config --fix` now repairs Windows ACLs.** The POSIX path applied `chmod 0600`; on Windows the audit reported the gap as "manual review." The Windows path now invokes `icacls /inheritance:r /grant:r` to restrict to the current user + SYSTEM + Administrators. The audit check itself (without `--fix`) parses `icacls <path>` and reports any extra principals.
|
|
48
|
+
|
|
49
|
+
**Skill chain: MCP findings inside a CI runner escalate to `cicd-pipeline-compromise`.** New deterministic indicator `mcp-server-invoked-from-ci-pipeline` keys on `GITHUB_ACTIONS` / `GITLAB_CI` / `BUILDKITE` / `JENKINS_URL` / `CIRCLECI` / `RUNNER_OS` env vars and known runner workdirs (`/_work/`, `/builds/`, `/var/jenkins_home/workspace/`, `/var/lib/buildkite-agent/builds/`). When paired with any other high-confidence MCP signal, the finding feeds into `cicd-pipeline-compromise` for OIDC / signing-key / publish-channel scope handling. Without this arc, MCP findings in CI received local-dev close-out only, under-counting publish-channel blast radius.
|
|
50
|
+
|
|
51
|
+
### Bugs
|
|
52
|
+
|
|
53
|
+
**`cve-catalog.schema.json` `active_exploitation` enum now matches `_meta.active_exploitation_vocabulary`.** The schema enumerated four values (`confirmed` / `suspected` / `none` / `unknown`); the meta vocabulary listed five (adding `theoretical`). Catalog entries written against the meta vocabulary that used `theoretical` were silently rejected at validation time. Schema now lists five values; `validate-cve-catalog.js` adds a cross-check that fails the gate if the two surfaces ever drift again.
|
|
54
|
+
|
|
55
|
+
### Internal
|
|
56
|
+
|
|
57
|
+
- Test count baseline updated for the +3 playbook delta and the new `--output-format` test cases.
|
|
58
|
+
- `validate-cve-catalog.js` schema-vs-meta enum cross-check is a hard predeploy gate.
|
|
59
|
+
- All 23 playbooks pass `validate-playbooks` and `lint-skills` warning-free.
|
|
60
|
+
|
|
3
61
|
## 0.13.4 — 2026-05-18
|
|
4
62
|
|
|
5
63
|
Warning-cleanup pass + catalog hygiene + docs surfacing. The post-v0.13.3 state had ~43 skill lint warnings and 20 cosmetic playbook warnings that operators saw on every predeploy run; this release drives both to zero. README and AGENTS catch up with the v0.13.0 → v0.13.3 operator surface.
|
package/bin/exceptd.js
CHANGED
|
@@ -251,6 +251,63 @@ const RENAMED_VERBS_HINT = {
|
|
|
251
251
|
"build-indexes": "refresh --indexes-only",
|
|
252
252
|
};
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* v0.13.5: Windows ACL audit helper for `doctor --ai-config`. Replaces
|
|
256
|
+
* the v0.13.3 "manual review" placeholder with a real check.
|
|
257
|
+
*
|
|
258
|
+
* Runs `icacls <path>` and parses the output for any principal beyond
|
|
259
|
+
* the current user. Anything other than the running USERNAME, NT
|
|
260
|
+
* AUTHORITY\SYSTEM, and BUILTIN\Administrators on the ACL counts as
|
|
261
|
+
* "broader than user-only" — typical offenders are inherited entries
|
|
262
|
+
* for BUILTIN\Users, Authenticated Users, or Everyone.
|
|
263
|
+
*
|
|
264
|
+
* Returns { ok: boolean, extraPrincipals: string[], error?: string }.
|
|
265
|
+
* On non-Windows hosts (defensive — only invoked from the win32 branch
|
|
266
|
+
* in cmdDoctor anyway), returns { ok: true, extraPrincipals: [] }.
|
|
267
|
+
*/
|
|
268
|
+
function checkWindowsAcl(targetPath) {
|
|
269
|
+
if (process.platform !== 'win32') return { ok: true, extraPrincipals: [] };
|
|
270
|
+
const childProc = require('child_process');
|
|
271
|
+
const user = (process.env.USERNAME || '').toLowerCase();
|
|
272
|
+
// Principals that are EXPECTED on every Windows ACL and don't count
|
|
273
|
+
// as "broader than user-only" — admins legitimately need access for
|
|
274
|
+
// system maintenance; SYSTEM is required for backup/restore.
|
|
275
|
+
const ALLOWED_PRINCIPAL_SUFFIXES = [
|
|
276
|
+
`\\${user}`,
|
|
277
|
+
'nt authority\\system',
|
|
278
|
+
'builtin\\administrators',
|
|
279
|
+
'administrators',
|
|
280
|
+
];
|
|
281
|
+
let stdout;
|
|
282
|
+
try {
|
|
283
|
+
stdout = childProc.execFileSync('icacls', [targetPath], {
|
|
284
|
+
encoding: 'utf8',
|
|
285
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
286
|
+
timeout: 5000,
|
|
287
|
+
});
|
|
288
|
+
} catch (e) {
|
|
289
|
+
return { ok: false, extraPrincipals: [], error: (e && e.message) || String(e) };
|
|
290
|
+
}
|
|
291
|
+
const extraPrincipals = [];
|
|
292
|
+
// icacls output format: each principal on its own line, prefixed by
|
|
293
|
+
// whitespace and ending with permission bits in parens. Lines for
|
|
294
|
+
// the file itself (target path) and the "Successfully processed"
|
|
295
|
+
// footer are skipped.
|
|
296
|
+
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
297
|
+
const line = rawLine.trim();
|
|
298
|
+
if (!line) continue;
|
|
299
|
+
if (line.toLowerCase().startsWith('successfully processed')) continue;
|
|
300
|
+
// The first line is the path; principal lines start with NT AUTHORITY,
|
|
301
|
+
// BUILTIN, or a domain\user. Match `name:(perms)` shape.
|
|
302
|
+
const m = line.match(/^([^:()]+?):\(/);
|
|
303
|
+
if (!m) continue;
|
|
304
|
+
const principal = m[1].trim().toLowerCase();
|
|
305
|
+
const isAllowed = ALLOWED_PRINCIPAL_SUFFIXES.some((suffix) => principal.endsWith(suffix));
|
|
306
|
+
if (!isAllowed) extraPrincipals.push(m[1].trim());
|
|
307
|
+
}
|
|
308
|
+
return { ok: extraPrincipals.length === 0, extraPrincipals };
|
|
309
|
+
}
|
|
310
|
+
|
|
254
311
|
function readPkgVersion() {
|
|
255
312
|
try {
|
|
256
313
|
return JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf8")).version;
|
|
@@ -5338,19 +5395,36 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5338
5395
|
if (runCves) {
|
|
5339
5396
|
try {
|
|
5340
5397
|
const orchPath = path.join(PKG_ROOT, "orchestrator", "index.js");
|
|
5341
|
-
// validate-cves doesn't emit JSON; parse text for
|
|
5398
|
+
// validate-cves doesn't emit JSON; parse text for drift signal.
|
|
5342
5399
|
const res = spawnSync(process.execPath, [orchPath, "validate-cves", "--offline"], {
|
|
5343
5400
|
encoding: "utf8",
|
|
5344
5401
|
cwd: PKG_ROOT,
|
|
5345
5402
|
timeout: 30000,
|
|
5346
5403
|
});
|
|
5347
5404
|
const text = (res.stdout || "") + (res.stderr || "");
|
|
5348
|
-
const totalMatch = text.match(/(\d+)\s+CVEs?\s+in\s+catalog/i);
|
|
5349
5405
|
const driftMatch = text.match(/drift[:\s]+(\d+)/i);
|
|
5350
5406
|
const ok = res.status === 0;
|
|
5407
|
+
// v0.13.6: total comes from the catalog file directly. The
|
|
5408
|
+
// validate-cves text-scrape only ever counted CVE-* prefixes, so
|
|
5409
|
+
// MAL-* (malicious package) entries silently dropped from the
|
|
5410
|
+
// doctor report — operators reading "34 entries" assumed the
|
|
5411
|
+
// Shai-Hulud / TanStack worm intel had been removed when it was
|
|
5412
|
+
// present all along. Read the catalog and report both totals.
|
|
5413
|
+
let total = null;
|
|
5414
|
+
let cve_count = null;
|
|
5415
|
+
let mal_count = null;
|
|
5416
|
+
try {
|
|
5417
|
+
const catalog = require(path.join(PKG_ROOT, "data", "cve-catalog.json"));
|
|
5418
|
+
const keys = Object.keys(catalog).filter((k) => !k.startsWith("_"));
|
|
5419
|
+
cve_count = keys.filter((k) => k.startsWith("CVE-")).length;
|
|
5420
|
+
mal_count = keys.filter((k) => k.startsWith("MAL-")).length;
|
|
5421
|
+
total = keys.length;
|
|
5422
|
+
} catch { /* fall through with nulls */ }
|
|
5351
5423
|
checks.cves = {
|
|
5352
5424
|
ok,
|
|
5353
|
-
total
|
|
5425
|
+
total,
|
|
5426
|
+
cve_count,
|
|
5427
|
+
mal_count,
|
|
5354
5428
|
drift: driftMatch ? Number(driftMatch[1]) : 0,
|
|
5355
5429
|
...(ok ? {} : { exit_code: res.status, raw: text.slice(0, 500) }),
|
|
5356
5430
|
};
|
|
@@ -5510,15 +5584,23 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5510
5584
|
let st;
|
|
5511
5585
|
try { st = fs.statSync(childAbs); } catch { continue; }
|
|
5512
5586
|
if (process.platform === 'win32') {
|
|
5513
|
-
//
|
|
5514
|
-
//
|
|
5515
|
-
//
|
|
5587
|
+
// v0.13.5: real ACL check via icacls. Replaces the v0.13.3
|
|
5588
|
+
// "manual review" info-level placeholder. Confirms only the
|
|
5589
|
+
// current-user SID has any read entry. Anything else (Users,
|
|
5590
|
+
// Everyone, Authenticated Users, BUILTIN\Users, etc.) on the
|
|
5591
|
+
// ACL counts as "broader than user-only" and surfaces as a
|
|
5592
|
+
// warn finding. The `--fix` path applies `icacls /inheritance:r
|
|
5593
|
+
// /grant:r <USER>:F` to strip inherited entries.
|
|
5594
|
+
const aclCheck = checkWindowsAcl(childAbs);
|
|
5595
|
+
if (aclCheck.ok) continue;
|
|
5516
5596
|
findings.push({
|
|
5517
5597
|
path: `${displayRoot}/${childRel}`,
|
|
5518
5598
|
mode: null,
|
|
5519
|
-
severity: '
|
|
5520
|
-
issue: '
|
|
5521
|
-
|
|
5599
|
+
severity: 'warn',
|
|
5600
|
+
issue: 'broader_than_user_only_acl',
|
|
5601
|
+
acl_extra_principals: aclCheck.extraPrincipals,
|
|
5602
|
+
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`,
|
|
5603
|
+
fix_command: ['icacls', childAbs, '/inheritance:r', '/grant:r', `${process.env.USERNAME}:F`],
|
|
5522
5604
|
});
|
|
5523
5605
|
continue;
|
|
5524
5606
|
}
|
|
@@ -5530,6 +5612,8 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5530
5612
|
severity: 'warn',
|
|
5531
5613
|
issue: 'group_or_other_readable',
|
|
5532
5614
|
hint: `chmod 600 '${childAbs}' # NEW-CTRL-050: AI-assistant configs holding MCP tokens / API keys must be 0600 to defeat unprivileged exfil`,
|
|
5615
|
+
fix_chmod: 0o600,
|
|
5616
|
+
fix_abs_path: childAbs,
|
|
5533
5617
|
});
|
|
5534
5618
|
}
|
|
5535
5619
|
}
|
|
@@ -5543,9 +5627,49 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5543
5627
|
}
|
|
5544
5628
|
}
|
|
5545
5629
|
const errorFindings = findings.filter((f) => f.severity === 'warn');
|
|
5630
|
+
|
|
5631
|
+
// v0.13.5: --fix path. When `doctor --ai-config --fix` is invoked
|
|
5632
|
+
// AND warn-severity findings exist, apply the per-finding fix
|
|
5633
|
+
// command (chmod 600 on POSIX; icacls /inheritance:r /grant:r on
|
|
5634
|
+
// Windows). The fix attempt is recorded per-finding so the report
|
|
5635
|
+
// surfaces which fixes landed vs which failed.
|
|
5636
|
+
let fixesApplied = 0;
|
|
5637
|
+
let fixesFailed = 0;
|
|
5638
|
+
if (args.fix && errorFindings.length > 0) {
|
|
5639
|
+
const childProc = require('child_process');
|
|
5640
|
+
for (const f of errorFindings) {
|
|
5641
|
+
if (f.fix_chmod && f.fix_abs_path) {
|
|
5642
|
+
try {
|
|
5643
|
+
fs.chmodSync(f.fix_abs_path, f.fix_chmod);
|
|
5644
|
+
f.fix_status = 'chmod_applied';
|
|
5645
|
+
fixesApplied++;
|
|
5646
|
+
} catch (e) {
|
|
5647
|
+
f.fix_status = 'chmod_failed';
|
|
5648
|
+
f.fix_error = e.message;
|
|
5649
|
+
fixesFailed++;
|
|
5650
|
+
}
|
|
5651
|
+
continue;
|
|
5652
|
+
}
|
|
5653
|
+
if (f.fix_command) {
|
|
5654
|
+
try {
|
|
5655
|
+
childProc.execFileSync(f.fix_command[0], f.fix_command.slice(1), {
|
|
5656
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
5657
|
+
timeout: 5000,
|
|
5658
|
+
});
|
|
5659
|
+
f.fix_status = 'icacls_applied';
|
|
5660
|
+
fixesApplied++;
|
|
5661
|
+
} catch (e) {
|
|
5662
|
+
f.fix_status = 'icacls_failed';
|
|
5663
|
+
f.fix_error = (e && e.message) || String(e);
|
|
5664
|
+
fixesFailed++;
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
|
|
5546
5670
|
checks.ai_config = {
|
|
5547
|
-
ok: errorFindings.length === 0,
|
|
5548
|
-
severity: errorFindings.length > 0 ? 'warn' : 'info',
|
|
5671
|
+
ok: errorFindings.length === 0 || (args.fix && fixesFailed === 0),
|
|
5672
|
+
severity: errorFindings.length > 0 && fixesFailed > 0 ? 'warn' : (errorFindings.length > 0 && !args.fix ? 'warn' : 'info'),
|
|
5549
5673
|
scanned_dirs: scannedDirs,
|
|
5550
5674
|
scanned_files: scannedFiles,
|
|
5551
5675
|
directories_inspected: AI_CONFIG_DIRS.map((d) => d.display),
|
|
@@ -5553,8 +5677,9 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5553
5677
|
findings,
|
|
5554
5678
|
platform: process.platform,
|
|
5555
5679
|
control_reference: 'NEW-CTRL-050 (MAL-2026-SHAI-HULUD-OSS lesson)',
|
|
5680
|
+
...(args.fix ? { fix_applied: fixesApplied, fix_failed: fixesFailed } : {}),
|
|
5556
5681
|
};
|
|
5557
|
-
if (errorFindings.length > 0) issues.push('ai_config');
|
|
5682
|
+
if (errorFindings.length > 0 && (!args.fix || fixesFailed > 0)) issues.push('ai_config');
|
|
5558
5683
|
}
|
|
5559
5684
|
|
|
5560
5685
|
// Walk every check and split: errors (severity error/missing/fail) vs warnings
|
|
@@ -5685,11 +5810,14 @@ function cmdDoctor(runner, args, runOpts, pretty) {
|
|
|
5685
5810
|
? `skill currency: all green (${c.total_skills ?? "?"} skills)`
|
|
5686
5811
|
: `skill currency: ${c.stale_skills?.length || "?"} stale, ${c.critical_count ?? 0} critical`
|
|
5687
5812
|
);
|
|
5688
|
-
mark(checks.cves, c =>
|
|
5689
|
-
c.ok
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5813
|
+
mark(checks.cves, c => {
|
|
5814
|
+
if (!c.ok) return `CVE catalog FAILED (exit=${c.exit_code ?? "?"})`;
|
|
5815
|
+
const total = c.total ?? "?";
|
|
5816
|
+
const breakdown = (c.cve_count != null && c.mal_count != null)
|
|
5817
|
+
? ` (${c.cve_count} CVE + ${c.mal_count} MAL)`
|
|
5818
|
+
: "";
|
|
5819
|
+
return `CVE catalog: ${total} entries${breakdown}, drift ${c.drift ?? 0}`;
|
|
5820
|
+
});
|
|
5693
5821
|
mark(checks.rfcs, c =>
|
|
5694
5822
|
c.ok
|
|
5695
5823
|
? `RFC catalog: ${c.total ?? "?"} entries, drift ${c.drift ?? 0}`
|
package/data/_indexes/_meta.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": "1.1.0",
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-18T07:02:32.618Z",
|
|
4
4
|
"generator": "scripts/build-indexes.js",
|
|
5
5
|
"source_count": 54,
|
|
6
6
|
"source_hashes": {
|
|
7
|
-
"manifest.json": "
|
|
8
|
-
"data/atlas-ttps.json": "
|
|
9
|
-
"data/attack-techniques.json": "
|
|
10
|
-
"data/cve-catalog.json": "
|
|
7
|
+
"manifest.json": "fac154a4d63a13c1289d3498abab8d28433c30b79393e63f49c6a2ce21f7922e",
|
|
8
|
+
"data/atlas-ttps.json": "c2aee9c70ec24cf48f1ea4daf170aa6e7b93292888239c46a8ec9e522ee32119",
|
|
9
|
+
"data/attack-techniques.json": "29cd5690040c7153dbf293b7e3a99b72fc897b0495478e369f7ce7004b8d64f4",
|
|
10
|
+
"data/cve-catalog.json": "b3731361d298483648264215fd8dbfca36d0f4e2ead4aebf7c49718e12038e1f",
|
|
11
11
|
"data/cwe-catalog.json": "4a0036f9ec17af29e0df111ac77b94f8be6a52742bfd89ff3583096d23b75e35",
|
|
12
12
|
"data/d3fend-catalog.json": "a1fc2827ceb344669e148d55197dbf1b0e5b20bcc618e90517639c17d67ee82d",
|
|
13
13
|
"data/dlp-controls.json": "d2406c482dddd30e49203879999dc4b3a7fd4d0494d6a61d86b91ee76415df19",
|
|
14
14
|
"data/exploit-availability.json": "ec2656f0d9a893610e27b43eb6035fe9b18e057c9f6dfaac7e7d4959bbcbb795",
|
|
15
|
-
"data/framework-control-gaps.json": "
|
|
15
|
+
"data/framework-control-gaps.json": "d49c75de55e6e1dabec46e6e975619489a2093b0be53c2a0b654e5c1826fbe46",
|
|
16
16
|
"data/global-frameworks.json": "9ba563a85f7f8d6c3c957de64945e20925a89d0ed6ea6fc561cf093811acf558",
|
|
17
17
|
"data/rfc-references.json": "e253a548c8a829d178d5aea601e268724b85c936ccbfa51c2e5d80c5f8efe2b0",
|
|
18
|
-
"data/zeroday-lessons.json": "
|
|
18
|
+
"data/zeroday-lessons.json": "bb3fec080f649a5968f8d0c6d69ca4d32fb120de0f7d07b1f5058184d4d3ff3a",
|
|
19
19
|
"skills/kernel-lpe-triage/skill.md": "08b3e9815ba481c57c80f5fc0ccbf5bb7cbb41f570c235ba6ff9596b8c07354d",
|
|
20
20
|
"skills/ai-attack-surface/skill.md": "d1361c53c8360999e1ec6a403bcbfaa53d0afc11689e8781d26081196dd079d4",
|
|
21
21
|
"skills/mcp-agent-trust/skill.md": "19a6b54375808e59143070011328d8c936836845bca4a484108738bbef290694",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"dlp_refs": 0
|
|
73
73
|
},
|
|
74
74
|
"trigger_table_entries": 538,
|
|
75
|
-
"chains_cve_entries":
|
|
75
|
+
"chains_cve_entries": 59,
|
|
76
76
|
"chains_cwe_entries": 55,
|
|
77
77
|
"jurisdictions_indexed": 29,
|
|
78
78
|
"handoff_dag_nodes": 42,
|
|
@@ -5,6 +5,22 @@
|
|
|
5
5
|
"event_count": 54
|
|
6
6
|
},
|
|
7
7
|
"events": [
|
|
8
|
+
{
|
|
9
|
+
"date": "2026-05-18",
|
|
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": 67
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"date": "2026-05-18",
|
|
18
|
+
"type": "catalog_update",
|
|
19
|
+
"artifact": "data/zeroday-lessons.json",
|
|
20
|
+
"path": "data/zeroday-lessons.json",
|
|
21
|
+
"schema_version": "1.1.0",
|
|
22
|
+
"entry_count": 67
|
|
23
|
+
},
|
|
8
24
|
{
|
|
9
25
|
"date": "2026-05-15",
|
|
10
26
|
"type": "skill_review",
|
|
@@ -39,7 +55,7 @@
|
|
|
39
55
|
"artifact": "data/atlas-ttps.json",
|
|
40
56
|
"path": "data/atlas-ttps.json",
|
|
41
57
|
"schema_version": "1.0.0",
|
|
42
|
-
"entry_count":
|
|
58
|
+
"entry_count": 33
|
|
43
59
|
},
|
|
44
60
|
{
|
|
45
61
|
"date": "2026-05-15",
|
|
@@ -47,7 +63,7 @@
|
|
|
47
63
|
"artifact": "data/attack-techniques.json",
|
|
48
64
|
"path": "data/attack-techniques.json",
|
|
49
65
|
"schema_version": "1.0.0",
|
|
50
|
-
"entry_count":
|
|
66
|
+
"entry_count": 106
|
|
51
67
|
},
|
|
52
68
|
{
|
|
53
69
|
"date": "2026-05-15",
|
|
@@ -81,14 +97,6 @@
|
|
|
81
97
|
"schema_version": "1.0.0",
|
|
82
98
|
"entry_count": 41
|
|
83
99
|
},
|
|
84
|
-
{
|
|
85
|
-
"date": "2026-05-15",
|
|
86
|
-
"type": "catalog_update",
|
|
87
|
-
"artifact": "data/zeroday-lessons.json",
|
|
88
|
-
"path": "data/zeroday-lessons.json",
|
|
89
|
-
"schema_version": "1.1.0",
|
|
90
|
-
"entry_count": 38
|
|
91
|
-
},
|
|
92
100
|
{
|
|
93
101
|
"date": "2026-05-15",
|
|
94
102
|
"type": "manifest_review",
|
|
@@ -96,14 +104,6 @@
|
|
|
96
104
|
"path": "manifest.json",
|
|
97
105
|
"note": "manifest threat_review_date — 42 skills, 11 catalogs"
|
|
98
106
|
},
|
|
99
|
-
{
|
|
100
|
-
"date": "2026-05-13",
|
|
101
|
-
"type": "catalog_update",
|
|
102
|
-
"artifact": "data/cve-catalog.json",
|
|
103
|
-
"path": "data/cve-catalog.json",
|
|
104
|
-
"schema_version": "1.0.0",
|
|
105
|
-
"entry_count": 38
|
|
106
|
-
},
|
|
107
107
|
{
|
|
108
108
|
"date": "2026-05-13",
|
|
109
109
|
"type": "catalog_update",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"rebuild_after_days": 365,
|
|
19
19
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
20
20
|
},
|
|
21
|
-
"entry_count":
|
|
21
|
+
"entry_count": 33,
|
|
22
22
|
"sample_keys": [
|
|
23
23
|
"AML.T0001",
|
|
24
24
|
"AML.T0040",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"rebuild_after_days": 365,
|
|
41
41
|
"note": "Catalog must be rebuilt against the upstream ATT&CK release whenever MITRE publishes a new version. AGENTS.md external-data version-pinning rule requires the bump to be intentional, not silent. ATT&CK ships semi-annually (April + October); audit on each release for tactic moves, technique splits, and new Detection Strategies."
|
|
42
42
|
},
|
|
43
|
-
"entry_count":
|
|
43
|
+
"entry_count": 106,
|
|
44
44
|
"sample_keys": [
|
|
45
45
|
"T0001",
|
|
46
46
|
"T0017",
|
|
@@ -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-18",
|
|
57
57
|
"tlp": "CLEAR",
|
|
58
58
|
"source_confidence_default": "A1",
|
|
59
59
|
"freshness_policy": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"rebuild_after_days": 365,
|
|
63
63
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
64
64
|
},
|
|
65
|
-
"entry_count":
|
|
65
|
+
"entry_count": 67,
|
|
66
66
|
"sample_keys": [
|
|
67
67
|
"CVE-2025-53773",
|
|
68
68
|
"CVE-2026-30615",
|
|
@@ -229,7 +229,7 @@
|
|
|
229
229
|
"path": "data/zeroday-lessons.json",
|
|
230
230
|
"purpose": "Distilled lessons from notable zero-days and campaigns (SesameOp, Copy Fail, Dirty Frag, Copilot RCE, Windsurf MCP). Each entry: technique, distinguishing characteristic, what it means for the framework lag.",
|
|
231
231
|
"schema_version": "1.1.0",
|
|
232
|
-
"last_updated": "2026-05-
|
|
232
|
+
"last_updated": "2026-05-18",
|
|
233
233
|
"tlp": "CLEAR",
|
|
234
234
|
"source_confidence_default": "B2",
|
|
235
235
|
"freshness_policy": {
|
|
@@ -238,7 +238,7 @@
|
|
|
238
238
|
"rebuild_after_days": 365,
|
|
239
239
|
"note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
|
|
240
240
|
},
|
|
241
|
-
"entry_count":
|
|
241
|
+
"entry_count": 67,
|
|
242
242
|
"sample_keys": [
|
|
243
243
|
"CVE-2026-31431",
|
|
244
244
|
"CVE-2025-53773",
|