## Ticket: CVE-2026-32871 — FastMCP OpenAPIProvider path traversal → authenticated SSRF

**Advisory**: GHSA-vv7q-7jx5-f767 — https://github.com/jlowin/fastmcp/security/advisories/GHSA-vv7q-7jx5-f767
**CVE**: CVE-2026-32871 | **CWE-22** (Path Traversal), **CWE-918** (SSRF)
**Severity**: High
**Package**: `fastmcp` (PyPI) | **Repository**: https://github.com/jlowin/fastmcp

### Impact

FastMCP's `OpenAPIProvider` parses an OpenAPI specification and exposes the
described backend endpoints to MCP clients as callable tools. The
`RequestDirector` class builds each outbound HTTP request; its `_build_url()`
method joins a **client-supplied path parameter** onto the configured backend
base URL **without rejecting path-traversal sequences**.

A client that puts `../` segments into a path parameter escapes the intended
API prefix and steers the request to arbitrary endpoints on the backend host —
an **authenticated SSRF**, because the provider attaches its configured
`Authorization` headers to the request. This exposes internal/un-described
backend APIs that were never meant to be reachable through the MCP surface.

### Affected / fixed versions

Affected: `fastmcp < 3.2.0`.
Fixed: **`3.2.0`**.

Reproduce on the **latest release prior to 3.2.0** (the 3.1.x line — resolve
the exact version from PyPI) and verify the fix on **`3.2.0`**.

### Where to look

The vulnerable code is `RequestDirector._build_url()` in the OpenAPI provider.
Compare the pre-3.2.0 release against `3.2.0`:

```bash
git clone https://github.com/jlowin/fastmcp.git
cd fastmcp && git diff v3.1.x v3.2.0 -- '*openapi*' '*request_director*'
```

The fix adds traversal rejection / normalization so a path parameter cannot
escape the configured API prefix.

### Reproduction approach

No external services are required — a small local stub backend is enough.

1. In a venv, install the **vulnerable** build: `pip install 'fastmcp<3.2.0'`.
2. Stand up a minimal local HTTP backend with an OpenAPI spec that defines an
   endpoint carrying a path parameter, plus a separate "internal" endpoint that
   the OpenAPI spec does **not** expose.
3. Configure a `FastMCP` `OpenAPIProvider` against that spec and base URL.
4. Invoke the generated tool with the path parameter set to a value containing
   `../` traversal so `_build_url()` resolves **outside** the intended API
   prefix and onto the internal endpoint.
5. Repeat with the **fixed** build: `pip install fastmcp==3.2.0`.

### Expected result

| Build | crafted `../` path parameter | outbound request |
|-------|------------------------------|------------------|
| `fastmcp < 3.2.0` (vulnerable) | accepted by `_build_url()` | **escapes** the API prefix, reaches the internal endpoint (SSRF) |
| `fastmcp 3.2.0` (fixed) | rejected / normalized | **contained** within the configured API prefix |

- **Vulnerable indicator**: the backend records a request to the internal
  endpoint that the OpenAPI spec never exposed (traversal succeeded).
- **Fixed indicator**: the request stays within the intended API prefix; the
  internal endpoint is never reached.

### Expected artifacts

- `reproduction_steps.sh` — installs both versions, stands up the stub backend,
  drives the OpenAPIProvider, and records which backend path each request hit.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the backend request paths for the vulnerable and fixed runs.
