feat(plh): validate plugin.json skills:-array entries (CA-PLH-016)
PLH now validates each entry of a plugin.json `skills` field (string|array):
every entry must resolve to an existing directory inside the plugin root.
One finding per bad entry (medium, plugin-hygiene), problem ∈ {non-string,
escapes-root, not-found, not-a-directory}. Mirrors `claude plugin validate`.
String|array normalized so a non-string top-level value is caught too.
Verifiseringsplikt: the plan's "CC suggests the parent directory" error text
is NOT in the primary docs — dropped. Only the four primary-source-verified
conditions are asserted (escape backed by the path-traversal rule).
Tests +3 (941->944). Scanner count unchanged (13). --json/--raw byte-stable.
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
9b63125f6a
commit
76d5eda101
9 changed files with 189 additions and 2 deletions
20
CLAUDE.md
20
CLAUDE.md
|
|
@ -201,6 +201,26 @@ flagged, because Claude Code keeps scanning the folder in that case (`addressesD
|
|||
predicate). The v5.4.0 plan originally listed `commands/agents/skills/hooks`; that set was
|
||||
corrected here against the live docs (Verifiseringsplikt).
|
||||
|
||||
### PLH scanner — skills:-array validation (`CA-PLH-016`)
|
||||
|
||||
Per-plugin check (in `scanSinglePlugin`, after the shadow check): when `plugin.json` has a
|
||||
`skills` field (string or array), each entry must resolve to an **existing directory inside the
|
||||
plugin root**. The value is normalized `Array.isArray(v) ? v : [v]`, so a single string is one
|
||||
entry — and a non-string top-level value (e.g. `42`) is naturally caught as a single non-string
|
||||
entry (no separate top-level check needed). One finding per bad entry, severity **MEDIUM**,
|
||||
`category: 'plugin-hygiene'`, `details: { field: 'skills', entry, problem }` where `problem` is
|
||||
one of `non-string` / `escapes-root` / `not-found` / `not-a-directory`. Mirrors
|
||||
`claude plugin validate` (~2.1.145).
|
||||
|
||||
Escape detection uses `skillsEntryEscapesRoot` (resolve + `startsWith(pluginDir + sep)`
|
||||
containment — robust against a literal `..foo` dir name), backed by the docs' path-traversal rule
|
||||
(*"Installed plugins cannot reference files outside their directory … such as `../shared-utils`"*).
|
||||
`statOrNull` distinguishes missing from file-vs-dir. **Verifiseringsplikt note:** the v5.4.0 plan
|
||||
claimed CC "suggests the parent directory when an entry points at a file"; that exact error text is
|
||||
**not** in the primary docs, so it was dropped — the finding asserts only the four
|
||||
primary-source-verified conditions. `skills` is deliberately *not* in `SHADOWING_PATH_FIELDS`
|
||||
(it adds to the default scan, never shadows).
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Session directories accumulate — use `/config-audit cleanup` to manage
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue