From 7fbdc55feac5730952debc35779305fe4987cdff Mon Sep 17 00:00:00 2001 From: Tomelias Date: Fri, 25 Sep 2026 13:06:51 +0300 Subject: [PATCH 1/8] feat(skills): add MCP dependency review skill --- .agents/skills/mcp-dependency-review/SKILL.md | 150 +++++++++++++++++ .../mcp-dependency-review/agents/openai.yaml | 7 + .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .cursor/skills/mcp-dependency-review/SKILL.md | 150 +++++++++++++++++ AGENTS.md | 4 +- README.md | 4 +- README.zh-CN.md | 2 +- agent.yaml | 1 + docs/zh-CN/AGENTS.md | 4 +- docs/zh-CN/README.md | 6 +- manifests/install-modules.json | 1 + package.json | 1 + skills/mcp-dependency-review/SKILL.md | 151 ++++++++++++++++++ 14 files changed, 473 insertions(+), 12 deletions(-) create mode 100644 .agents/skills/mcp-dependency-review/SKILL.md create mode 100644 .agents/skills/mcp-dependency-review/agents/openai.yaml create mode 100644 .cursor/skills/mcp-dependency-review/SKILL.md create mode 100644 skills/mcp-dependency-review/SKILL.md diff --git a/.agents/skills/mcp-dependency-review/SKILL.md b/.agents/skills/mcp-dependency-review/SKILL.md new file mode 100644 index 000000000..86ac921c0 --- /dev/null +++ b/.agents/skills/mcp-dependency-review/SKILL.md @@ -0,0 +1,150 @@ +--- +name: mcp-dependency-review +description: Statically review MCP configuration for mutable package references before approval or CI, without executing discovered MCP servers. Use when reviewing .mcp.json, Cursor, VS Code, Claude, Windsurf, or other repo-scoped MCP config. +license: MIT +--- + +# MCP Dependency Review + +Use this skill to review MCP configuration for package references that can resolve to different code after the configuration itself was approved. + +This is a **static review workflow**. Read configuration as text/JSON only. Do not execute discovered MCP server commands as part of the review. + +## When to Activate + +- Before approving a new or changed MCP configuration. +- When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. +- When adding a deterministic MCP configuration check to CI. +- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When a team wants to know whether a previously reviewed config can silently resolve to newer package code. + +## Review Boundary + +Default to the repository or workspace the user asked about. Do not inspect home-directory or machine-wide MCP configuration unless the user explicitly requests that broader scope. + +Do not: + +- run a `command` or `args` value found in MCP configuration; +- start an MCP server to confirm a static finding; +- install or resolve a referenced package merely to classify its version selector; +- copy credentials, headers, tokens, or secret values into the report; +- describe dependency mutability alone as proof of a vulnerability, compromise, or malicious package. + +## Static Classification Rules + +For npm/npx-style package selectors, classify the package reference itself: + +| Selector | Result | Why | +| --- | --- | --- | +| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package` | HIGH | A future resolution can select different package code. | +| `@scope/package` | HIGH | Scoped bare package is still mutable. | +| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@^1.2.0` | MEDIUM | Resolution can move within the range. | +| `package@~1.2.0` | MEDIUM | Resolution can move within the range. | +| wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | +| local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | + +Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. + +## Review Workflow + +### 1. Locate repo-scoped configuration + +Check only relevant workspace paths first, for example: + +```text +.mcp.json +.github/mcp.json +.cursor/mcp.json +.vscode/mcp.json +.windsurf/mcp.json +``` + +Also inspect another MCP config path when the user names it explicitly. + +### 2. Parse without executing + +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. + +Never execute the discovered command to learn what it does. + +### 3. Classify the selector + +Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. + +### 4. Recommend a reproducible fix + +For mutable selectors, recommend an exact package version that the team has actually reviewed. + +Do **not** invent a pin by substituting today's latest registry version. If the reviewed version is unknown, say so. A registry-history lookup is a separate network operation and should only be performed when the user asks for it. + +### 5. Produce a bounded report + +Use a compact table: + +| Config | MCP server | Package/reference | Result | Why | Next step | +| --- | --- | --- | --- | --- | --- | + +End with these boundaries: + +- No MCP servers were executed during this review. +- Mutable dependency references are reproducibility/review signals, not breach claims. +- A clean result here is not a complete MCP security assessment. + +## Example + +Given: + +```json +{ + "mcpServers": { + "browser": { + "command": "npx", + "args": ["-y", "example-browser-mcp@latest"] + } + } +} +``` + +Report: + +```text +.mcp.json | browser | example-browser-mcp@latest | HIGH +Reason: @latest can resolve to different package code later without a config diff. +Next: pin the exact version the team reviews and update it deliberately. +``` + +## Anti-Patterns + +### Calling every mutable reference a vulnerability + +**Wrong:** `@latest` means the MCP package is compromised. + +**Better:** `@latest` means the configuration does not fully determine which package version will run later. Establish actual security impact separately. + +### Pinning whatever is latest today + +**Wrong:** replace a mutable selector with the current registry version and call the review fixed. + +**Better:** pin a version the team has reviewed. If that evidence is unavailable, record the uncertainty. + +### Expanding scope silently + +**Wrong:** a repo review automatically scans user-level Claude, Cursor, or VS Code configuration. + +**Better:** remain workspace-scoped unless machine-wide review is explicitly requested. + +### Treating a clean drift review as complete MCP security + +Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. + +## Related Skills + +- `mcp-server-patterns` — MCP server design, tools, resources, prompts, and transports. +- `security-review` — broader application-security checklist. +- `security-scan` — broader security scanning workflow. + +## Further Reading + +A public reference implementation and reproducible research methodology for this narrow review class are available in [MCP Drift Check](https://github.com/tomelias10/mcp-drift-check). The external project is optional; this ECC skill does not require it to perform the static review. diff --git a/.agents/skills/mcp-dependency-review/agents/openai.yaml b/.agents/skills/mcp-dependency-review/agents/openai.yaml new file mode 100644 index 000000000..6ffc64aa1 --- /dev/null +++ b/.agents/skills/mcp-dependency-review/agents/openai.yaml @@ -0,0 +1,7 @@ +interface: + display_name: "MCP Dependency Review" + short_description: "Review mutable MCP package references" + brand_color: "#0EA5E9" + default_prompt: "Use $mcp-dependency-review to statically review this repository's MCP configuration without executing discovered servers." +policy: + allow_implicit_invocation: true diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b19c87b8d..d5385a381 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "Harness-native ECC operator layer - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", + "description": "Harness-native ECC operator layer - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.2.2", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 072edddfe..8871e37d2 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.2.2", - "description": "Harness-native ECC plugin for engineering teams - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", + "description": "Harness-native ECC plugin for engineering teams - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/.cursor/skills/mcp-dependency-review/SKILL.md b/.cursor/skills/mcp-dependency-review/SKILL.md new file mode 100644 index 000000000..fba6f89ae --- /dev/null +++ b/.cursor/skills/mcp-dependency-review/SKILL.md @@ -0,0 +1,150 @@ +--- +name: mcp-dependency-review +description: Statically review MCP configuration for mutable package references before approval or CI, without executing discovered MCP servers. +origin: ECC +--- + +# MCP Dependency Review + +Use this skill to review MCP configuration for package references that can resolve to different code after the configuration itself was approved. + +This is a **static review workflow**. Read configuration as text/JSON only. Do not execute discovered MCP server commands as part of the review. + +## When to Activate + +- Before approving a new or changed MCP configuration. +- When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. +- When adding a deterministic MCP configuration check to CI. +- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When a team wants to know whether a previously reviewed config can silently resolve to newer package code. + +## Review Boundary + +Default to the repository or workspace the user asked about. Do not inspect home-directory or machine-wide MCP configuration unless the user explicitly requests that broader scope. + +Do not: + +- run a `command` or `args` value found in MCP configuration; +- start an MCP server to confirm a static finding; +- install or resolve a referenced package merely to classify its version selector; +- copy credentials, headers, tokens, or secret values into the report; +- describe dependency mutability alone as proof of a vulnerability, compromise, or malicious package. + +## Static Classification Rules + +For npm/npx-style package selectors, classify the package reference itself: + +| Selector | Result | Why | +| --- | --- | --- | +| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package` | HIGH | A future resolution can select different package code. | +| `@scope/package` | HIGH | Scoped bare package is still mutable. | +| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@^1.2.0` | MEDIUM | Resolution can move within the range. | +| `package@~1.2.0` | MEDIUM | Resolution can move within the range. | +| wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | +| local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | + +Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. + +## Review Workflow + +### 1. Locate repo-scoped configuration + +Check only relevant workspace paths first, for example: + +```text +.mcp.json +.github/mcp.json +.cursor/mcp.json +.vscode/mcp.json +.windsurf/mcp.json +``` + +Also inspect another MCP config path when the user names it explicitly. + +### 2. Parse without executing + +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. + +Never execute the discovered command to learn what it does. + +### 3. Classify the selector + +Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. + +### 4. Recommend a reproducible fix + +For mutable selectors, recommend an exact package version that the team has actually reviewed. + +Do **not** invent a pin by substituting today's latest registry version. If the reviewed version is unknown, say so. A registry-history lookup is a separate network operation and should only be performed when the user asks for it. + +### 5. Produce a bounded report + +Use a compact table: + +| Config | MCP server | Package/reference | Result | Why | Next step | +| --- | --- | --- | --- | --- | --- | + +End with these boundaries: + +- No MCP servers were executed during this review. +- Mutable dependency references are reproducibility/review signals, not breach claims. +- A clean result here is not a complete MCP security assessment. + +## Example + +Given: + +```json +{ + "mcpServers": { + "browser": { + "command": "npx", + "args": ["-y", "example-browser-mcp@latest"] + } + } +} +``` + +Report: + +```text +.mcp.json | browser | example-browser-mcp@latest | HIGH +Reason: @latest can resolve to different package code later without a config diff. +Next: pin the exact version the team reviews and update it deliberately. +``` + +## Anti-Patterns + +### Calling every mutable reference a vulnerability + +**Wrong:** `@latest` means the MCP package is compromised. + +**Better:** `@latest` means the configuration does not fully determine which package version will run later. Establish actual security impact separately. + +### Pinning whatever is latest today + +**Wrong:** replace a mutable selector with the current registry version and call the review fixed. + +**Better:** pin a version the team has reviewed. If that evidence is unavailable, record the uncertainty. + +### Expanding scope silently + +**Wrong:** a repo review automatically scans user-level Claude, Cursor, or VS Code configuration. + +**Better:** remain workspace-scoped unless machine-wide review is explicitly requested. + +### Treating a clean drift review as complete MCP security + +Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. + +## Related Skills + +- `mcp-server-patterns` — MCP server design, tools, resources, prompts, and transports. +- `security-review` — broader application-security checklist. +- `security-scan` — broader security scanning workflow. + +## Further Reading + +A public reference implementation and reproducible research methodology for this narrow review class are available in [MCP Drift Check](https://github.com/tomelias10/mcp-drift-check). The external project is optional; this ECC skill does not require it to perform the static review. diff --git a/AGENTS.md b/AGENTS.md index 17330b848..c6c5acd98 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 68 specialized agents, 292 skills, 94 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 68 specialized agents, 293 skills, 94 commands, and automated hook workflows for software development. **Version:** 2.2.2 @@ -154,7 +154,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 68 specialized subagents -skills/ — 292 workflow skills and domain knowledge +skills/ — 293 workflow skills and domain knowledge commands/ — 94 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index 117552c2b..ef7ad4249 100644 --- a/README.md +++ b/README.md @@ -138,12 +138,12 @@ Instead of rebuilding that process in every prompt, you install it once and make ECC is MIT-licensed open source. It works best with Claude Code today, has a supported Codex sync path, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity. -Access to 68 agents, 292 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. +Access to 68 agents, 293 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. | Included | Count | What it gives you | | ---------------- | ----------: | ------------------------------------------------------------------------------------ | | Agents | 68 agents | Planning, review, build repair, security, architecture, and domain work | -| Skills | 292 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | +| Skills | 293 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | | Commands | 94 commands | Convenient entry points while ECC moves to a skills-first surface | | Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls | | Rules | Selective | Always-loaded standards you choose by language or project | diff --git a/README.zh-CN.md b/README.zh-CN.md index 552d69b58..e8aacbbd9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -196,7 +196,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 68 个代理、292 个技能和 94 个命令。 +**完成!** 你现在可以使用 68 个代理、293 个技能和 94 个命令。 ### multi-* 命令需要额外配置 diff --git a/agent.yaml b/agent.yaml index 4236f04cc..0d1f154cc 100644 --- a/agent.yaml +++ b/agent.yaml @@ -99,6 +99,7 @@ skills: - liquid-glass-design - logistics-exception-management - market-research + - mcp-dependency-review - mcp-server-patterns - motion-advanced - motion-foundations diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 31e1a3817..555599e2e 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、292 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、293 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.2.2 @@ -147,7 +147,7 @@ ``` agents/ — 68 个专业子代理 -skills/ — 292 个工作流技能和领域知识 +skills/ — 293 个工作流技能和领域知识 commands/ — 94 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 3228c6159..e5ed37377 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -260,7 +260,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 68 个智能体、292 项技能和 94 个命令了。 +**搞定!** 你现在可以使用 68 个智能体、293 项技能和 94 个命令了。 *** @@ -1174,7 +1174,7 @@ opencode |---------|---------------|----------|--------| | 智能体 | PASS: 68 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 94 个 | PASS: 35 个 | **Claude Code 领先** | -| 技能 | PASS: 292 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 293 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1282,7 +1282,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|-----------------------|------------|-----------|----------| | **智能体** | 68 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 94 | 共享 | 基于指令 | 35 | -| **技能** | 292 | 共享 | 10 (原生格式) | 37 | +| **技能** | 293 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/manifests/install-modules.json b/manifests/install-modules.json index 884c7d39d..369105c46 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -430,6 +430,7 @@ "skills/hipaa-compliance", "skills/laravel-security", "skills/llm-trading-agent-security", + "skills/mcp-dependency-review", "skills/nodejs-keccak256", "skills/perl-security", "skills/quarkus-security", diff --git a/package.json b/package.json index 6a53ed2f5..8bc66851f 100644 --- a/package.json +++ b/package.json @@ -278,6 +278,7 @@ "skills/manim-video/", "skills/market-research/", "skills/make-interfaces-feel-better/", + "skills/mcp-dependency-review/", "skills/mcp-server-patterns/", "skills/messages-ops/", "skills/mle-workflow/", diff --git a/skills/mcp-dependency-review/SKILL.md b/skills/mcp-dependency-review/SKILL.md new file mode 100644 index 000000000..8a06b520f --- /dev/null +++ b/skills/mcp-dependency-review/SKILL.md @@ -0,0 +1,151 @@ +--- +name: mcp-dependency-review +description: Statically review MCP configuration for mutable package references before approval or CI, without executing discovered MCP servers. Use when reviewing .mcp.json, Cursor, VS Code, Claude, Windsurf, or other repo-scoped MCP config. +metadata: + origin: ECC +--- + +# MCP Dependency Review + +Use this skill to review MCP configuration for package references that can resolve to different code after the configuration itself was approved. + +This is a **static review workflow**. Read configuration as text/JSON only. Do not execute discovered MCP server commands as part of the review. + +## When to Activate + +- Before approving a new or changed MCP configuration. +- When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. +- When adding a deterministic MCP configuration check to CI. +- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When a team wants to know whether a previously reviewed config can silently resolve to newer package code. + +## Review Boundary + +Default to the repository or workspace the user asked about. Do not inspect home-directory or machine-wide MCP configuration unless the user explicitly requests that broader scope. + +Do not: + +- run a `command` or `args` value found in MCP configuration; +- start an MCP server to confirm a static finding; +- install or resolve a referenced package merely to classify its version selector; +- copy credentials, headers, tokens, or secret values into the report; +- describe dependency mutability alone as proof of a vulnerability, compromise, or malicious package. + +## Static Classification Rules + +For npm/npx-style package selectors, classify the package reference itself: + +| Selector | Result | Why | +| --- | --- | --- | +| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package` | HIGH | A future resolution can select different package code. | +| `@scope/package` | HIGH | Scoped bare package is still mutable. | +| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@^1.2.0` | MEDIUM | Resolution can move within the range. | +| `package@~1.2.0` | MEDIUM | Resolution can move within the range. | +| wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | +| local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | + +Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. + +## Review Workflow + +### 1. Locate repo-scoped configuration + +Check only relevant workspace paths first, for example: + +```text +.mcp.json +.github/mcp.json +.cursor/mcp.json +.vscode/mcp.json +.windsurf/mcp.json +``` + +Also inspect another MCP config path when the user names it explicitly. + +### 2. Parse without executing + +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. + +Never execute the discovered command to learn what it does. + +### 3. Classify the selector + +Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. + +### 4. Recommend a reproducible fix + +For mutable selectors, recommend an exact package version that the team has actually reviewed. + +Do **not** invent a pin by substituting today's latest registry version. If the reviewed version is unknown, say so. A registry-history lookup is a separate network operation and should only be performed when the user asks for it. + +### 5. Produce a bounded report + +Use a compact table: + +| Config | MCP server | Package/reference | Result | Why | Next step | +| --- | --- | --- | --- | --- | --- | + +End with these boundaries: + +- No MCP servers were executed during this review. +- Mutable dependency references are reproducibility/review signals, not breach claims. +- A clean result here is not a complete MCP security assessment. + +## Example + +Given: + +```json +{ + "mcpServers": { + "browser": { + "command": "npx", + "args": ["-y", "example-browser-mcp@latest"] + } + } +} +``` + +Report: + +```text +.mcp.json | browser | example-browser-mcp@latest | HIGH +Reason: @latest can resolve to different package code later without a config diff. +Next: pin the exact version the team reviews and update it deliberately. +``` + +## Anti-Patterns + +### Calling every mutable reference a vulnerability + +**Wrong:** `@latest` means the MCP package is compromised. + +**Better:** `@latest` means the configuration does not fully determine which package version will run later. Establish actual security impact separately. + +### Pinning whatever is latest today + +**Wrong:** replace a mutable selector with the current registry version and call the review fixed. + +**Better:** pin a version the team has reviewed. If that evidence is unavailable, record the uncertainty. + +### Expanding scope silently + +**Wrong:** a repo review automatically scans user-level Claude, Cursor, or VS Code configuration. + +**Better:** remain workspace-scoped unless machine-wide review is explicitly requested. + +### Treating a clean drift review as complete MCP security + +Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. + +## Related Skills + +- `mcp-server-patterns` — MCP server design, tools, resources, prompts, and transports. +- `security-review` — broader application-security checklist. +- `security-scan` — broader security scanning workflow. + +## Further Reading + +A public reference implementation and reproducible research methodology for this narrow review class are available in [MCP Drift Check](https://github.com/tomelias10/mcp-drift-check). The external project is optional; this ECC skill does not require it to perform the static review. From 8f6b4d613325b51f69762b17c0b20e2feed7876e Mon Sep 17 00:00:00 2001 From: Tomelias Date: Fri, 25 Sep 2026 13:08:55 +0300 Subject: [PATCH 2/8] test(skills): add MCP dependency review regression coverage --- tests/skills/mcp-dependency-review.test.js | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/skills/mcp-dependency-review.test.js diff --git a/tests/skills/mcp-dependency-review.test.js b/tests/skills/mcp-dependency-review.test.js new file mode 100644 index 000000000..7aac1fee4 --- /dev/null +++ b/tests/skills/mcp-dependency-review.test.js @@ -0,0 +1,81 @@ +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const repoRoot = path.resolve(__dirname, '..', '..'); +const canonicalPath = path.join(repoRoot, 'skills', 'mcp-dependency-review', 'SKILL.md'); +const codexPath = path.join(repoRoot, '.agents', 'skills', 'mcp-dependency-review', 'SKILL.md'); +const cursorPath = path.join(repoRoot, '.cursor', 'skills', 'mcp-dependency-review', 'SKILL.md'); + +let passed = 0; +let failed = 0; + +function test(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + passed += 1; + } catch (error) { + console.log(` ✗ ${name}`); + console.log(` Error: ${error.message}`); + failed += 1; + } +} + +function body(markdown) { + return markdown.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '').trim(); +} + +const canonical = fs.readFileSync(canonicalPath, 'utf8'); +const codex = fs.readFileSync(codexPath, 'utf8'); +const cursor = fs.readFileSync(cursorPath, 'utf8'); + +console.log('\n=== MCP dependency review skill tests ===\n'); +test('has a focused activation surface for MCP config review', () => { + const frontmatter = canonical.match(/^---\n([\s\S]*?)\n---/); + assert.ok(frontmatter, 'SKILL.md frontmatter is missing'); + assert.match(frontmatter[1], /name:\s*mcp-dependency-review/); + assert.match(frontmatter[1], /description:.*statically review MCP configuration/i); + assert.match(canonical, /## When to Activate/); + assert.match(canonical, /\.mcp\.json/); +}); + +test('keeps a zero-execution and bounded-scope security boundary', () => { + assert.match(canonical, /Do not execute discovered MCP server commands/i); + assert.match(canonical, /Do not inspect home-directory or machine-wide MCP configuration unless the user explicitly requests/i); + assert.match(canonical, /do not:\s*[\s\S]*run a `command` or `args` value/i); + assert.match(canonical, /never execute the discovered command/i); + assert.match(canonical, /No MCP servers were executed during this review/i); +}); + +test('classifies mutable selectors without treating them as compromise evidence', () => { + assert.match(canonical, /`package@1\.2\.3`\s*\| SAFE/); + assert.match(canonical, /`package`\s*\| HIGH/); + assert.match(canonical, /`package@latest`\s*\| HIGH/); + assert.match(canonical, /`package@\^1\.2\.0`\s*\| MEDIUM/); + assert.match(canonical, /local path \/ script \/ unknown binary\s*\| REVIEW/); + assert.match(canonical, /not breach claims/i); + assert.match(canonical, /describe dependency mutability alone as proof of a vulnerability, compromise, or malicious package/i); +}); +test('does not recommend substituting an arbitrary current latest version', () => { + assert.match(canonical, /Do \*\*not\*\* invent a pin by substituting today's latest registry version/i); + assert.match(canonical, /pin a version the team has reviewed/i); + assert.match(canonical, /registry-history lookup is a separate network operation/i); +}); + +test('keeps the external reference optional rather than making it a dependency', () => { + assert.match(canonical, /external project is optional/i); + assert.match(canonical, /does not require it to perform the static review/i); +}); + +test('keeps the same review workflow across ECC, Codex, and Cursor surfaces', () => { + assert.strictEqual(body(codex), body(canonical)); + assert.strictEqual(body(cursor), body(canonical)); + assert.match(codex, /license:\s*MIT/); + assert.match(cursor, /origin:\s*ECC/); +}); + +console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); +process.exit(failed > 0 ? 1 : 0); From 1d2b7205af95aa91d6dd2415e4ae9855deca57d7 Mon Sep 17 00:00:00 2001 From: Tomelias Date: Fri, 25 Sep 2026 13:11:30 +0300 Subject: [PATCH 3/8] docs(security): add AgentShield evidence for MCP review skill --- .../mcp-dependency-review-agentshield.md | 26 +++++++++++++++ .../mcp-dependency-review-agentshield.sarif | 33 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 docs/security/evidence/mcp-dependency-review-agentshield.md create mode 100644 docs/security/evidence/mcp-dependency-review-agentshield.sarif diff --git a/docs/security/evidence/mcp-dependency-review-agentshield.md b/docs/security/evidence/mcp-dependency-review-agentshield.md new file mode 100644 index 000000000..347e0a3fc --- /dev/null +++ b/docs/security/evidence/mcp-dependency-review-agentshield.md @@ -0,0 +1,26 @@ +# MCP Dependency Review — AgentShield Evidence + +Generated for PR validation on 2026-09-25 using ECC's published AgentShield CLI. + +Scope: `.agents/skills/mcp-dependency-review` + +Command shape: + +```bash +npx --yes ecc-agentshield scan \ + --path .agents/skills/mcp-dependency-review \ + --format json \ + --evidence-pack +``` + +No active prompt-injection testing, sandbox execution, deep analysis, or auto-fix mode was enabled. + +Result: + +- AgentShield exit code: `0` +- files scanned: `1` +- security score: `100` +- grade: `A` +- SARIF findings: `0` + +The committed SARIF is the redacted `agentshield-results.sarif` artifact produced by the evidence pack. AgentShield replaced the local scan root with ``; no local username, absolute home path, credential, or token is included. diff --git a/docs/security/evidence/mcp-dependency-review-agentshield.sarif b/docs/security/evidence/mcp-dependency-review-agentshield.sarif new file mode 100644 index 000000000..899786f97 --- /dev/null +++ b/docs/security/evidence/mcp-dependency-review-agentshield.sarif @@ -0,0 +1,33 @@ +{ + "version": "2.1.0", + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "name": "AgentShield", + "informationUri": "https://github.com/affaan-m/agentshield", + "rules": [] + } + }, + "automationDetails": { + "id": "agentshield/security-scan" + }, + "invocations": [ + { + "executionSuccessful": true, + "endTimeUtc": "2026-09-25T10:10:50.216Z", + "workingDirectory": { + "uri": "" + } + } + ], + "properties": { + "score": 100, + "grade": "A", + "filesScanned": 1 + }, + "results": [] + } + ] +} From 8f895f47295a275b10b20a0f01a17d6e3dc4be80 Mon Sep 17 00:00:00 2001 From: ELIAS TOM Date: Sat, 26 Sep 2026 00:50:53 +0300 Subject: [PATCH 4/8] docs: address MCP dependency review feedback --- skills/mcp-dependency-review/SKILL.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/skills/mcp-dependency-review/SKILL.md b/skills/mcp-dependency-review/SKILL.md index 8a06b520f..aabe8a733 100644 --- a/skills/mcp-dependency-review/SKILL.md +++ b/skills/mcp-dependency-review/SKILL.md @@ -16,7 +16,7 @@ This is a **static review workflow**. Read configuration as text/JSON only. Do n - Before approving a new or changed MCP configuration. - When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. - When adding a deterministic MCP configuration check to CI. -- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When an MCP package reference uses `@latest`, another npm distribution tag, a bare package name, or a version range. - When a team wants to know whether a previously reviewed config can silently resolve to newer package code. ## Review Boundary @@ -37,10 +37,11 @@ For npm/npx-style package selectors, classify the package reference itself: | Selector | Result | Why | | --- | --- | --- | -| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package@1.2.3` | SAFE | The direct package selector is exact. This does not prove the full dependency tree is reproducible without a lockfile or equivalent integrity controls. | | `package` | HIGH | A future resolution can select different package code. | | `@scope/package` | HIGH | Scoped bare package is still mutable. | -| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@latest` | HIGH | The selector is an npm distribution tag and is explicitly mutable. | +| `package@beta`, `package@next`, or another distribution tag | HIGH | npm distribution tags can be moved to different package versions. | | `package@^1.2.0` | MEDIUM | Resolution can move within the range. | | `package@~1.2.0` | MEDIUM | Resolution can move within the range. | | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | @@ -52,7 +53,7 @@ Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it ### 1. Locate repo-scoped configuration -Check only relevant workspace paths first, for example: +Check common workspace paths first, for example: ```text .mcp.json @@ -60,19 +61,23 @@ Check only relevant workspace paths first, for example: .cursor/mcp.json .vscode/mcp.json .windsurf/mcp.json +.kiro/settings/mcp.json +.kiro/settings/mcp.json.example ``` -Also inspect another MCP config path when the user names it explicitly. +Then perform bounded discovery inside the requested repository/workspace for equivalent tracked MCP configuration files and examples. Stay inside the requested workspace; do not silently expand into home-directory or machine-wide configuration. Also inspect another MCP config path when the user names it explicitly. ### 2. Parse without executing -Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate every package selector from command-line flags. + +Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. Never execute the discovered command to learn what it does. ### 3. Classify the selector -Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. +Apply the static classification table above. Treat any npm distribution tag, not only `latest`, as HIGH. If the syntax is ambiguous, return REVIEW rather than guessing. ### 4. Recommend a reproducible fix @@ -91,6 +96,7 @@ End with these boundaries: - No MCP servers were executed during this review. - Mutable dependency references are reproducibility/review signals, not breach claims. +- `SAFE` means the direct selector is exact; it does not establish full transitive dependency reproducibility. - A clean result here is not a complete MCP security assessment. ## Example @@ -138,7 +144,7 @@ Next: pin the exact version the team reviews and update it deliberately. ### Treating a clean drift review as complete MCP security -Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. +Exact direct dependency pins do not prove full transitive reproducibility, safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. ## Related Skills From 103758761951697768852a7136beb6569418c405 Mon Sep 17 00:00:00 2001 From: ELIAS TOM Date: Sat, 26 Sep 2026 00:51:13 +0300 Subject: [PATCH 5/8] docs: sync MCP review guidance --- .agents/skills/mcp-dependency-review/SKILL.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.agents/skills/mcp-dependency-review/SKILL.md b/.agents/skills/mcp-dependency-review/SKILL.md index 86ac921c0..a6a19c601 100644 --- a/.agents/skills/mcp-dependency-review/SKILL.md +++ b/.agents/skills/mcp-dependency-review/SKILL.md @@ -15,7 +15,7 @@ This is a **static review workflow**. Read configuration as text/JSON only. Do n - Before approving a new or changed MCP configuration. - When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. - When adding a deterministic MCP configuration check to CI. -- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When an MCP package reference uses `@latest`, another npm distribution tag, a bare package name, or a version range. - When a team wants to know whether a previously reviewed config can silently resolve to newer package code. ## Review Boundary @@ -36,10 +36,11 @@ For npm/npx-style package selectors, classify the package reference itself: | Selector | Result | Why | | --- | --- | --- | -| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package@1.2.3` | SAFE | The direct package selector is exact. This does not prove the full dependency tree is reproducible without a lockfile or equivalent integrity controls. | | `package` | HIGH | A future resolution can select different package code. | | `@scope/package` | HIGH | Scoped bare package is still mutable. | -| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@latest` | HIGH | The selector is an npm distribution tag and is explicitly mutable. | +| `package@beta`, `package@next`, or another distribution tag | HIGH | npm distribution tags can be moved to different package versions. | | `package@^1.2.0` | MEDIUM | Resolution can move within the range. | | `package@~1.2.0` | MEDIUM | Resolution can move within the range. | | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | @@ -51,7 +52,7 @@ Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it ### 1. Locate repo-scoped configuration -Check only relevant workspace paths first, for example: +Check common workspace paths first, for example: ```text .mcp.json @@ -59,19 +60,23 @@ Check only relevant workspace paths first, for example: .cursor/mcp.json .vscode/mcp.json .windsurf/mcp.json +.kiro/settings/mcp.json +.kiro/settings/mcp.json.example ``` -Also inspect another MCP config path when the user names it explicitly. +Then perform bounded discovery inside the requested repository/workspace for equivalent tracked MCP configuration files and examples. Stay inside the requested workspace; do not silently expand into home-directory or machine-wide configuration. Also inspect another MCP config path when the user names it explicitly. ### 2. Parse without executing -Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate every package selector from command-line flags. + +Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. Never execute the discovered command to learn what it does. ### 3. Classify the selector -Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. +Apply the static classification table above. Treat any npm distribution tag, not only `latest`, as HIGH. If the syntax is ambiguous, return REVIEW rather than guessing. ### 4. Recommend a reproducible fix @@ -90,6 +95,7 @@ End with these boundaries: - No MCP servers were executed during this review. - Mutable dependency references are reproducibility/review signals, not breach claims. +- `SAFE` means the direct selector is exact; it does not establish full transitive dependency reproducibility. - A clean result here is not a complete MCP security assessment. ## Example @@ -137,7 +143,7 @@ Next: pin the exact version the team reviews and update it deliberately. ### Treating a clean drift review as complete MCP security -Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. +Exact direct dependency pins do not prove full transitive reproducibility, safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. ## Related Skills From 3128610f7cff2c0ec38de205f43921129027b3d7 Mon Sep 17 00:00:00 2001 From: ELIAS TOM Date: Sat, 26 Sep 2026 00:52:26 +0300 Subject: [PATCH 6/8] docs: sync Cursor MCP review guidance --- .cursor/skills/mcp-dependency-review/SKILL.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.cursor/skills/mcp-dependency-review/SKILL.md b/.cursor/skills/mcp-dependency-review/SKILL.md index fba6f89ae..47eab7091 100644 --- a/.cursor/skills/mcp-dependency-review/SKILL.md +++ b/.cursor/skills/mcp-dependency-review/SKILL.md @@ -15,7 +15,7 @@ This is a **static review workflow**. Read configuration as text/JSON only. Do n - Before approving a new or changed MCP configuration. - When reviewing `.mcp.json`, `.github/mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, or equivalent workspace configuration. - When adding a deterministic MCP configuration check to CI. -- When an MCP package reference uses `@latest`, a bare package name, or a version range. +- When an MCP package reference uses `@latest`, another npm distribution tag, a bare package name, or a version range. - When a team wants to know whether a previously reviewed config can silently resolve to newer package code. ## Review Boundary @@ -36,10 +36,11 @@ For npm/npx-style package selectors, classify the package reference itself: | Selector | Result | Why | | --- | --- | --- | -| `package@1.2.3` | SAFE | Exact semantic version is reproducible. | +| `package@1.2.3` | SAFE | The direct package selector is exact. This does not prove the full dependency tree is reproducible without a lockfile or equivalent integrity controls. | | `package` | HIGH | A future resolution can select different package code. | | `@scope/package` | HIGH | Scoped bare package is still mutable. | -| `package@latest` | HIGH | The selector is explicitly mutable. | +| `package@latest` | HIGH | The selector is an npm distribution tag and is explicitly mutable. | +| `package@beta`, `package@next`, or another distribution tag | HIGH | npm distribution tags can be moved to different package versions. | | `package@^1.2.0` | MEDIUM | Resolution can move within the range. | | `package@~1.2.0` | MEDIUM | Resolution can move within the range. | | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | @@ -51,7 +52,7 @@ Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it ### 1. Locate repo-scoped configuration -Check only relevant workspace paths first, for example: +Check common workspace paths first, for example: ```text .mcp.json @@ -59,19 +60,23 @@ Check only relevant workspace paths first, for example: .cursor/mcp.json .vscode/mcp.json .windsurf/mcp.json +.kiro/settings/mcp.json +.kiro/settings/mcp.json.example ``` -Also inspect another MCP config path when the user names it explicitly. +Then perform bounded discovery inside the requested repository/workspace for equivalent tracked MCP configuration files and examples. Stay inside the requested workspace; do not silently expand into home-directory or machine-wide configuration. Also inspect another MCP config path when the user names it explicitly. ### 2. Parse without executing -Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate the package selector from command-line flags. +Read JSON or configuration text and identify each configured MCP server. For package-runner invocations such as `npx`, `npm exec`, `bunx`, `bun x`, `pnpm dlx`, or `yarn dlx`, isolate every package selector from command-line flags. + +Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. Never execute the discovered command to learn what it does. ### 3. Classify the selector -Apply the static classification table above. If the syntax is ambiguous, return REVIEW rather than guessing. +Apply the static classification table above. Treat any npm distribution tag, not only `latest`, as HIGH. If the syntax is ambiguous, return REVIEW rather than guessing. ### 4. Recommend a reproducible fix @@ -90,6 +95,7 @@ End with these boundaries: - No MCP servers were executed during this review. - Mutable dependency references are reproducibility/review signals, not breach claims. +- `SAFE` means the direct selector is exact; it does not establish full transitive dependency reproducibility. - A clean result here is not a complete MCP security assessment. ## Example @@ -137,7 +143,7 @@ Next: pin the exact version the team reviews and update it deliberately. ### Treating a clean drift review as complete MCP security -Exact dependency pins do not prove safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. +Exact direct dependency pins do not prove full transitive reproducibility, safe authorization, prompt-injection resistance, package provenance, secure implementation, or runtime isolation. ## Related Skills From 85960fd95b6dcba21a99ad474c28a2f062750999 Mon Sep 17 00:00:00 2001 From: ELIAS TOM Date: Sat, 26 Sep 2026 00:54:18 +0300 Subject: [PATCH 7/8] docs(security): pin AgentShield evidence version --- docs/security/evidence/mcp-dependency-review-agentshield.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/security/evidence/mcp-dependency-review-agentshield.md b/docs/security/evidence/mcp-dependency-review-agentshield.md index 347e0a3fc..ff510537c 100644 --- a/docs/security/evidence/mcp-dependency-review-agentshield.md +++ b/docs/security/evidence/mcp-dependency-review-agentshield.md @@ -1,13 +1,13 @@ # MCP Dependency Review — AgentShield Evidence -Generated for PR validation on 2026-09-25 using ECC's published AgentShield CLI. +Generated for PR validation on 2026-09-25 using `ecc-agentshield@1.6.0`. Version 1.6.0 was already the published npm release when this evidence was generated; the reproduction command is pinned so a later publish cannot change the scanner code used. Scope: `.agents/skills/mcp-dependency-review` Command shape: ```bash -npx --yes ecc-agentshield scan \ +npx --yes ecc-agentshield@1.6.0 scan \ --path .agents/skills/mcp-dependency-review \ --format json \ --evidence-pack @@ -17,6 +17,7 @@ No active prompt-injection testing, sandbox execution, deep analysis, or auto-fi Result: +- AgentShield version: `1.6.0` - AgentShield exit code: `0` - files scanned: `1` - security score: `100` From 05c6b017e10e972615aa45eee373004a30d3d5e6 Mon Sep 17 00:00:00 2001 From: Tom Elias <182494385+tomelias10@users.noreply.github.com> Date: Sun, 4 Oct 2026 09:48:28 +0300 Subject: [PATCH 8/8] fix: cover Docker and Python MCP dependency selectors --- .agents/skills/mcp-dependency-review/SKILL.md | 22 +++++++++++++++++- .cursor/skills/mcp-dependency-review/SKILL.md | 22 +++++++++++++++++- skills/mcp-dependency-review/SKILL.md | 22 +++++++++++++++++- tests/skills/mcp-dependency-review.test.js | 23 +++++++++++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/.agents/skills/mcp-dependency-review/SKILL.md b/.agents/skills/mcp-dependency-review/SKILL.md index 6fc96a72e..a8c530596 100644 --- a/.agents/skills/mcp-dependency-review/SKILL.md +++ b/.agents/skills/mcp-dependency-review/SKILL.md @@ -32,7 +32,7 @@ Do not: ## Static Classification Rules -For npm/npx-style package selectors, classify the package reference itself: +For npm/npx-style package selectors, Python package selectors, and Docker image references, classify the direct reference: | Selector | Result | Why | | --- | --- | --- | @@ -46,6 +46,20 @@ For npm/npx-style package selectors, classify the package reference itself: | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | | local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | +For Docker and Python references, apply these additional outcomes: + +| Selector | Result | Why | +| --- | --- | --- | +| Docker image pinned by `@sha256:DIGEST` | SAFE | A well-formed full SHA-256 digest fixes the direct image content, including when a tag is also present. | +| Docker image with a tag or no tag and no digest | HIGH | All tags, including version-looking tags and the default `latest`, can move to different image content. | +| Python exact version (`package==1.2.3` or `uvx package@1.2.3`) | SAFE | The direct version selector is exact; wildcard equality is not an exact pin. | +| Python package without a version or `uvx package@latest` | HIGH | Future resolution can select different package code. | +| Python version range or wildcard (`package>=1.2,<2`, `package~=1.2`, `package==1.*`) | MEDIUM | The selector permits more than one version. | +| Python editable / direct URL / VCS reference | REVIEW | These forms need separate source and integrity review; do not infer safety from a URL or commit-looking suffix. | +| Unsupported or ambiguous Docker / Python invocation or selector | REVIEW | Record the invocation even when its selected reference cannot be isolated confidently. | + +For every runner, `SAFE` applies only to the direct selector or image digest. It does not establish package integrity, provenance, or full transitive dependency reproducibility; it is not a complete security verdict. + Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. ## Review Workflow @@ -72,6 +86,12 @@ Read JSON or configuration text and identify each configured MCP server. For pac Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. +For `docker run` and `docker container run`, isolate the image separately from Docker options and the in-container command. For example, `docker run --rm -i example/server:1.2.3 serve` selects `example/server:1.2.3`, not `serve`. Account for option values before the image; do not treat a volume, environment value, or option argument as the image. Inspect an explicit image field in equivalent configuration as an image reference too. + +For `uvx` (including `uv tool run`), classify the package from `--from`; for `pipx run`, classify the package from `--spec`. For example, `uvx --from example-tool==1.2.3 example-command` and `pipx run --spec example-tool==1.2.3 example-command` both select `example-tool==1.2.3`; `example-command` is the executable. Accept both separated flag values and `--from=SPEC` / `--spec=SPEC` forms. Without those flags, isolate the tool/package selector (`uvx example-tool@1.2.3` or `pipx run example-tool`) from subsequent executable arguments. Review additional package-bearing options such as uv's `--with` separately when their syntax is clear. + +If the invocation, option boundaries, or selector syntax is unsupported or ambiguous, report REVIEW and never omit the configured server, guess a selected package, or execute the command to resolve uncertainty. Editable installs, direct URLs, VCS sources, shell wrappers, and uncertain executable-to-package mappings require REVIEW. + Never execute the discovered command to learn what it does. ### 3. Classify the selector diff --git a/.cursor/skills/mcp-dependency-review/SKILL.md b/.cursor/skills/mcp-dependency-review/SKILL.md index cc3162ad3..5d6e2343a 100644 --- a/.cursor/skills/mcp-dependency-review/SKILL.md +++ b/.cursor/skills/mcp-dependency-review/SKILL.md @@ -32,7 +32,7 @@ Do not: ## Static Classification Rules -For npm/npx-style package selectors, classify the package reference itself: +For npm/npx-style package selectors, Python package selectors, and Docker image references, classify the direct reference: | Selector | Result | Why | | --- | --- | --- | @@ -46,6 +46,20 @@ For npm/npx-style package selectors, classify the package reference itself: | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | | local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | +For Docker and Python references, apply these additional outcomes: + +| Selector | Result | Why | +| --- | --- | --- | +| Docker image pinned by `@sha256:DIGEST` | SAFE | A well-formed full SHA-256 digest fixes the direct image content, including when a tag is also present. | +| Docker image with a tag or no tag and no digest | HIGH | All tags, including version-looking tags and the default `latest`, can move to different image content. | +| Python exact version (`package==1.2.3` or `uvx package@1.2.3`) | SAFE | The direct version selector is exact; wildcard equality is not an exact pin. | +| Python package without a version or `uvx package@latest` | HIGH | Future resolution can select different package code. | +| Python version range or wildcard (`package>=1.2,<2`, `package~=1.2`, `package==1.*`) | MEDIUM | The selector permits more than one version. | +| Python editable / direct URL / VCS reference | REVIEW | These forms need separate source and integrity review; do not infer safety from a URL or commit-looking suffix. | +| Unsupported or ambiguous Docker / Python invocation or selector | REVIEW | Record the invocation even when its selected reference cannot be isolated confidently. | + +For every runner, `SAFE` applies only to the direct selector or image digest. It does not establish package integrity, provenance, or full transitive dependency reproducibility; it is not a complete security verdict. + Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. ## Review Workflow @@ -72,6 +86,12 @@ Read JSON or configuration text and identify each configured MCP server. For pac Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. +For `docker run` and `docker container run`, isolate the image separately from Docker options and the in-container command. For example, `docker run --rm -i example/server:1.2.3 serve` selects `example/server:1.2.3`, not `serve`. Account for option values before the image; do not treat a volume, environment value, or option argument as the image. Inspect an explicit image field in equivalent configuration as an image reference too. + +For `uvx` (including `uv tool run`), classify the package from `--from`; for `pipx run`, classify the package from `--spec`. For example, `uvx --from example-tool==1.2.3 example-command` and `pipx run --spec example-tool==1.2.3 example-command` both select `example-tool==1.2.3`; `example-command` is the executable. Accept both separated flag values and `--from=SPEC` / `--spec=SPEC` forms. Without those flags, isolate the tool/package selector (`uvx example-tool@1.2.3` or `pipx run example-tool`) from subsequent executable arguments. Review additional package-bearing options such as uv's `--with` separately when their syntax is clear. + +If the invocation, option boundaries, or selector syntax is unsupported or ambiguous, report REVIEW and never omit the configured server, guess a selected package, or execute the command to resolve uncertainty. Editable installs, direct URLs, VCS sources, shell wrappers, and uncertain executable-to-package mappings require REVIEW. + Never execute the discovered command to learn what it does. ### 3. Classify the selector diff --git a/skills/mcp-dependency-review/SKILL.md b/skills/mcp-dependency-review/SKILL.md index 8f00fea77..4b08af8b0 100644 --- a/skills/mcp-dependency-review/SKILL.md +++ b/skills/mcp-dependency-review/SKILL.md @@ -33,7 +33,7 @@ Do not: ## Static Classification Rules -For npm/npx-style package selectors, classify the package reference itself: +For npm/npx-style package selectors, Python package selectors, and Docker image references, classify the direct reference: | Selector | Result | Why | | --- | --- | --- | @@ -47,6 +47,20 @@ For npm/npx-style package selectors, classify the package reference itself: | wildcard / inequality / other range | MEDIUM | Selector permits more than one version. | | local path / script / unknown binary | REVIEW | Package-version drift rules do not establish its update behavior. | +For Docker and Python references, apply these additional outcomes: + +| Selector | Result | Why | +| --- | --- | --- | +| Docker image pinned by `@sha256:DIGEST` | SAFE | A well-formed full SHA-256 digest fixes the direct image content, including when a tag is also present. | +| Docker image with a tag or no tag and no digest | HIGH | All tags, including version-looking tags and the default `latest`, can move to different image content. | +| Python exact version (`package==1.2.3` or `uvx package@1.2.3`) | SAFE | The direct version selector is exact; wildcard equality is not an exact pin. | +| Python package without a version or `uvx package@latest` | HIGH | Future resolution can select different package code. | +| Python version range or wildcard (`package>=1.2,<2`, `package~=1.2`, `package==1.*`) | MEDIUM | The selector permits more than one version. | +| Python editable / direct URL / VCS reference | REVIEW | These forms need separate source and integrity review; do not infer safety from a URL or commit-looking suffix. | +| Unsupported or ambiguous Docker / Python invocation or selector | REVIEW | Record the invocation even when its selected reference cannot be isolated confidently. | + +For every runner, `SAFE` applies only to the direct selector or image digest. It does not establish package integrity, provenance, or full transitive dependency reproducibility; it is not a complete security verdict. + Treat `-y` / `--yes` only as context. It suppresses interactive confirmation; it is not a vulnerability by itself. ## Review Workflow @@ -73,6 +87,12 @@ Read JSON or configuration text and identify each configured MCP server. For pac Package-valued flags count as package selectors too. For example, in `npx --package ecc-universal ecc`, classify `ecc-universal` as the package selector and treat `ecc` as the executable. If multiple package-valued flags are present, review every supplied package selector. +For `docker run` and `docker container run`, isolate the image separately from Docker options and the in-container command. For example, `docker run --rm -i example/server:1.2.3 serve` selects `example/server:1.2.3`, not `serve`. Account for option values before the image; do not treat a volume, environment value, or option argument as the image. Inspect an explicit image field in equivalent configuration as an image reference too. + +For `uvx` (including `uv tool run`), classify the package from `--from`; for `pipx run`, classify the package from `--spec`. For example, `uvx --from example-tool==1.2.3 example-command` and `pipx run --spec example-tool==1.2.3 example-command` both select `example-tool==1.2.3`; `example-command` is the executable. Accept both separated flag values and `--from=SPEC` / `--spec=SPEC` forms. Without those flags, isolate the tool/package selector (`uvx example-tool@1.2.3` or `pipx run example-tool`) from subsequent executable arguments. Review additional package-bearing options such as uv's `--with` separately when their syntax is clear. + +If the invocation, option boundaries, or selector syntax is unsupported or ambiguous, report REVIEW and never omit the configured server, guess a selected package, or execute the command to resolve uncertainty. Editable installs, direct URLs, VCS sources, shell wrappers, and uncertain executable-to-package mappings require REVIEW. + Never execute the discovered command to learn what it does. ### 3. Classify the selector diff --git a/tests/skills/mcp-dependency-review.test.js b/tests/skills/mcp-dependency-review.test.js index 7aac1fee4..a88683367 100644 --- a/tests/skills/mcp-dependency-review.test.js +++ b/tests/skills/mcp-dependency-review.test.js @@ -59,6 +59,29 @@ test('classifies mutable selectors without treating them as compromise evidence' assert.match(canonical, /not breach claims/i); assert.match(canonical, /describe dependency mutability alone as proof of a vulnerability, compromise, or malicious package/i); }); +test('covers Docker and Python selectors without silently skipping ambiguous invocations', () => { + const rows = canonical.split('\n').filter(line => line.startsWith('| ')); + const cases = [ + ['Docker image pinned by', 'SAFE'], + ['Docker image with a tag or no tag and no digest', 'HIGH'], + ['Python exact version', 'SAFE'], + ['Python package without a version', 'HIGH'], + ['Python version range or wildcard', 'MEDIUM'], + ['Python editable / direct URL / VCS reference', 'REVIEW'], + ]; + for (const [selector, result] of cases) { + assert.ok(rows.some(row => row.includes(selector) && row.includes(`| ${result} |`)), + `${selector} must have an explicit ${result} classification`); + } + assert.match(canonical, /`docker run`.*`docker container run`/); + assert.match(canonical, /image separately from Docker options and the in-container command/i); + assert.match(canonical, /`uvx --from example-tool==1\.2\.3 example-command`/); + assert.match(canonical, /`pipx run --spec example-tool==1\.2\.3 example-command`/); + assert.match(canonical, /`--from=SPEC`.*`--spec=SPEC`/); + assert.match(canonical, /unsupported or ambiguous.*REVIEW.*never omit/i); + assert.match(canonical, /package integrity, provenance, or full transitive dependency reproducibility/i); +}); + test('does not recommend substituting an arbitrary current latest version', () => { assert.match(canonical, /Do \*\*not\*\* invent a pin by substituting today's latest registry version/i); assert.match(canonical, /pin a version the team has reviewed/i);