Skip to content

Harden fetch SSRF protection and git repo_path restriction - #4803

Open
sachajw wants to merge 1 commit into
modelcontextprotocol:mainfrom
sachajw:security/ssrf-and-repo-path-hardening
Open

Harden fetch SSRF protection and git repo_path restriction#4803
sachajw wants to merge 1 commit into
modelcontextprotocol:mainfrom
sachajw:security/ssrf-and-repo-path-hardening

Conversation

@sachajw

@sachajw sachajw commented Sep 13, 2026

Copy link
Copy Markdown

Summary

Two hardening fixes found during local security review of the reference fetch and git servers. Both are additive and default to the safer behavior, with an explicit opt-out flag for anyone who needs the old, permissive behavior.

  • fetch server — SSRF protection. fetch_url()/check_may_autonomously_fetch_url() previously called httpx.AsyncClient.get() with follow_redirects=True and no restriction on the resolved target address, so a client could point the server at loopback/private/link-local addresses (e.g. cloud metadata endpoints, internal services) directly, or reach them indirectly via a redirect. Now:

    • check_url_is_not_internal() resolves the hostname via socket.getaddrinfo() and rejects the request if any resolved address is private/loopback/link-local/multicast/reserved/unspecified.
    • Redirects are followed manually (capped at 5 hops), re-checking every hop's target instead of trusting httpx to follow them blindly.
    • New --allow-private-ips flag disables this for trusted deployments.
  • git server — unrestricted repo_path when --repository isn't set. validate_repo_path() only enforced a boundary when --repository was passed; without it, allowed_repository was None and any repo_path was accepted by every tool. Now serve() defaults allowed_repository to the current working directory instead of leaving it unrestricted. New --allow-any-repository flag restores the previous behavior.

Both changes are documented in SECURITY.md (new "Local security hardening" section, existing disclosure-policy content unchanged).

Test plan

  • cd src/fetch && uv run pytest -q — 20 passed (fixed one test that used a non-resolvable subdomain unrelated to the SSRF logic)
  • cd src/git && uv run pytest -q — 47 passed
  • py_compile on all touched files

🤖 Generated with Claude Code

fetch server: block requests to private/loopback/link-local/reserved
addresses by default (resolved via socket.getaddrinfo, not just hostname
string matching), and manually re-validate every redirect hop instead of
trusting httpx's follow_redirects=True. Both closable via a new
--allow-private-ips flag for trusted deployments.

git server: restrict repo_path to the current working directory by default
when --repository isn't passed, instead of leaving it completely
unrestricted. New --allow-any-repository flag restores the old behavior.

Documented both in SECURITY.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant