release: v5.4.0 — plugin-hygiene & settings-validation hardening
Three additive findings extend existing PLH and SET scanners (scanner count stays 13; --json/--raw byte-stable): - CA-PLH-015 plugin-folder shadowing — a plugin.json component-path key in the replaces-set (commands/agents/outputStyles) pointing at a custom path while the default folder still exists; mirrors CC /doctor & claude plugin list. - CA-PLH-016 skills:-array validation — each entry must resolve to a directory inside the plugin root; flags non-string/escapes-root/not-found/not-a-directory; mirrors claude plugin validate. - CA-SET autoMode — structure (only environment/allow/soft_deny/hard_deny string arrays; "$defaults" valid) = medium; dead-config (autoMode in shared .claude/settings.json is not read by CC) = low. Release mechanics: version 5.3.0 -> 5.4.0 (plugin.json); CHANGELOG [5.4.0]; README badge/TOC/What's-New/version-history; knowledge v5.4.0 scanner-backing facts. Gates: suite 949/949, self-audit A/A + readmeCheck.passed (count 13), SC-5 byte-equal, gitleaks clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ter3E2JSi1Khgmuf2kady8
This commit is contained in:
parent
d77c18aa53
commit
a86b92e2b5
4 changed files with 75 additions and 28 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "config-audit",
|
"name": "config-audit",
|
||||||
"description": "Multi-agent workflow for analyzing, reporting, and optimizing Claude Code configuration across your entire machine",
|
"description": "Multi-agent workflow for analyzing, reporting, and optimizing Claude Code configuration across your entire machine",
|
||||||
"version": "5.3.0",
|
"version": "5.4.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Kjell Tore Guttormsen"
|
"name": "Kjell Tore Guttormsen"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
41
CHANGELOG.md
41
CHANGELOG.md
|
|
@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [5.4.0] - 2026-06-19
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
Plugin-hygiene & settings-validation hardening. Three additive findings extend the existing PLH and
|
||||||
|
SET scanners — no new scanner file, so the scanner count stays **13**. PLH gains plugin-folder
|
||||||
|
shadow detection and `skills:`-array validation; the settings validator now checks `autoMode`
|
||||||
|
structure and flags it when placed in shared project settings, where Claude Code does not read it.
|
||||||
|
Scanner internals only — no command, agent, or output-format changes; `--json` and `--raw` remain
|
||||||
|
byte-stable.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **PLH plugin-folder shadowing** (`CA-PLH-015`) — flags a `plugin.json` component-path key in the
|
||||||
|
*replaces* set (`commands`/`agents`/`outputStyles`) that points at a custom path while the
|
||||||
|
default folder of that name still exists on disk, so the folder is silently ignored (dead
|
||||||
|
config). Mirrors Claude Code's own warning in `/doctor`, `claude plugin list`, and the `/plugin`
|
||||||
|
detail view (v2.1.140+). Severity **medium**, `category: 'plugin-hygiene'`,
|
||||||
|
`details: { field, ignoredDir, customPaths }`. Deliberately excludes `skills` (adds to the
|
||||||
|
default scan, never shadows) and `hooks`/`mcpServers`/`lspServers` (own merge rules); honors the
|
||||||
|
explicit-address exception (a custom path resolving *into* the default folder is not flagged).
|
||||||
|
- **PLH `skills:`-array validation** (`CA-PLH-016`) — validates each `plugin.json` `skills` entry
|
||||||
|
(string or array) resolves to an existing directory inside the plugin root; one finding per bad
|
||||||
|
entry with `problem` ∈ `non-string` / `escapes-root` / `not-found` / `not-a-directory`. Mirrors
|
||||||
|
`claude plugin validate` (~2.1.145). Severity **medium**, `category: 'plugin-hygiene'`,
|
||||||
|
`details: { field: 'skills', entry, problem }`. Path-escape detection is containment-based
|
||||||
|
(resolve + plugin-root prefix), backed by the docs' path-traversal rule.
|
||||||
|
- **SET autoMode structure + dead-config** (`CA-SET`) — `autoMode`, if present, must be an object
|
||||||
|
whose only keys are `environment`/`allow`/`soft_deny`/`hard_deny`, each a **string array** (the
|
||||||
|
literal `"$defaults"` is a valid entry); unknown sub-keys and wrong value types are flagged
|
||||||
|
(`problem` ∈ `not-an-object` / `unknown-subkey` / `not-string-array`), severity **medium**.
|
||||||
|
Separately, `autoMode` placed in **shared** project settings (`.claude/settings.json`) is flagged
|
||||||
|
as dead config (`problem: 'shared-project-scope'`), severity **low** — Claude Code's classifier
|
||||||
|
does not read `autoMode` from shared project settings. The two sub-checks are independent. Both
|
||||||
|
verified against `code.claude.com/docs/en/auto-mode-config`.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
- **Test count** — 936 → **949** (+13: PLH-015, PLH-016, and SET autoMode structure/dead-config).
|
||||||
|
- **Scanner count stays 13** — all three findings extend existing PLH and SET scanners; no new
|
||||||
|
scanner file, no badge/table/`countScannerShape` change.
|
||||||
|
- **Byte-stability** — `json-backcompat` + `raw-backcompat` + the SC-5 default-output snapshot stay
|
||||||
|
green; the three findings are additive and appear only in configs that trigger them.
|
||||||
|
|
||||||
## [5.3.0] - 2026-06-19
|
## [5.3.0] - 2026-06-19
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
|
|
|
||||||
46
README.md
46
README.md
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
*AI-generated: all code produced by Claude Code through dialog-driven development. [Full disclosure →](../../README.md#ai-generated-code-disclosure)*
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
@ -21,7 +21,7 @@ A Claude Code plugin that checks configuration health, suggests context-aware im
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [What's New in v5.3.0](#whats-new-in-v530)
|
- [What's New in v5.4.0](#whats-new-in-v540)
|
||||||
- [What Is This?](#what-is-this)
|
- [What Is This?](#what-is-this)
|
||||||
- [The Configuration Problem](#the-configuration-problem)
|
- [The Configuration Problem](#the-configuration-problem)
|
||||||
- [Quick Start](#quick-start)
|
- [Quick Start](#quick-start)
|
||||||
|
|
@ -45,31 +45,28 @@ A Claude Code plugin that checks configuration health, suggests context-aware im
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What's New in v5.3.0
|
## What's New in v5.4.0
|
||||||
|
|
||||||
**Permission-rule & plugin-hygiene hardening.** Five additive scanner findings extend the
|
**Plugin-hygiene & settings-validation hardening.** Three additive findings extend the plugin and
|
||||||
permission, CLAUDE.md, and plugin surfaces — no new scanner, so the count stays **13**:
|
settings surfaces — no new scanner, so the count stays **13**:
|
||||||
|
|
||||||
- **DIS forbidden-param rules** — flags `Tool(param:value)` whose key is the tool's own
|
- **PLH plugin-folder shadowing** (`CA-PLH-015`) — flags a `plugin.json` component-path key in the
|
||||||
canonicalizing field (`command`/`file_path`/`path`/`notebook_path`/`url`), which Claude Code
|
*replaces* set (`commands`/`agents`/`outputStyles`) that points at a custom path while the
|
||||||
silently ignores with a startup warning. Severity by intent: deny/ask = false security
|
default folder of that name still exists, so the folder is silently ignored (dead config).
|
||||||
(medium), allow = dead config (low). Valid forms (`Bash(npm:*)`, `WebFetch(domain:host)`,
|
Mirrors Claude Code's own warning in `/doctor`, `claude plugin list`, and the `/plugin` detail
|
||||||
`Agent(model:opus)`) are never flagged.
|
view. `skills` is excluded (it *adds to* the default scan, never shadows), as are
|
||||||
- **DIS ineffective allow-wildcards** — unanchored tool-name globs in `permissions.allow`
|
`hooks`/`mcpServers`/`lspServers` (own merge rules); a custom path resolving *into* the default
|
||||||
(`*`, `B*`, `mcp__*`) that CC silently skips, plus `Tool(*)` treated as deny-all
|
folder is not flagged.
|
||||||
(`Bash(*)` ≡ `Bash`) so a bare allow killed by it is reported as dead config.
|
- **PLH `skills:`-array validation** (`CA-PLH-016`) — validates each `plugin.json` `skills` entry
|
||||||
- **CML context-window-scaled char budget** — mirrors CC's own *"Large CLAUDE.md will impact
|
(string or array) resolves to an existing directory inside the plugin root; flags `non-string`,
|
||||||
performance (X chars > 40.0k)"* startup warning; anchors on the conservative 200k window and
|
`escapes-root`, `not-found`, and `not-a-directory` entries. Mirrors `claude plugin validate`.
|
||||||
discloses the relaxed ~200,000-char figure at 1M context. Char-keyed, complementary to the
|
- **SET `autoMode` structure + dead-config** — checks that `autoMode` is an object whose only keys
|
||||||
existing line checks.
|
are `environment`/`allow`/`soft_deny`/`hard_deny`, each a string array (the literal `"$defaults"`
|
||||||
- **PLH plugin namespace collision** — flags two or more plugins declaring the same `name` in
|
is valid); unknown sub-keys and wrong types are flagged (medium). Separately, `autoMode` placed
|
||||||
`plugin.json`; their namespaces collapse and Claude Code silently drops the loser's components.
|
in **shared** project settings (`.claude/settings.json`) is flagged as dead config (low) —
|
||||||
- **feature-gap `disableBundledSkills` lever** — recommends disabling bundled skills when the
|
Claude Code's classifier does not read it there.
|
||||||
active skill listing is over budget (remediation companion to `CA-SKL-002`).
|
|
||||||
|
|
||||||
The cross-plugin command-name finding is reframed from a HIGH conflict to a **LOW ambiguity** to
|
All three extend existing PLH and SET scanners. `--json` and `--raw` output remain byte-stable.
|
||||||
match Claude Code's namespacing (`/name:command` keeps both commands reachable). `--json` and
|
|
||||||
`--raw` output remain byte-stable.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -650,6 +647,7 @@ This plugin is cautious by design — configuration files are important, and a b
|
||||||
|
|
||||||
| Version | Date | Highlights |
|
| Version | Date | Highlights |
|
||||||
|---------|------|-----------|
|
|---------|------|-----------|
|
||||||
|
| **5.4.0** | 2026-06-19 | Plugin-hygiene & settings-validation hardening. Three additive findings extend existing PLH and SET scanners (count stays **13**): PLH plugin-folder shadowing (`CA-PLH-015` — a `plugin.json` component-path key in the *replaces* set `commands`/`agents`/`outputStyles` pointing at a custom path while the default folder still exists) mirroring CC's `/doctor` & `claude plugin list` warning; PLH `skills:`-array validation (`CA-PLH-016` — each entry must resolve to a directory in the plugin root; flags `non-string`/`escapes-root`/`not-found`/`not-a-directory`) mirroring `claude plugin validate`; SET `autoMode` structure (only `environment`/`allow`/`soft_deny`/`hard_deny` string arrays) + dead-config (`autoMode` in shared `.claude/settings.json` is not read by CC). `--json`/`--raw` byte-stable. 949 tests |
|
||||||
| **5.3.0** | 2026-06-19 | Permission-rule & plugin-hygiene hardening. Five additive scanner findings extend existing scanners (count stays **13**): DIS forbidden-param rules (`Tool(param:value)` on a canonicalizing field — deny/ask = false security, allow = dead config) and ineffective allow-wildcards + `Tool(*)` deny-all; CML context-window-scaled 40.0k-char CLAUDE.md budget mirroring CC's startup warning; PLH plugin namespace collision (two plugins declaring the same `name`); feature-gap `disableBundledSkills` lever under skill-listing pressure. PLH cross-plugin command-name overlap reframed HIGH → LOW (namespacing keeps both reachable). `--json`/`--raw` byte-stable. 936 tests |
|
| **5.3.0** | 2026-06-19 | Permission-rule & plugin-hygiene hardening. Five additive scanner findings extend existing scanners (count stays **13**): DIS forbidden-param rules (`Tool(param:value)` on a canonicalizing field — deny/ask = false security, allow = dead config) and ineffective allow-wildcards + `Tool(*)` deny-all; CML context-window-scaled 40.0k-char CLAUDE.md budget mirroring CC's startup warning; PLH plugin namespace collision (two plugins declaring the same `name`); feature-gap `disableBundledSkills` lever under skill-listing pressure. PLH cross-plugin command-name overlap reframed HIGH → LOW (namespacing keeps both reachable). `--json`/`--raw` byte-stable. 936 tests |
|
||||||
| **5.2.0** | 2026-06-18 | CC 2.1.114→181 compatibility + skill-listing budget. New orchestrated scanner **SKL** (`CA-SKL-001` 1,536-char listing cap, `CA-SKL-002` listing-budget sum) → 13 orchestrated scanners. Five validators refreshed for CC 2.1.114–181 settings/hook surface (`xhigh` effort, `MessageDisplay` + post-session events, 28 hook events). False positives eliminated in MCP (auto-injected/POSIX env vars, invented `trust` field) and permissions (param-aware DIS/CNF). Hermetic HOME isolation across all CLI-spawning tests. 875 tests |
|
| **5.2.0** | 2026-06-18 | CC 2.1.114→181 compatibility + skill-listing budget. New orchestrated scanner **SKL** (`CA-SKL-001` 1,536-char listing cap, `CA-SKL-002` listing-budget sum) → 13 orchestrated scanners. Five validators refreshed for CC 2.1.114–181 settings/hook surface (`xhigh` effort, `MessageDisplay` + post-session events, 28 hook events). False positives eliminated in MCP (auto-injected/POSIX env vars, invented `trust` field) and permissions (param-aware DIS/CNF). Hermetic HOME isolation across all CLI-spawning tests. 875 tests |
|
||||||
| **5.1.0** | 2026-05-01 | Plain-language UX humanizer. Default output of all 18 commands now leads with prose; findings grouped by user-impact category (Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config) and led by urgency phrase (Fix this now → FYI). New `--raw` flag preserves v5.0.0 verbatim output for tooling that scrapes stderr; `--json` is unchanged and byte-stable. New scanner-lib modules: `humanizer.mjs`, `humanizer-data.mjs` with TRANSLATIONS for 13 scanner prefixes. Self-audit terminal output also humanized. 792 tests (+157 humanizer-tester) |
|
| **5.1.0** | 2026-05-01 | Plain-language UX humanizer. Default output of all 18 commands now leads with prose; findings grouped by user-impact category (Configuration mistake, Conflict, Wasted tokens, Missed opportunity, Dead config) and led by urgency phrase (Fix this now → FYI). New `--raw` flag preserves v5.0.0 verbatim output for tooling that scrapes stderr; `--json` is unchanged and byte-stable. New scanner-lib modules: `humanizer.mjs`, `humanizer-data.mjs` with TRANSLATIONS for 13 scanner prefixes. Self-audit terminal output also humanized. 792 tests (+157 humanizer-tester) |
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,14 @@
|
||||||
| **"Large CLAUDE.md will impact performance (X chars > 40.0k)"** — Claude Code emits this startup warning when a CLAUDE.md exceeds ~40,000 chars. CC 2.1.169 scales the threshold with the model's context window: ~40.0k at a 200k-context model, relaxing to ~200,000 chars at 1M context. The figure is a **char** count, not a line count — complementary to the 200/500-line adherence guidance (a file can be long by lines yet under budget, or short by lines yet over it). | CC changelog 2.1.169 + live startup-warning text | CML `CA-CML` char-budget finding — anchors on the conservative 200k window (fires earliest, since the user's window is unobservable) and discloses the relaxed 1M figure. Window constants in `scanners/lib/context-window.mjs`. Severity medium (token cost, not an adherence cliff). |
|
| **"Large CLAUDE.md will impact performance (X chars > 40.0k)"** — Claude Code emits this startup warning when a CLAUDE.md exceeds ~40,000 chars. CC 2.1.169 scales the threshold with the model's context window: ~40.0k at a 200k-context model, relaxing to ~200,000 chars at 1M context. The figure is a **char** count, not a line count — complementary to the 200/500-line adherence guidance (a file can be long by lines yet under budget, or short by lines yet over it). | CC changelog 2.1.169 + live startup-warning text | CML `CA-CML` char-budget finding — anchors on the conservative 200k window (fires earliest, since the user's window is unobservable) and discloses the relaxed 1M figure. Window constants in `scanners/lib/context-window.mjs`. Severity medium (token cost, not an adherence cliff). |
|
||||||
| **`Tool(param:value)` permission matching is deny/ask-only.** The `param:value` form is honored only in `permissions.deny` and `permissions.ask`; in `permissions.allow` it matches nothing (dead config). Separately, a rule whose key is the tool's own *canonicalizing field* — `command` (Bash/PowerShell), `file_path` (Read/Edit/Write), `path` (Grep/Glob), `notebook_path` (NotebookEdit), `url` (WebFetch) — is **ignored** by CC, which emits a startup warning. Valid parameter forms (`Bash(npm:*)`, `WebFetch(domain:host)`, `Agent(model:opus)`) are honored. | code.claude.com/docs/en/permissions | DIS forbidden-param finding (`forbiddenParamRule` in `permission-rules.mjs`): deny/ask = false security (medium, the block never applies), allow = dead config (low). |
|
| **`Tool(param:value)` permission matching is deny/ask-only.** The `param:value` form is honored only in `permissions.deny` and `permissions.ask`; in `permissions.allow` it matches nothing (dead config). Separately, a rule whose key is the tool's own *canonicalizing field* — `command` (Bash/PowerShell), `file_path` (Read/Edit/Write), `path` (Grep/Glob), `notebook_path` (NotebookEdit), `url` (WebFetch) — is **ignored** by CC, which emits a startup warning. Valid parameter forms (`Bash(npm:*)`, `WebFetch(domain:host)`, `Agent(model:opus)`) are honored. | code.claude.com/docs/en/permissions | DIS forbidden-param finding (`forbiddenParamRule` in `permission-rules.mjs`): deny/ask = false security (medium, the block never applies), allow = dead config (low). |
|
||||||
|
|
||||||
|
## v5.4.0 scanner-backing facts (verified 2026-06-19)
|
||||||
|
|
||||||
|
| Fact | Source | Backs |
|
||||||
|
|------|--------|-------|
|
||||||
|
| **plugin.json component-path keys in the *replaces* set shadow their default folder.** `commands`, `agents`, and `outputStyles` each replace a default folder (`commands/`, `agents/`, `output-styles/`); setting one to a custom path while the same-named default folder still exists makes the folder silently ignored — Claude Code warns in `/doctor`, `claude plugin list`, and the `/plugin` detail view. **`skills` is different — it *adds to* the default `skills/` scan (both load, never a shadow).** `hooks`/`mcpServers`/`lspServers` have their own merge rules (not folder shadows). A custom path that resolves *into* the default folder (e.g. `"commands": ["./commands/x.md"]`) keeps the folder scanned (explicit-address exception). | code.claude.com/docs path-behavior-rules; CC `/doctor` + `claude plugin list` + `/plugin` detail (v2.1.140+) | PLH `CA-PLH-015` plugin-folder shadowing — `SHADOWING_PATH_FIELDS` = `commands`/`agents`/`outputStyles` only; `addressesDefaultDir` exception. Severity medium, `category: 'plugin-hygiene'`. |
|
||||||
|
| **`claude plugin validate` requires each `skills:` entry to be a directory inside the plugin.** A `plugin.json` `skills` field (string or array) must point at an existing directory within the plugin root; installed plugins **cannot reference files outside their directory** (no `../shared-utils` path traversal). | `claude plugin validate` (~2.1.145); code.claude.com/docs path-traversal rule | PLH `CA-PLH-016` `skills:`-array validation — normalizes string→`[string]`, flags `non-string` / `escapes-root` / `not-found` / `not-a-directory`; escape detection is plugin-root containment. Severity medium. |
|
||||||
|
| **`autoMode` is a four-sub-key object and is not read from shared project settings.** Its only valid sub-keys are `environment`, `allow`, `soft_deny`, `hard_deny`, each a **string array** (entries are prose rules; the literal `"$defaults"` is valid). The classifier **does not read `autoMode` from shared project settings** (`.claude/settings.json`), so a checked-in repo cannot inject its own allow rules; valid scopes are user (`~/.claude/settings.json`), local (`.claude/settings.local.json`), and managed. | code.claude.com/docs/en/auto-mode-config | SET `CA-SET` autoMode — structure (object + four string-array sub-keys; `not-an-object`/`unknown-subkey`/`not-string-array`) = medium; dead-config (`shared-project-scope`, keyed on `file.scope === 'project'`) = low. |
|
||||||
|
|
||||||
## Official Configuration Guidance (Anthropic)
|
## Official Configuration Guidance (Anthropic)
|
||||||
|
|
||||||
These principles are backed by official docs and verified community reports. Use them to ground recommendations.
|
These principles are backed by official docs and verified community reports. Use them to ground recommendations.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue