Skip to content

security: tool description pinning, injection scan, DNS rebinding default fix - #3754

Closed
Ablation-Tool wants to merge 1 commit into
modelcontextprotocol:mainfrom
Ablation-Tool:security/tool-desc-pinning-injection-scan
Closed

Ablation-Tool wants to merge 1 commit into
modelcontextprotocol:mainfrom
Ablation-Tool:security/tool-desc-pinning-injection-scan

Conversation

@Ablation-Tool

Copy link
Copy Markdown

Summary

This PR fixes three security issues identified during a full static audit of the Python SDK (128 files, ruff + pyright clean, 244 tests pass):

  • Rug-pull attack (CWE-345): _absorb_tool_listing() accepted re-fetched tool descriptions after a tools/list_changed notification with no integrity check. A malicious server can serve clean descriptions for N interactions, then send the notification and return poisoned descriptions on re-fetch. Fix: SHA-256 pin each description on first sight; any subsequent change drops the updated version and logs a security warning. Runs for all protocol versions (not gated on modern-only).

  • Tool description injection (CWE-20): _absorb_tool_listing() validates x-mcp-header annotations and output schemas but never inspects tool.description — the field sent verbatim to the LLM context. Fix: regex scan against known prompt-injection patterns before pinning; drops tools that match on first sight.

  • DNS rebinding protection off by default (CWE-16): TransportSecurityMiddleware defaulted to enable_dns_rebinding_protection=False for backwards compatibility. Fix: flip default to True; add a startup WARNING log when allowed_hosts is empty so misconfigured deployments fail visibly rather than silently rejecting all requests.

  • CWE-78 in CLI npx probe: subprocess.run([cmd, "--version"], shell=True) on Windows. cmd is currently hardcoded, but shell=True with a list is exploitable if ever made configurable. Fix: remove shell=True.

Changed files

File Change
src/mcp/client/session.py Add _INJECTION_PATTERNS, _tool_desc_hash(), _scan_tool_description(); security pass in _absorb_tool_listing()
src/mcp/server/transport_security.py Flip DNS rebinding default to True; add empty-allowed_hosts startup warning
src/mcp/cli/cli.py Remove shell=True from npx probe

Test plan

  • Existing test suite: pytest tests/client/ tests/transports/ tests/cli/ — 244 passed, 4 skipped (verified locally)
  • ruff check on all three files — clean
  • pyright on all three files — 0 errors, 0 warnings
  • New tests needed: rug-pull drop, injection drop, clean tool pass-through, legacy protocol also protected

Background

These vectors were originally documented as part of the DEADBUG-MCP research series (rug-pull V3, cross-server poisoning V4). The rug-pull fix described by MCP Playground ("pin the tool definitions you approved, call the server several times, list again, and treat any difference as a security event") is now implemented at the SDK level in _absorb_tool_listing().

Signed-off-by: Nicholas Michael Kloster nicholas@nuclide-research.com

🤖 Generated with Claude Code

…ault

Three security fixes identified via full static audit of the Python SDK:

1. client/session.py — add protocol-version-agnostic security pass to
   _absorb_tool_listing(): SHA-256 description pinning blocks rug-pull
   attacks (tools/list_changed re-fetch accepts poisoned descriptions
   silently); regex injection scan drops tools with prompt injection
   patterns on first sight.

2. server/transport_security.py — flip enable_dns_rebinding_protection
   default from False to True; add startup WARNING when allowed_hosts
   is empty so misconfigured deployments fail visibly, not silently.

3. cli/cli.py — remove shell=True from Windows npx probe subprocess
   call (CWE-78; cmd values are hardcoded today but shell=True with a
   list is exploitable if ever made configurable).

Signed-off-by: Nicholas Michael Kloster <nicholas@nuclide-research.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Oct 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and this PR doesn't link an open issue yet.

  • If you're already assigned to an issue for this, add Fixes #<n> to the description and the PR will reopen on its own.
  • If there's no issue yet, please open one instead: what you ran into, why it matters for your use case, and a minimal reproduction. That context is super important to us and is what we use to decide what to prioritise.
  • If there's an issue but you're not assigned, add Fixes #<n> anyway so they're linked, then engage on the issue itself by confirming the repro or describing the approach you'd take. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. If you are assigned, this PR reopens automatically.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way.

CONTRIBUTING.md has the full reasoning, but in short:

  • We're a small team with very little capacity to review community PRs right now.
  • Many recent PRs are AI-generated with little human review, and reviewing one carefully still costs a maintainer as much time as it ever did. A well-described issue is usually more useful to us than the code.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants