Threat model of the scanner¶
mcp-posture talks to servers that may be hostile. This page lists what it defends against.
| Threat | Mitigation |
|---|---|
SSRF: a target, redirect, resource_metadata or authorization_servers URL points to internal services or cloud metadata |
Every connection resolves the host and refuses loopback, private, link-local, CGNAT, multicast and reserved addresses at connect time, after DNS resolution (no TOCTOU), on every redirect hop. --allow-private lifts it for your own lab. With --proxy, every request and redirect hop is resolved and vetted locally before it is handed to the proxy; a proxy that resolves names differently (split-horizon DNS) is outside the scanner's control. |
| Credential leakage | Tokens come only from an env var, a file or stdin. They are sent only to the target origin, never forwarded on cross-origin redirects, and redacted from every report and log line (plus token-shaped strings). Headers from client configs are never read. |
| Resource exhaustion | Per-request timeout, streamed bodies capped (1 MiB by default, 64 KiB for SSE), bounded redirects (5), bounded pagination (20 pages), bounded concurrency. |
| Malicious content in reports | Server-controlled strings are neutralized: no terminal escape sequences, bidi overrides or invisible characters reach your terminal, PR comments or code scanning. |
| Parser crashes | Header and metadata parsers are property-tested (Hypothesis); a check that raises becomes an MCPP-ERR00 finding instead of aborting the scan; malformed Location headers are handled without httpx's redirect parser. |
login: hostile metadata, redirect interception, token leaks |
Discovery refuses a PRM resource, issuer or PKCE mismatch before the browser opens; the browser only ever opens the HTTPS authorization_endpoint. PKCE S256, random state, RFC 9207 iss check. The loopback listener binds 127.0.0.1, accepts only GET /callback with the right state, and echoes nothing. The token is never printed to a terminal without --show-token; token files are mode 600 and never written through a symlink; refresh tokens are discarded. DCR clients are deleted afterwards (RFC 7592) unless kept. |
| Side effects on the target | Passive mode sends metadata GETs, the MCP handshake and list calls only. No tool is ever called. |
| Supply chain | Locked dependencies (uv.lock), pip-audit and CodeQL in CI, actions pinned by commit SHA and base images by digest, distroless nonroot image. Releases use PyPI Trusted Publishing with attestations, build provenance, a CycloneDX SBOM and keyless cosign signatures on images (see SECURITY.md). |
| Telemetry | None. The scanner only contacts the targets and the authorization servers they advertise. |
Out of scope: protecting a target from a malicious operator of the scanner. Responsible use is the operator's obligation.