@blamejs/exceptd-skills 0.13.1 → 0.13.3

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/bin/exceptd.js +140 -7
  3. package/data/_indexes/_meta.json +28 -28
  4. package/data/_indexes/activity-feed.json +3 -3
  5. package/data/_indexes/catalog-summaries.json +3 -3
  6. package/data/_indexes/chains.json +1897 -88
  7. package/data/_indexes/frequency.json +20 -0
  8. package/data/_indexes/section-offsets.json +574 -574
  9. package/data/_indexes/token-budget.json +97 -97
  10. package/data/atlas-ttps.json +2 -0
  11. package/data/attack-techniques.json +24 -3
  12. package/data/cve-catalog.json +96 -29
  13. package/data/cwe-catalog.json +20 -3
  14. package/data/framework-control-gaps.json +700 -1
  15. package/data/zeroday-lessons.json +889 -0
  16. package/lib/lint-skills.js +54 -1
  17. package/lib/source-advisories.js +26 -0
  18. package/manifest.json +62 -62
  19. package/orchestrator/index.js +155 -3
  20. package/package.json +1 -1
  21. package/sbom.cdx.json +50 -39
  22. package/scripts/check-test-count.js +146 -0
  23. package/scripts/predeploy.js +16 -0
  24. package/skills/age-gates-child-safety/skill.md +1 -0
  25. package/skills/ai-risk-management/skill.md +1 -0
  26. package/skills/api-security/skill.md +14 -4
  27. package/skills/cloud-iam-incident/skill.md +1 -1
  28. package/skills/defensive-countermeasure-mapping/skill.md +1 -0
  29. package/skills/email-security-anti-phishing/skill.md +15 -4
  30. package/skills/fuzz-testing-strategy/skill.md +1 -0
  31. package/skills/mlops-security/skill.md +1 -0
  32. package/skills/ot-ics-security/skill.md +1 -0
  33. package/skills/researcher/skill.md +1 -0
  34. package/skills/sector-energy/skill.md +1 -0
  35. package/skills/sector-federal-government/skill.md +1 -0
  36. package/skills/sector-telecom/skill.md +1 -0
  37. package/skills/skill-update-loop/skill.md +1 -0
  38. package/skills/threat-model-currency/skill.md +1 -0
  39. package/skills/threat-modeling-methodology/skill.md +1 -0
  40. package/skills/webapp-security/skill.md +1 -0
  41. package/skills/zeroday-gap-learn/skill.md +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,78 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.3 — 2026-05-18
4
+
5
+ Audit close-out continuation: the items the prior pass marked for follow-up. Workflow hardening, lint enforcement promoted from warning to hard error, two new operator-facing health checks for the Shai-Hulud lesson controls, and 4 more primary-source pollers covering kernel.org / oss-security / JFrog / CISA.
6
+
7
+ ### Security
8
+
9
+ **`refresh.yml` split into two jobs — `refresh-data` (no write credentials) + `open-pr` (contents:write + pull-requests:write + issues:write scoped to PR creation only).** Pre-split a single `refresh` job carried write capability against the repo throughout the long-running data-parse + prefetch + apply + predeploy sequence; a compromise of any of those steps had repo-write access during the whole run. The new shape scopes write capability to the few-second PR-creation window. Data mutations flow between jobs via an upload-artifact / download-artifact bundle. The `refresh-data` checkout now uses `persist-credentials: false`.
10
+
11
+ **`lib/lint-skills.js` Hard Rule #1 body-scan flipped from warning to hard error.** v0.13.2 introduced the body-scan as a warning while the 2 pre-existing violations were triaged. Both are now resolved (`CVE-2024-21762` landed in the catalog with full Hard Rule #1 fields; the placeholder `CVE-2026-21370` reference was removed from `cloud-iam-incident`). The body-scan now errors when a skill cites a CVE not in the catalog. Draft references continue to surface as warnings.
12
+
13
+ ### Features
14
+
15
+ **`exceptd doctor --ai-config` audits AI-assistant config-file permissions.** Implements NEW-CTRL-050 from the MAL-2026-SHAI-HULUD-OSS zeroday-lessons entry. Walks `~/.claude`, `~/.cursor`, `~/.codeium`, `~/.aider`, `~/.continue` for sensitive files (`settings.json`, `mcp.json`, `*.mcp_config.json`, `api_key*`, `*.token`, `*.credentials`) and reports any not at mode 0600 on POSIX. On Windows the mode bits aren't load-bearing; each sensitive file is flagged with an info-level "manual ACL review" note. Opt-in via `--ai-config`; doesn't run as part of the default no-flag doctor pass.
16
+
17
+ **`exceptd watchlist --org-scan` probes GitHub for threat-actor repo naming patterns.** Implements NEW-CTRL-052 from the MAL-2026-SHAI-HULUD-OSS zeroday-lessons entry. Queries the GitHub Search API for repos matching the canonical Shai-Hulud / TeamPCP patterns ("A Gift From TeamPCP", "Shai-Hulud", "TeamPCP") scoped to `--org <login>`. Custom patterns via repeatable `--pattern <s>`. Set `GITHUB_TOKEN` env var for private-repo coverage and higher rate limit; without it, public-repo search only.
18
+
19
+ **4 more primary-source advisory pollers.** `lib/source-advisories.js` `FEEDS` grew 4 → 8:
20
+ - `kernel-org` — torvalds/linux master commits atom feed. Catches the CVE-2026-46333 / ssh-keysign-pwn class at T+0, the moment the upstream fix lands. The v0.13.1 post-mortem identified this as the exact venue we missed.
21
+ - `oss-security` — openwall.com `oss-security` mailing list atom feed. Coordinated-disclosure venue; many distro advisories announce CVEs here days before NVD enrichment.
22
+ - `jfrog` — JFrog SecOps research blog feed. npm / PyPI / Maven supply-chain disclosures with CVE assignments (TanStack / Mini Shai-Hulud class).
23
+ - `cisa-current` — CISA cybersecurity advisories feed (federal-vendor coordinated disclosures, separate from KEV which captures only exploited-in-the-wild items).
24
+
25
+ ### Bugs
26
+
27
+ **`CVE-2024-21762` (Fortinet FortiOS SSL-VPN preauth RCE) added to catalog.** Was cited in skill prose without a backing catalog entry — surfaced by the v0.13.2 Hard Rule #1 body-scan. Full Hard Rule #1 fields (CVSS 9.8, CISA KEV 2024-02-09, public PoC, confirmed mass exploitation across multiple APT clusters, FortiOS patch versions 7.6.2 / 7.4.7 / 7.2.11 / 7.0.17 / 6.4.16). RWEP 85. Includes the 2025-04 follow-up advisory documenting symlink persistence that survives firmware patching.
28
+
29
+ **`CVE-2026-21370` placeholder reference removed from `skills/cloud-iam-incident/skill.md`.** No record of CVE-2026-21370 in any source; was a class-marker parenthetical for the Azure managed-identity token-replay attack class. Rewritten as "design-class issue, not a single CVE" so the prose still accurately describes the IMDS-token-theft pattern without inventing threat intel.
30
+
31
+ **12 framework-gap forward-orphan references closed.** Each pre-existing orphan got a real gap entry with theater_test per Hard Rule #6: `CIS-Kubernetes-Benchmark-4.2.13`, `CIS-Kubernetes-Benchmark-5.3`, `CIS-Controls-v8-Control6`, `ISO-27001-2022-A.5.15`, `ISO-27001-2022-A.8.13`, `NIST-800-53-IA-2`, `NIST-AI-RMF-MEASURE-2.7`, `OWASP-ML-Top-10-2023-ML06`, `NIS2-Art21-network-security`, `NIS2-Art21-business-continuity`, `PCI-DSS-4.0-5.1`, `AU-ISM-1808`. Gap catalog 130 → 142 entries; orphan count for `framework-control-gaps.json` is now 0.
32
+
33
+ **2 empty-`data_deps` skills fixed.** `api-security` and `email-security-anti-phishing` previously had empty `data_deps` because the bodies referenced no catalog file by name. Each now carries 6 catalog references (atlas-ttps, attack-techniques, cwe-catalog / dlp-controls, d3fend-catalog, framework-control-gaps, rfc-references) threaded through the body in 4 new prose passages each. Every cited ID resolves to a real entry in its respective catalog. `last_threat_review` bumped to 2026-05-18.
34
+
35
+ ### Internal
36
+
37
+ - 8 new tests in `tests/v0_13_3-fixes.test.js` covering all 5 phases.
38
+ - Test-count baseline refreshed to match the new test surface.
39
+ - ADVISORIES_SOURCE test-fixture extended to include the 4 new feeds.
40
+ - `tests/source-advisories.test.js` `FEEDS: exactly N feeds` pin updated 4 → 8.
41
+
42
+ ## 0.13.2 — 2026-05-18
43
+
44
+ Audit close-out: the remaining v0.13 deferrals from the original 6-domain audit + the v0.13.1 post-mortem follow-ups. Patch-class — additive across CI hardening, lint enforcement, CLI UX, predeploy gates, catalog data cleanup, and skill metadata.
45
+
46
+ ### Security
47
+
48
+ **`release.yml` publish job split: `publish-npm` (id-token:write only) + `publish-github-release` (contents:write only).** Pre-v0.13.2 a single `publish` job carried BOTH permissions at once — a compromise of any step in that job (leaked NODE_AUTH_TOKEN, malicious dependency in the runner image, third-party action with elevated trust) had access to the npm provenance signing identity AND repo-write simultaneously. The new shape isolates each permission to the minimum surface that needs it. `publish-github-release` depends on `publish-npm` so the GitHub Release only fires when npm publish succeeded — releases pointing at a tag whose npm publish failed are operator-confusing.
49
+
50
+ ### Features
51
+
52
+ **`exceptd watchlist --alerts` 5 patterns now stable.** No change in v0.13.2; documenting that the v0.13.1 patterns are now operationally proven against the post-mortem seeds (`CVE-2026-46333` ssh-keysign-pwn surfacing under `kernel_lpe_with_poc`; `MAL-2026-SHAI-HULUD-OSS` under `supply_chain_family`).
53
+
54
+ **Flag-value did-you-mean across 6 sites.** `run --mode`, `brief --phase`, `run --format`, `attest export --format`, `ci --format`, and orchestrator `report <format>` now surface a Levenshtein-≤2 typo suggestion in the structured error body alongside the accepted-set list. JSON shape: `{ok:false, error, provided, accepted, did_you_mean:["..."]}`. Example: `brief library-author --phase goven` → `did_you_mean: ["govern"]`.
55
+
56
+ **`lib/lint-skills.js` Hard Rule #1 body-scan.** Every `CVE-* / MAL-*` reference in skill prose is now resolved against the canonical catalog. Missing-from-catalog surfaces as a WARNING in v0.13.2 (will hard-fail in v0.14.0); `_draft:true` references surface as WARNING. The forcing function lands; pre-existing violations on `ransomware-response` (CVE-2024-21762) and `cloud-iam-incident` (CVE-2026-21370) don't block the release but are now visible in every lint run.
57
+
58
+ **`scripts/check-test-count.js` — new 15th predeploy gate.** Static-counts `test(` declarations across `tests/*.test.js` and refuses shrinkage beyond the configured tolerance (default 1). Baseline pinned in `tests/.test-count-baseline.json`. Catches accidentally-deleted test files / mass-skip mistakes that the lint + diff-coverage gates wouldn't surface. Initial baseline 924 declarations across 94 files; bump with `--update-baseline` on releases that legitimately add many tests.
59
+
60
+ **Skill `discovery_mode: standalone` frontmatter field.** 16 skills that are intentionally reached via `exceptd brief <name>` or `exceptd ask` rather than playbook `skill_chain` now carry the explicit marker. Closes the v0.12 audit gap that flagged these as "unreferenced" — operator intent now explicit. Affected: `age-gates-child-safety`, `ai-risk-management`, `defensive-countermeasure-mapping`, `email-security-anti-phishing`, `fuzz-testing-strategy`, `mlops-security`, `ot-ics-security`, `researcher`, `sector-energy`, `sector-federal-government`, `sector-telecom`, `skill-update-loop`, `threat-model-currency`, `threat-modeling-methodology`, `webapp-security`, `zeroday-gap-learn`.
61
+
62
+ ### Bugs
63
+
64
+ **14 still-draft CVEs flipped to verified.** Each got a matching `zeroday-lessons.json` entry (the AGENTS.md rule #6 requirement) and had `_draft` removed: `CVE-2024-3154` (CRI-O kernel-module load), `CVE-2023-43472` (MLflow path-traversal), `CVE-2020-10148` (SUNBURST), `CVE-2023-3519` (Citrix NetScaler unauth RCE), `CVE-2024-1709` (ConnectWise ScreenConnect), `CVE-2026-20182` (Cisco SD-WAN), `CVE-2024-40635` (containerd integer overflow), `CVE-2026-30623` (Anthropic MCP SDK stdio injection), `CVE-2025-12686` (Synology BeeStation Pwn2Own), `CVE-2025-62847` / `CVE-2025-62848` / `CVE-2025-62849` (QNAP QTS DEVCORE chain), `CVE-2025-59389` (QNAP Hyper Data Protector), `CVE-2025-11837` (QNAP Malware Remover). Three new control requirements introduced where the CVE surfaced a novel class: `NEW-CTRL-053` MCP-SERVER-CONFIG-ALLOWLIST, `NEW-CTRL-054` BACKUP-TIER-NETWORK-ISOLATION, `NEW-CTRL-055` SECURITY-TOOL-INTEGRITY-VERIFICATION. Catalog now 37/39 entries verified; 2 remaining drafts are quarantined / embargoed placeholders.
65
+
66
+ **8 framework-gap forward-orphan refs cleaned up.** The v0.13.0 Hard Rule #5 backfill surfaced 8 framework-control gap IDs cited by CVE entries' `framework_control_gaps` field but missing from `framework-control-gaps.json`. All 8 added with theater_test blocks per Hard Rule #6: `NIST-800-53-SC-39` (Process Isolation), `ISO-27001-2022-A.8.22` (Segregation of networks), `CIS-Kubernetes-Benchmark-5.7` (Network Policies), `NIST-800-218-SSDF-PW.4` (Reuse Existing, Well-Secured Software), `NIST-800-53-SR-3` (Supply Chain Controls), `SLSA-v1.0-Source-L3`, `NIST-AI-RMF-MAP-3.4`, `OWASP-Top-10-2021-A06`. Gap catalog 122 → 130 entries.
67
+
68
+ **`release.yml` CHANGELOG-extraction fallback now emits `::warning::`.** Surfaces the parse failure on the run page rather than silently shipping a generic body.
69
+
70
+ ### Internal
71
+
72
+ - 11 new tests in `tests/v0_13_2-fixes.test.js`. Test count baseline 924 (initial pin).
73
+ - Predeploy gate count 14 → 15.
74
+ - `refresh.yml` split-checkout pattern (persist-credentials hardening) deferred to v0.14 — needs peter-evans/create-pull-request auth-mode research first.
75
+
3
76
  ## 0.13.1 — 2026-05-17
4
77
 
5
78
  Threat-intake gap closure. Driven by the post-mortem on CVE-2026-46333 (ssh-keysign-pwn) — disclosed 2026-05-14 by Qualys, missed by the toolkit at T+0 through T+3 because the existing source set (KEV, EPSS, NVD, RFC, PINS, GHSA, OSV) sits at the END of the disclosure pipeline. Adds primary-source polling, CVE-class alert surfacing, and seeds two retroactive catalog entries for the disclosures the toolkit should have caught.
package/bin/exceptd.js CHANGED
@@ -1236,7 +1236,14 @@ function dispatchPlaybook(cmd, argv) {
1236
1236
  // `--mode garbage` was silently accepted.
1237
1237
  const VALID_MODES = ["self_service", "authorized_pentest", "ir_response", "ctf", "research", "compliance_audit"];
1238
1238
  if (!VALID_MODES.includes(args.mode)) {
1239
- return emitError(`run: --mode "${args.mode}" not in accepted set ${JSON.stringify(VALID_MODES)}.`, { provided: args.mode }, pretty);
1239
+ // v0.13.2: did-you-mean on flag-value typos (Levenshtein 2).
1240
+ const dym = suggestFlag(String(args.mode), VALID_MODES);
1241
+ const hint = dym ? ` Did you mean "${dym}"?` : '';
1242
+ return emitError(
1243
+ `run: --mode "${args.mode}" not in accepted set ${JSON.stringify(VALID_MODES)}.${hint}`,
1244
+ { provided: args.mode, accepted: VALID_MODES, did_you_mean: dym ? [dym] : [] },
1245
+ pretty,
1246
+ );
1240
1247
  }
1241
1248
  runOpts.mode = args.mode;
1242
1249
  }
@@ -2268,7 +2275,13 @@ function cmdBrief(runner, args, runOpts, pretty) {
2268
2275
  if (onlyPhase != null) {
2269
2276
  const ACCEPTED_PHASES = ["govern", "direct", "look"];
2270
2277
  if (!ACCEPTED_PHASES.includes(onlyPhase)) {
2271
- return emitError(`brief: --phase "${onlyPhase}" not in accepted set ${JSON.stringify(ACCEPTED_PHASES)}.`, { verb: "brief", provided: onlyPhase }, pretty);
2278
+ const dym = suggestFlag(String(onlyPhase), ACCEPTED_PHASES);
2279
+ const hint = dym ? ` Did you mean "${dym}"?` : '';
2280
+ return emitError(
2281
+ `brief: --phase "${onlyPhase}" not in accepted set ${JSON.stringify(ACCEPTED_PHASES)}.${hint}`,
2282
+ { verb: "brief", provided: onlyPhase, accepted: ACCEPTED_PHASES, did_you_mean: dym ? [dym] : [] },
2283
+ pretty,
2284
+ );
2272
2285
  }
2273
2286
  }
2274
2287
 
@@ -2998,7 +3011,13 @@ function cmdRun(runner, args, runOpts, pretty) {
2998
3011
  const requested = Array.isArray(args.format) ? args.format[0] : args.format;
2999
3012
  const VALID = ["summary", "markdown", "csaf-2.0", "csaf", "sarif", "openvex", "json"];
3000
3013
  if (!VALID.includes(requested)) {
3001
- return emitError(`run: --format "${requested}" not in accepted set ${JSON.stringify(VALID)}.`, null, pretty);
3014
+ const dym = suggestFlag(String(requested), VALID);
3015
+ const hint = dym ? ` Did you mean "${dym}"?` : '';
3016
+ return emitError(
3017
+ `run: --format "${requested}" not in accepted set ${JSON.stringify(VALID)}.${hint}`,
3018
+ { verb: "run", provided: requested, accepted: VALID, did_you_mean: dym ? [dym] : [] },
3019
+ pretty,
3020
+ );
3002
3021
  }
3003
3022
  if (requested === "summary") {
3004
3023
  const cls = result.phases?.detect?.classification;
@@ -4788,7 +4807,13 @@ function cmdAttest(runner, args, runOpts, pretty) {
4788
4807
  // accepting any value the operator passed.
4789
4808
  const VALID_EXPORT_FORMATS = ["json", "csaf", "csaf-2.0"];
4790
4809
  if (!VALID_EXPORT_FORMATS.includes(formatRaw)) {
4791
- return emitError(`attest export: --format "${formatRaw}" not in accepted set ${JSON.stringify(VALID_EXPORT_FORMATS)}.`, null, pretty);
4810
+ const dym = suggestFlag(String(formatRaw), VALID_EXPORT_FORMATS);
4811
+ const hint = dym ? ` Did you mean "${dym}"?` : '';
4812
+ return emitError(
4813
+ `attest export: --format "${formatRaw}" not in accepted set ${JSON.stringify(VALID_EXPORT_FORMATS)}.${hint}`,
4814
+ { verb: "attest export", provided: formatRaw, accepted: VALID_EXPORT_FORMATS, did_you_mean: dym ? [dym] : [] },
4815
+ pretty,
4816
+ );
4792
4817
  }
4793
4818
  const redacted = attestations.map(a => ({
4794
4819
  session_id: a.session_id,
@@ -5180,16 +5205,24 @@ function cmdDoctor(runner, args, runOpts, pretty) {
5180
5205
 
5181
5206
  // Selective subchecks. If any of the four flags is passed, run only those.
5182
5207
  // If none are passed, run all four plus signing-status.
5208
+ // v0.13.3: --ai-config audits AI-assistant config-file permissions per
5209
+ // NEW-CTRL-050 (from the MAL-2026-SHAI-HULUD-OSS zeroday-lessons entry).
5210
+ // It's a separate flag because the check is opt-in — most operators
5211
+ // don't want their AI-config state probed by default.
5183
5212
  const onlySigs = !!args.signatures;
5184
5213
  const onlyCurrency = !!args.currency;
5185
5214
  const onlyCves = !!args.cves;
5186
5215
  const onlyRfcs = !!args.rfcs;
5187
- const anySelected = onlySigs || onlyCurrency || onlyCves || onlyRfcs;
5216
+ const onlyAiConfig = !!args["ai-config"];
5217
+ const anySelected = onlySigs || onlyCurrency || onlyCves || onlyRfcs || onlyAiConfig;
5188
5218
  const runSigs = !anySelected || onlySigs;
5189
5219
  const runCurrency = !anySelected || onlyCurrency;
5190
5220
  const runCves = !anySelected || onlyCves;
5191
5221
  const runRfcs = !anySelected || onlyRfcs;
5192
5222
  const runSigning = !anySelected;
5223
+ // --ai-config is opt-in — never runs as part of the default no-flag
5224
+ // doctor pass. Operators ask for it explicitly.
5225
+ const runAiConfig = onlyAiConfig;
5193
5226
 
5194
5227
  const checks = {};
5195
5228
  const issues = [];
@@ -5428,6 +5461,102 @@ function cmdDoctor(runner, args, runOpts, pretty) {
5428
5461
  }
5429
5462
  }
5430
5463
 
5464
+ // v0.13.3 — AI-assistant config-file permission audit per NEW-CTRL-050
5465
+ // (from the MAL-2026-SHAI-HULUD-OSS zeroday-lessons entry). Walks
5466
+ // ~/.claude/, ~/.cursor/, ~/.codeium/, ~/.aider/, ~/.continue/ for
5467
+ // sensitive config files (settings.json, mcp.json, *.mcp_config.json,
5468
+ // api_key*, *.token, *.credentials) and reports any not at mode 0600.
5469
+ // The MAL-2026-SHAI-HULUD-OSS framework reads these files at
5470
+ // unprivileged-process scope; tightening to 0600 forces npm/node-spawned
5471
+ // processes that don't share UID to fail the read.
5472
+ //
5473
+ // Opt-in only — never runs as part of the default no-flag doctor pass.
5474
+ // Operators request it via `exceptd doctor --ai-config`.
5475
+ if (runAiConfig) {
5476
+ const os = require('os');
5477
+ const HOME = os.homedir();
5478
+ const AI_CONFIG_DIRS = [
5479
+ { dir: '.claude', display: '~/.claude' },
5480
+ { dir: '.cursor', display: '~/.cursor' },
5481
+ { dir: '.codeium', display: '~/.codeium' },
5482
+ { dir: '.aider', display: '~/.aider' },
5483
+ { dir: '.continue', display: '~/.continue' },
5484
+ ];
5485
+ // Files within those dirs that warrant the strict-mode check.
5486
+ const SENSITIVE_PATTERNS = [
5487
+ /^settings\.json$/,
5488
+ /^mcp\.json$/,
5489
+ /\.mcp_config\.json$/,
5490
+ /^api_key/,
5491
+ /\.token$/,
5492
+ /\.credentials$/,
5493
+ ];
5494
+ const findings = [];
5495
+ let scannedDirs = 0;
5496
+ let scannedFiles = 0;
5497
+ function walk(absDir, displayRoot, rel) {
5498
+ if (!fs.existsSync(absDir)) return;
5499
+ let entries;
5500
+ try { entries = fs.readdirSync(absDir, { withFileTypes: true }); }
5501
+ catch { return; }
5502
+ for (const e of entries) {
5503
+ const childAbs = path.join(absDir, e.name);
5504
+ const childRel = rel ? rel + '/' + e.name : e.name;
5505
+ if (e.isDirectory()) {
5506
+ walk(childAbs, displayRoot, childRel);
5507
+ } else if (e.isFile()) {
5508
+ scannedFiles++;
5509
+ if (!SENSITIVE_PATTERNS.some((re) => re.test(e.name))) continue;
5510
+ let st;
5511
+ try { st = fs.statSync(childAbs); } catch { continue; }
5512
+ if (process.platform === 'win32') {
5513
+ // Windows POSIX mode bits don't carry meaningful ACL info.
5514
+ // Flag every sensitive file with a manual-review note rather
5515
+ // than emit a noisy permission claim that's likely wrong.
5516
+ findings.push({
5517
+ path: `${displayRoot}/${childRel}`,
5518
+ mode: null,
5519
+ severity: 'info',
5520
+ issue: 'win32_acl_check_not_implemented',
5521
+ hint: 'On Windows the POSIX mode bits are not load-bearing. Use icacls to confirm only the current user has read access. Tracked for v0.14+.',
5522
+ });
5523
+ continue;
5524
+ }
5525
+ const mode = st.mode & 0o777;
5526
+ if ((mode & 0o077) !== 0) {
5527
+ findings.push({
5528
+ path: `${displayRoot}/${childRel}`,
5529
+ mode: '0' + mode.toString(8),
5530
+ severity: 'warn',
5531
+ issue: 'group_or_other_readable',
5532
+ hint: `chmod 600 '${childAbs}' # NEW-CTRL-050: AI-assistant configs holding MCP tokens / API keys must be 0600 to defeat unprivileged exfil`,
5533
+ });
5534
+ }
5535
+ }
5536
+ }
5537
+ }
5538
+ for (const d of AI_CONFIG_DIRS) {
5539
+ const abs = path.join(HOME, d.dir);
5540
+ if (fs.existsSync(abs)) {
5541
+ scannedDirs++;
5542
+ walk(abs, d.display, '');
5543
+ }
5544
+ }
5545
+ const errorFindings = findings.filter((f) => f.severity === 'warn');
5546
+ checks.ai_config = {
5547
+ ok: errorFindings.length === 0,
5548
+ severity: errorFindings.length > 0 ? 'warn' : 'info',
5549
+ scanned_dirs: scannedDirs,
5550
+ scanned_files: scannedFiles,
5551
+ directories_inspected: AI_CONFIG_DIRS.map((d) => d.display),
5552
+ sensitive_patterns: ['settings.json', 'mcp.json', '*.mcp_config.json', 'api_key*', '*.token', '*.credentials'],
5553
+ findings,
5554
+ platform: process.platform,
5555
+ control_reference: 'NEW-CTRL-050 (MAL-2026-SHAI-HULUD-OSS lesson)',
5556
+ };
5557
+ if (errorFindings.length > 0) issues.push('ai_config');
5558
+ }
5559
+
5431
5560
  // Walk every check and split: errors (severity error/missing/fail) vs warnings
5432
5561
  // (severity warn). all_green is true ONLY when zero errors AND zero warnings.
5433
5562
  const warnList = [];
@@ -6551,9 +6680,13 @@ function cmdCi(runner, args, runOpts, pretty) {
6551
6680
  // Route through emitError so the body propagates exit codes via the
6552
6681
  // emit() ok:false contract. ci-format-typo is operator-decision class
6553
6682
  // (GENERIC_FAILURE), not DETECTED_ESCALATE.
6683
+ // v0.13.2: did-you-mean on the unknown format value (Levenshtein ≤ 2).
6684
+ const CI_FORMATS = ["summary", "markdown", "csaf-2.0", "sarif", "openvex", "json"];
6685
+ const dym = suggestFlag(String(fmt), CI_FORMATS);
6686
+ const hint = dym ? ` Did you mean "${dym}"?` : '';
6554
6687
  emitError(
6555
- `ci: --format "${fmt}" not in accepted set ["summary","markdown","csaf-2.0","sarif","openvex","json"].`,
6556
- { verb: "ci" },
6688
+ `ci: --format "${fmt}" not in accepted set ${JSON.stringify(CI_FORMATS)}.${hint}`,
6689
+ { verb: "ci", provided: fmt, accepted: CI_FORMATS, did_you_mean: dym ? [dym] : [] },
6557
6690
  pretty
6558
6691
  );
6559
6692
  return;
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "schema_version": "1.1.0",
3
- "generated_at": "2026-05-18T01:04:18.854Z",
3
+ "generated_at": "2026-05-18T03:04:24.499Z",
4
4
  "generator": "scripts/build-indexes.js",
5
5
  "source_count": 54,
6
6
  "source_hashes": {
7
- "manifest.json": "500446c009c4da12a8e33794fefaffea287d10ee6f3b4d1e5298fb87447ed09e",
8
- "data/atlas-ttps.json": "0ec427652a9e613f04675beb26dc4c08934ba291e47427972b2a008c151cca78",
9
- "data/attack-techniques.json": "0ca33f8b0cf55a43de1290e310096020c4e0d16305bd01bcbe6cb46e0278caa8",
10
- "data/cve-catalog.json": "7fae34cf0abbd09abbbbd6a61ea06e487ddbd57060d3af6a58528c684156cf60",
11
- "data/cwe-catalog.json": "832d096bd52081fe43c082fd6958f9054d6b6e136df5b3d4cef7efd0ea49a843",
7
+ "manifest.json": "b1b4b86879805e28975155d7aa29c1d1463ec266f2c98a1045d543ecf5acaa6c",
8
+ "data/atlas-ttps.json": "2b021f47355365d1ba59078dfa582397c7a64c2b4ebea4657ea260a66b76daf6",
9
+ "data/attack-techniques.json": "76461dbec048c5e072435d57e3a04b780e3992dab9f316b1b52608e0a997e355",
10
+ "data/cve-catalog.json": "1d34601fbc4ff925ac38b8eb325375a32dc60ffaff31a23a5ca5f3e1524e88f8",
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": "003a400f5ae5b15527589571679ccdb9b3a62e60073627b5fbdeb2a9fe330a7a",
15
- "data/framework-control-gaps.json": "5e2baf1e435c5b61b183e3f603636eae4fab34ee800488919c679665882c4f62",
15
+ "data/framework-control-gaps.json": "ce1535f13d29ab90fac99b983f38a23dd685702b3f12ac9f2371294cb9859ecf",
16
16
  "data/global-frameworks.json": "9ba563a85f7f8d6c3c957de64945e20925a89d0ed6ea6fc561cf093811acf558",
17
17
  "data/rfc-references.json": "e253a548c8a829d178d5aea601e268724b85c936ccbfa51c2e5d80c5f8efe2b0",
18
- "data/zeroday-lessons.json": "40d666d0932da24b425b01ced0f9c9e5f2e6cfd2082f53861d982919dde56a4a",
18
+ "data/zeroday-lessons.json": "1438620d2c8b0606eac4f63e620906b9ba079c57bfa7f737ceb6a50370cdc9a5",
19
19
  "skills/kernel-lpe-triage/skill.md": "ae4a0af924d0078ffc6cd051a3ef9fce75a6a3f9c0c15d1c07900ae5faf80502",
20
20
  "skills/ai-attack-surface/skill.md": "dcca7d92a1ab4d1e4c46356b614a138b1c1f79b65a6a290eccf2095d8d443993",
21
21
  "skills/mcp-agent-trust/skill.md": "6821f6d38f6e23bbed953f8f86a279597b0b95a2d0548b5383e851bca7442531",
@@ -25,38 +25,38 @@
25
25
  "skills/rag-pipeline-security/skill.md": "ff07e48918090247aef71def4150b0df372a24bcdaa34eb6e11d246b9e71e1ee",
26
26
  "skills/ai-c2-detection/skill.md": "3da9f549f5c62e6163cddd70c8edccbef7be622d5a45fa89c90c6550e68c6b2e",
27
27
  "skills/policy-exception-gen/skill.md": "a7d886f7fa99a150b040f158b09045ba45e107439315389aea785311b0013395",
28
- "skills/threat-model-currency/skill.md": "ecc6441cb47ef2bc24547e47be018098228c956a41d61ddb50de7e7b37114a37",
28
+ "skills/threat-model-currency/skill.md": "cf1cc27ae5ae68d336c56d9f3afd950641e1d8d5b9f90b64c2daf00abe92bab0",
29
29
  "skills/global-grc/skill.md": "1dca534cce7612c1d26a7b1bfd088a811081555ecfa25b1f68cff2ca2ba28c98",
30
- "skills/zeroday-gap-learn/skill.md": "59a0d7cd85b923b3f5633bdc15c1a88eef7dea6332480d93b0bb0ae93a4cd0fe",
30
+ "skills/zeroday-gap-learn/skill.md": "e26f194880cd6acf46abe31e9348d445e9222c7691e9b9b953662c4a472462f5",
31
31
  "skills/pqc-first/skill.md": "a7131b65d0ceee47887b16679ee4e4b065d32d8751fe59921762388703662913",
32
- "skills/skill-update-loop/skill.md": "cf2b996cb18a5146614c06e3a50f4734a07d02b5be36bbdf492583f9cdcfed4d",
32
+ "skills/skill-update-loop/skill.md": "b6f3bee321833dc18f5624a9be4d28673d22e22018254b0bd1f3690b945073af",
33
33
  "skills/security-maturity-tiers/skill.md": "ed962937c45f3d95f325f231b787d272fe45c4cb91d4c5a2d982493d722c2acf",
34
- "skills/researcher/skill.md": "b47daaa26fdac07aa23e7becaa18487c5302e65c654f99fecab3689f23ec1bd2",
34
+ "skills/researcher/skill.md": "fd441131484dc5af4cd785ded0bac039123e6205483543752cb16fa508460c00",
35
35
  "skills/attack-surface-pentest/skill.md": "0d301beb9fb8e247ec80256a7e647804b5f9a41c7156e5724555ca9f93ccb986",
36
- "skills/fuzz-testing-strategy/skill.md": "51acb746cd63366ca62567588c700a9eb3f37c43250bd9ae4e1477ccb71c5b6d",
36
+ "skills/fuzz-testing-strategy/skill.md": "fb8c261def9e3344b44fd219c209027029e1eddf0e6bee1ecffb2d2176e1585e",
37
37
  "skills/dlp-gap-analysis/skill.md": "1c4e1d7da2421b82f202eaf2c9e21876af34ab5c76ce1359166842ee473f02dd",
38
38
  "skills/supply-chain-integrity/skill.md": "ad69b72f5c5df095f8618b977fbc8f0fbff396eebd4a8448b44c3f93309f63f9",
39
- "skills/defensive-countermeasure-mapping/skill.md": "e62c71ba3be2b4d0f7dfa529fec007cba6bee3013f76b93756e3e6310f2d22ab",
39
+ "skills/defensive-countermeasure-mapping/skill.md": "3d0c7ca85f32ee1fe74598889361ef2be16d099fe6e9e8d8c8184b7004306b30",
40
40
  "skills/identity-assurance/skill.md": "4ee7096fd82997c66b0f9e825ea3c04c3aa84768b74e6f668c1a9104104138cf",
41
- "skills/ot-ics-security/skill.md": "9ece7b1fb7f24e37dbdd8618b94b2a4434e182e3426e15f17e26464c0a1fdfd1",
41
+ "skills/ot-ics-security/skill.md": "7423cca19aab1026c07de63279137441018345731d3ee895c474316d432adaa2",
42
42
  "skills/coordinated-vuln-disclosure/skill.md": "0e875953bb8a38a89c8ec5d2a9ef967b12e9a9f166dc9356723f10304fd0535e",
43
- "skills/threat-modeling-methodology/skill.md": "ac623f61585de66c9ef5ed63e9c6059faef77e525abc672ac6d435c616a7268f",
44
- "skills/webapp-security/skill.md": "fdb07324b69a3a724e3eaba17bf687d72d4bd9d5c4f440be816bc9b08b8aef04",
45
- "skills/ai-risk-management/skill.md": "67e62791f60231f2ff53408922fa7137a9060de72097769c630f838a1c227c45",
43
+ "skills/threat-modeling-methodology/skill.md": "cebeba3940320ebc5b44ad2bb7b4cdcda412257c1a6319a1b7379c875ebe8d6a",
44
+ "skills/webapp-security/skill.md": "f2063eaea3f5ddf0f3d37b41985bf522b682a41f104796b3f0dff611cefd043c",
45
+ "skills/ai-risk-management/skill.md": "2b611eb8fa4841fdfc3f1dd1ffd504a46c6ecdc654213a955efbabefb6b1db87",
46
46
  "skills/sector-healthcare/skill.md": "a18e11d25524cdbf40df3798f4c2aa3cb51a4db1b088242ea53fa2885e86b64c",
47
47
  "skills/sector-financial/skill.md": "023b5440d614e6b83ba7294219bcac3cdbffd28fdfdd5f0ec23abbeea71b8230",
48
- "skills/sector-federal-government/skill.md": "c63cf1c7c98e920f968cfe60f14e718ea71b120c1b01616af22f64a796963bbe",
49
- "skills/sector-energy/skill.md": "643fd951359c2602d9b029a244fe66c1e23f726e711141a06c09cc760a479534",
50
- "skills/sector-telecom/skill.md": "862f9482af88e5409e011a6981a5d719863deeb646e41cd4df63e5d6597c50b1",
51
- "skills/api-security/skill.md": "2bdfa3dbe534efa3df245e0da37998ad7ab2da4a3171d5000d3346513c10bceb",
48
+ "skills/sector-federal-government/skill.md": "a73c3f36f23c12750d369931b7e3f884edae4a8aef35fc8690d15ef4500c4dd0",
49
+ "skills/sector-energy/skill.md": "91f00e7a9be2608393ec8cb6d5f0c9828f81b954a12a7c9fd04bd642b9091e09",
50
+ "skills/sector-telecom/skill.md": "59193e39c2fd73fdd7fede38a956bc730bbe4b712d7d6020788bb4d85f001ad8",
51
+ "skills/api-security/skill.md": "9fc2252cbcf6162591e70d0bf5499a430b0584495ad584ce49fb7daf070d335f",
52
52
  "skills/cloud-security/skill.md": "c9fad9ed3663cf2faec74ad8f06d62eb86e6636f79933560d8c8d50e0e82d1da",
53
53
  "skills/container-runtime-security/skill.md": "605a8e8eb1af09835b967ec7179456015ec116c6b9051af3a8d225866cc2f7af",
54
- "skills/mlops-security/skill.md": "ca3fd922b43fc57aeb5e65c2d5a2823e6bc438167d6afa3a767cee83e4af1f96",
54
+ "skills/mlops-security/skill.md": "72429f05010accbcb191cb1544f1b88493c2f5249362846e5713ec3226b83dc2",
55
55
  "skills/incident-response-playbook/skill.md": "2017515d899c1b2bcb878bc6731e4059623ac52345b2cebbd92204583657bf60",
56
56
  "skills/ransomware-response/skill.md": "2e4fc488f86ed1ba7791ab0e7021160d8ca5ad33a02cdf92a5b916c8afecaa54",
57
- "skills/email-security-anti-phishing/skill.md": "e4e9e5a820c0ed3fde9483282e7a0ecaf79284cd2e9923ce66f2b0fb1fc44626",
58
- "skills/age-gates-child-safety/skill.md": "66e7773d29c179ab62f409007c05e05993e04a19273225a1e520f2481fd9a90d",
59
- "skills/cloud-iam-incident/skill.md": "6494ee3856edeb212e65fe5cdb208357c1a832eb8ac374b26055586bfc71f629",
57
+ "skills/email-security-anti-phishing/skill.md": "250f266908f51f99a4cb3aec0d5dacfcf91fac9f3d95e5a117429a40ed2ff45a",
58
+ "skills/age-gates-child-safety/skill.md": "51295c849bcced965b6448eb6b4bbd5caef5ba0b0cea7ce48abbacf47d331621",
59
+ "skills/cloud-iam-incident/skill.md": "5ec3800a0049b2123aff67bfab4ff28491a86d2daeb712283e5e88b10c3d5d7b",
60
60
  "skills/idp-incident-response/skill.md": "e67a2576e7f1c3bf89f499f5c977bc470ef29e8b3e3e45f4cb5bd45a82674282"
61
61
  },
62
62
  "skill_count": 42,
@@ -72,13 +72,13 @@
72
72
  "dlp_refs": 0
73
73
  },
74
74
  "trigger_table_entries": 538,
75
- "chains_cve_entries": 34,
75
+ "chains_cve_entries": 35,
76
76
  "chains_cwe_entries": 55,
77
77
  "jurisdictions_indexed": 29,
78
78
  "handoff_dag_nodes": 42,
79
79
  "summary_cards": 42,
80
80
  "section_offsets_skills": 42,
81
- "token_budget_total_approx": 402643,
81
+ "token_budget_total_approx": 404483,
82
82
  "recipes": 8,
83
83
  "jurisdiction_clocks": 29,
84
84
  "did_ladders": 8,
@@ -63,7 +63,7 @@
63
63
  "artifact": "data/framework-control-gaps.json",
64
64
  "path": "data/framework-control-gaps.json",
65
65
  "schema_version": "1.0.0",
66
- "entry_count": 122
66
+ "entry_count": 142
67
67
  },
68
68
  {
69
69
  "date": "2026-05-15",
@@ -87,7 +87,7 @@
87
87
  "artifact": "data/zeroday-lessons.json",
88
88
  "path": "data/zeroday-lessons.json",
89
89
  "schema_version": "1.1.0",
90
- "entry_count": 24
90
+ "entry_count": 39
91
91
  },
92
92
  {
93
93
  "date": "2026-05-15",
@@ -102,7 +102,7 @@
102
102
  "artifact": "data/cve-catalog.json",
103
103
  "path": "data/cve-catalog.json",
104
104
  "schema_version": "1.0.0",
105
- "entry_count": 39
105
+ "entry_count": 40
106
106
  },
107
107
  {
108
108
  "date": "2026-05-13",
@@ -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": 39,
65
+ "entry_count": 40,
66
66
  "sample_keys": [
67
67
  "CVE-2025-53773",
68
68
  "CVE-2026-30615",
@@ -172,7 +172,7 @@
172
172
  "rebuild_after_days": 365,
173
173
  "note": "Per-entry last_verified governs decay. Skills depending on this catalog must check entry freshness before high-stakes use."
174
174
  },
175
- "entry_count": 122,
175
+ "entry_count": 142,
176
176
  "sample_keys": [
177
177
  "ALL-AI-PIPELINE-INTEGRITY",
178
178
  "ALL-MCP-TOOL-TRUST",
@@ -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": 24,
241
+ "entry_count": 39,
242
242
  "sample_keys": [
243
243
  "CVE-2026-31431",
244
244
  "CVE-2025-53773",