feat: MCP tool pinning — rug pull defense#81
Closed
Xaik89 wants to merge 4 commits intonode9-ai:mainfrom
Closed
Conversation
Automatically hashes MCP server tool definitions (name, description, inputSchema) on first connection and blocks if they change on subsequent connections. This defends against "rug pull" attacks where a trusted MCP server silently modifies tool descriptions to inject malicious instructions. - Replace child.stdout.pipe() with readline interceptor in MCP gateway to inspect tools/list responses before forwarding to the agent - SHA-256 hash of canonicalized tool definitions, sorted by name - Pin storage at ~/.node9/mcp-pins.json (atomic writes, mode 0o600) - On mismatch: return JSON-RPC -32000 error with clear remediation steps - CLI: node9 mcp pin list/update/reset for pin management - 20 unit tests (hashing, storage, pin lifecycle) - 5 integration tests (first pin, match, rug pull block, re-pin, transparency) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… review-and-approve update Addresses adversarial review findings: 1. Pin file reads fail closed: corrupt/unreadable pin files now throw instead of silently returning empty (which re-trusted the upstream). Only ENOENT is treated as "no pin exists." 2. Session quarantine: tools/call is blocked until a tools/list pin check passes. Mismatch or corrupt pin state permanently quarantines the session — no tool calls forwarded until the operator resolves it. 3. Pin update is now a review flow: `mcp pin update` spawns the upstream, fetches current tools, diffs old vs new definitions, and requires explicit operator confirmation before re-pinning. 4. README updated with MCP tool pinning section explaining the rug pull defense and CLI commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert mcp pin update to simple delete-and-repin. The review-and-approve flow (upstream fetch, diff display, confirmation prompt) adds ~170 lines and is a UX enhancement — not a security fix. Moving to a follow-up PR to keep this one focused on the two security hardening changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- pin list: uses readMcpPinsSafe() to show friendly error on corrupt file - pin update: catches corrupt file with recovery instructions - pin reset: works on corrupt files (clears without reading first) - README: fix stale comment about pin update reviewing diffs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
node9ai
added a commit
that referenced
this pull request
Apr 11, 2026
Co-authored-by: andreykh89 <andreykh89@users.noreply.github.com>
Contributor
|
Thanks great contribution, i added your MCP protect, very good idea! |
Contributor
|
BTW, iadded it, closing your because it did not pass the test |
10 tasks
3 tasks
Xaik89
added a commit
to Xaik89/node9-proxy
that referenced
this pull request
Apr 14, 2026
…AST 07) Extends the MCP tool pinning primitive (v1.5.0 / PR node9-ai#81) to agent skill repositories. On the first tool call of a session, SHA-256 hashes are recorded for every skill file in known roots (~/.claude/skills/, ~/.claude/CLAUDE.md, ~/.claude/rules/, project .claude/CLAUDE.md, .claude/CLAUDE.local.md, .claude/rules/, .cursor/rules/, AGENTS.md, CLAUDE.md). On subsequent sessions the hook re-verifies; any drift quarantines the session and blocks every tool call until a human reviews via `node9 skill pin update <rootKey>`. One feature, two threats covered in a single primitive — AST 02 Supply Chain Compromise and AST 07 Update Drift — at the skills layer that no competitor currently defends at runtime. Security properties: - Fail-closed on corrupt pin file - Symlink-safe (never follows symlinks out of the tree) - Size-capped at 5000 files / 50 MB per root - Path-traversal-safe session IDs (/^[A-Za-z0-9_-]{1,128}$/) - Atomic writes, mode 0o600 for ~/.node9/skill-pins.json and flags CLI: - `node9 skill pin list` — show pinned roots, hashes, file counts - `node9 skill pin update <rootKey> [--yes]` — diff + re-pin - `node9 skill pin reset` — clear pins AND wipe session flags Config: - `policy.skillRoots: string[]` extends the default root set (absolute, `~/`-prefixed, or cwd-relative; relative paths require absolute cwd). Tests (TDD, all green): - src/__tests__/skill-pin.unit.test.ts (36 tests) - src/__tests__/skill-pin-cli.integration.test.ts (7 tests, spawnSync) - src/__tests__/check-skill-pin.integration.test.ts (8 tests, spawnSync) - src/__tests__/skill-roots-config.spec.ts (4 tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
node9 mcp pin list/update/reset) for pin managementSplit out: review-and-approve
pin updateflow (upstream fetch + diff + confirm) deferred to follow-up PR to keep this focused on security.What is an MCP Rug Pull?
An MCP server exposes tools with
name,description, andinputSchema. The AI reads these descriptions to decide how to use tools. In a rug pull attack:send_emaildescription: "Send an email to the specified recipient"How it works
tools/listresponses from upstream~/.node9/mcp-pins.jsontools/callbeforetools/list→ blocked (client must verify first)node9 mcp pin update <key>removes pin so next connection re-pinsTest plan
npm run typecheck/lint/format:checkpass🤖 Generated with Claude Code