/** * Browser-safe SSRF guard for outbound OAuth metadata fetches (P1, shared * hardening pass). The OAuth state machines hand a URL — `resource_metadata` * from a WWW-Authenticate challenge, an authorization-server metadata endpoint, * a CIMD document — to their request executor. Without a destination check, an * attacker-influenced value (a hostile MCP server's PRM pointer, a redirect) * can steer that fetch at a private/reserved address: cloud metadata * (169.254.169.254), link-local, or a LAN service. * * This module holds the pure RFC 6890 special-use IP classifier (moved here * from `oauth-proxy.ts`, which stays Node-only for DNS) so it can run in the * browser executor path too, plus `assertOutboundOAuthUrlAllowed` and a * request-executor wrapper the factory applies to every machine at once. * * Scope of the browser-safe check: URL scheme + IP-literal classification and * an explicit loopback opt-in. DNS-resolution (rebinding) validation needs a * resolver and remains in the Node proxy path (`resolveAndValidateDns`); a bare * public hostname passes here and is resolved+revalidated there. */ /** * RFC 6890 special-use / non-public-unicast check for a numeric IP (IPv4, IPv6, * or IPv4-mapped IPv6 in any text form). Returns true for addresses an outbound * SSRF-sensitive fetch must refuse. A bare hostname (not an IP literal) returns * false — the caller resolves it first. */ declare function isDisallowedIpAddress(ip: string): boolean; /** * True for a hostname an SSRF-sensitive fetch must refuse: the loopback names * plus any special-use IP literal. A bare public hostname returns false (its * DNS resolution is validated separately in the Node proxy path). */ declare function isPrivateHost(hostname: string): boolean; /** * True when a URL's host is loopback (`localhost`, `127.0.0.0/8`, `::1`, or an * IPv4-mapped loopback). Callers derive the loopback opt-in from the * USER-CONFIGURED server URL: a localhost MCP server legitimately needs loopback * metadata fetches, while a public/remote server must never be allowed to steer * one at the user's own loopback (exact-origin allowance, not a global toggle). */ declare function isLoopbackOAuthUrl(rawUrl: string | undefined): boolean; /** Thrown when an outbound OAuth metadata fetch targets a blocked host. */ declare class OAuthOutboundUrlBlockedError extends Error { readonly url: string; readonly reason: "invalid-url" | "invalid-scheme" | "private-host"; constructor(url: string, reason: "invalid-url" | "invalid-scheme" | "private-host", message: string); } /** * Refuse an outbound OAuth metadata fetch to a private/reserved destination * before it runs. `allowLoopback` (local-dev opt-in) carves out loopback hosts * only — it never relaxes the guard for a LAN/link-local/reserved address. */ declare function assertOutboundOAuthUrlAllowed(rawUrl: string, options?: { allowLoopback?: boolean; }): URL; export { OAuthOutboundUrlBlockedError as O, assertOutboundOAuthUrlAllowed as a, isLoopbackOAuthUrl as b, isPrivateHost as c, isDisallowedIpAddress as i };