From 96dd37282268caf8a86c4542624617043e5d30a7 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:36:27 -0400 Subject: [PATCH] docs: add plugin-acceptance security review and repo security policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the process gap surfaced by a third-party fork that added a plugin bundling a remote MCP server: the marketplace had no security gate for accepting plugins that execute code or delegate trust to a third party. - MIGRATION-PLAYBOOK.md: new "Plugin-acceptance security review" section adjacent to the migration gate — six surfaces (code execution, remote MCP servers, userConfig secrets, cache isolation, data egress, provenance), deny-by-default, grounded in the plugins/MCP reference (verified 2026-07-09). - CLAUDE.md: a "Security-reviewed" design rule + pointer. - SECURITY.md (new): repo-specific policy mirroring the org channel (GitHub private vulnerability reporting + security@melodicsoftware.com), with plugin-supply-chain reporting guidance. Private vulnerability reporting was enabled on this repo this session. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01J1pUFM3DckCZ8iW1JaviZj --- CLAUDE.md | 8 ++++++-- SECURITY.md | 27 +++++++++++++++++++++++++ docs/MIGRATION-PLAYBOOK.md | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 SECURITY.md diff --git a/CLAUDE.md b/CLAUDE.md index f9f2ae10d..a9ca7d4e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,8 +43,12 @@ Machine-readable JSON Schemas (editor validation for the JSON in this repo; Clau plugin via `${CLAUDE_PLUGIN_ROOT}`; persist state in `${CLAUDE_PLUGIN_DATA}`. No `../` reach-outs. - **No PII / secrets.** Public repo + permanent git history: scrub before the first commit, not after. - **Versioned.** Set an explicit semver `version` in each `plugin.json` so consumers update on bumps. +- **Security-reviewed.** Every plugin clears the playbook's plugin-acceptance security review before publish — + code execution, remote MCP servers, config secrets, cache isolation, data egress, and third-party trust. + Deny by default on unjustified egress or trust delegation. ## Process -The full design charter, extensibility model, plugin-form caveats, and per-plugin migration gate live -in [`docs/MIGRATION-PLAYBOOK.md`](docs/MIGRATION-PLAYBOOK.md). Follow it for every migration. +The full design charter, extensibility model, plugin-form caveats, per-plugin migration gate, and the +plugin-acceptance security review live in [`docs/MIGRATION-PLAYBOOK.md`](docs/MIGRATION-PLAYBOOK.md). Follow it +for every migration. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..f0bbf649e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +This marketplace distributes plugins — skills, hooks, and agents — that run code on a consumer's machine and +can wire Claude Code to external systems. Security reports about a published plugin, the marketplace catalog, or +this repository's own tooling are all in scope. + +## Reporting a Vulnerability + +Please do **not** report security vulnerabilities through public GitHub issues, discussions, or pull requests. + +Report them privately through GitHub's [private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability): + +1. Open the **Security** tab of this repository. +2. Click **Report a vulnerability** and complete the advisory form. + +If private reporting is not available, email **** instead. + +Please include enough detail to reproduce and assess the issue: the affected plugin and version (from its +`plugin.json`), the impact, and reproduction steps. If the report concerns a plugin that executes code (a hook) +or connects to a remote MCP server, note the specific command, endpoint, or data flow involved. We will +acknowledge your report and keep you informed as we investigate and address it. + +## Supported Versions + +Fixes are applied to the latest released version of each plugin on the default branch; consumers receive them by +updating the marketplace (a plugin delivers a change only on a `version` bump in its `plugin.json`). Older plugin +versions are not maintained. diff --git a/docs/MIGRATION-PLAYBOOK.md b/docs/MIGRATION-PLAYBOOK.md index 2d9437a70..d0245c34e 100644 --- a/docs/MIGRATION-PLAYBOOK.md +++ b/docs/MIGRATION-PLAYBOOK.md @@ -83,6 +83,47 @@ For each skill/hook/agent being migrated: 2026-06-23). Then run `claude plugin validate --strict ` to validate the **catalog manifest itself** — a bad entry surfaces only there, not in per-plugin validation. Document the plugin in the README. +## Plugin-acceptance security review + +A plugin runs code on the consumer's machine and can wire Claude to external systems. **Every plugin accepted +here — new, or a version bump that adds a trust surface — passes this review** in addition to the migration +gate above (whose step 6 gates PII/secrets). **Deny by default** any surface below that can't be justified. +Facts verified against the plugins/MCP reference 2026-07-09; re-verify per the `CLAUDE.md` fresh-docs mandate. + +1. **Code execution — hooks & scripts.** A hook command runs shell on the consumer's machine on matched events, + with `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_PROJECT_DIR}`, `${CLAUDE_PLUGIN_DATA}`, `${user_config.*}`, and any + `${ENV_VAR}` interpolated in. Check: which binaries it spawns; whether it mutates files in place and is + **advisory** (exits 0, never blocks) vs gating; no `eval` / `curl … | sh` / outbound network; untrusted + input (file contents, tool args, PR/issue text) never flows unquoted into a shell; a kill switch + (`HOOK__ENABLED`) exists. +2. **Remote MCP servers — `.mcp.json` / inline in `plugin.json`.** **Net-new surface** — no current plugin + ships one. A plugin's MCP server **starts automatically when the plugin is enabled** (subject to per-server + approval). Check: the server host/URL and who runs it (first-party vs a third party you're delegating trust + to); transport (local `stdio` vs remote `http`/`sse`/`ws`); **what data leaves the machine** — a remote + server receives whatever Claude sends and, if it returns external content, is a prompt-injection vector + (official guidance: "Verify you trust each server before connecting it"); auth shape (header/Bearer/OAuth) + with any token sourced from `userConfig` `sensitive` or an env var, **never hardcoded**; a stated reason the + capability can't be a local `stdio` server. **Do not accept a third-party remote MCP server** without an + explicit recorded trust decision naming the vendor, the data egress, and the token scope. +3. **Consumer config — `userConfig`.** Any credential/token option MUST set `"sensitive": true` — that masks + input and stores the value in the system keychain (or `~/.claude/.credentials.json`), **not** `settings.json`. + Non-sensitive values land in `settings.json` under `pluginConfigs[].options` and are readable — never put + a secret there. Endpoints and toggles are fine as non-sensitive. Every option is documented. +4. **Cache isolation — no reach-outs.** References only files inside the plugin via `${CLAUDE_PLUGIN_ROOT}`; + persists state in `${CLAUDE_PLUGIN_DATA}`. No `../` reach-outs, no absolute paths, no reading consumer files + outside `${CLAUDE_PROJECT_DIR}`. +5. **Data egress — telemetry & network.** Any telemetry (e.g. `HOOK_TELEMETRY_SINK`) is opt-in (unset = exact + no-op), never writes to the hook's stdout/`additionalContext` channel, and emits only the declared envelope — + no payload beyond the documented schema. Name any other outbound network call and justify it. +6. **Provenance & third-party trust.** Verify authorship (does `plugin.json` `author` match who actually + submitted the PR?), license, and that the source is what it claims. A plugin that promotes or wires a + third-party SaaS is a trust delegation — record accept/deny with rationale. Note the platform already blocks + plugin-shipped **agents** from declaring `hooks` / `mcpServers` / `permissionMode` "for security reasons" — + don't design around that. + +Record accept/deny + rationale for any plugin touching surfaces 2, 5, or 6; a later version bump that +introduces a new surface re-triggers this review. + ## Local development loop For a plugin that already ships here, iterate against your local clone without re-publishing and