Skip to content
pull request #126, “feat: harden SSRF on external APIs”

pull request #126, “feat: harden SSRF on external APIs”

github.com • October 5, 2026

get_dataservice_openapi_spec makes a GET request on the URL provided by machine_documentation_url from the catalog on the MCP host. That field is set by any registered data.gouv.fr producer, so a malicious dataservice could point at loopback, RFC1918, or cloud metadata ( 169.254.169.254 ).

Checking the URL string before requests.get is not enough: DNS can rebind between check and connect, and a 302 can still land on an internal target.

Example (what this blocked)

An attacker creates a dataservice with machine_documentation_url= (or , or ).

Anyone using the MCP later calls get_dataservice_openapi_spec on that id.

Before this change, mcp.data.gouv.fr issued that GET (not the attacker's laptop). A live internal port vs a closed one produced different error text, which is enough to scan the server's network. A 302 from a "normal" public URL could also bounce to metadata. After this change, the MCP refuses to connect to those addresses (and re-checks the IP after each redirect).

This follows the same approach as opendatateam/udata#3877 ( udata.ssrf / ssrf_session ):

destination IP validated at connect time (same resolution as the socket), including IPv6-mapped / NAT64 forms

each redirect hop re-checked

environment/explicit proxies refused

same URLS_ALLOW_LOCAL / URLS_ALLOW_PRIVATE grouping (hosted MCP: both off)

uv run pytest tests/test_ssrf.py (loopback, metadata, RFC1918, localhost, IPv6-mapped, redirect to metadata, HTTP_PROXY)

There was an error while loading. Please reload this page .

There was an error while loading. Please reload this page .

Extracted Entities