{"record":{"id":"3e60ea2720544188","repo":"zed-industries/zed","slug":"oauth-endpoint-must-not-point-to-private-reserved-3e60ea","errorCode":null,"errorMessage":"OAuth endpoint must not point to private/reserved IP: ::ffff:{}","messagePattern":"OAuth endpoint must not point to private/reserved IP: ::ffff:(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/context_server/src/oauth.rs","lineNumber":99,"sourceCode":"                // Loopback is already allowed by require_https_or_loopback.\n                if ip.is_private() || ip.is_link_local() || ip.is_broadcast() || ip.is_unspecified()\n                {\n                    bail!(\n                        \"OAuth endpoint must not point to private/reserved IP: {}\",\n                        ip\n                    );\n                }\n            }\n            url::Host::Ipv6(ip) => {\n                // Check for IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) which\n                // could bypass the IPv4 checks above.\n                if let Some(mapped_v4) = ip.to_ipv4_mapped() {\n                    if mapped_v4.is_private()\n                        || mapped_v4.is_link_local()\n                        || mapped_v4.is_broadcast()\n                        || mapped_v4.is_unspecified()\n                    {\n                        bail!(\n                            \"OAuth endpoint must not point to private/reserved IP: ::ffff:{}\",\n                            mapped_v4\n                        );\n                    }\n                }\n\n                if ip.is_unspecified() || ip.is_multicast() {\n                    bail!(\n                        \"OAuth endpoint must not point to reserved IPv6 address: {}\",\n                        ip\n                    );\n                }\n                // IPv6 Unique Local Addresses (fc00::/7). is_unique_local() is\n                // nightly-only, so check the prefix manually.\n                if (ip.segments()[0] & 0xfe00) == 0xfc00 {\n                    bail!(\n                        \"OAuth endpoint must not point to IPv6 unique-local address: {}\",\n                        ip","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/context_server/src/oauth.rs#L81-L117","documentation":"IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) embed an IPv4 address and would otherwise slip past the plain-IPv4 checks in validate_oauth_url. The source explicitly converts the IPv6 host with to_ipv4_mapped() and re-runs the private/link-local/broadcast/unspecified checks on the embedded IPv4 value, bailing with the ::ffff: prefix in the message. Hitting this error means the endpoint URL tried the mapped form of a blocked internal IPv4 address.","triggerScenarios":"An endpoint URL whose host is an IPv6 literal in the ::ffff:0:0/96 range whose mapped IPv4 is private, link-local, broadcast, or unspecified — e.g. https://[::ffff:192.168.1.10]/token or https://[::ffff:169.254.169.254]/latest/meta-data.","commonSituations":"Dual-stack servers or test harnesses that normalize IPv4 hosts into mapped-IPv6 form; deliberate bypass attempts where a malicious MCP server encodes a cloud-metadata or LAN address as ::ffff:169.254.169.254 to evade naive blocklists; tools that emit mapped addresses when a hostname resolves to both A and AAAA records.","solutions":["Use the endpoint's public DNS name over HTTPS instead of any IP-literal form","If testing locally, use http://localhost or http://127.0.0.1 directly rather than their mapped-IPv6 equivalents","When fuzzing/reviewing an MCP server, treat this exact message as a blocked SSRF bypass attempt, not a bug in Zed"],"exampleFix":"// before\nUrl::parse(\"https://[::ffff:10.0.0.5]:9443/token\")\n\n// after\nUrl::parse(\"https://auth.example.com/token\")","handlingStrategy":"validation","validationCode":"use url::Url;\n\nfn mapped_ipv4_host_allowed(url: &Url) -> bool {\n    match url.host() {\n        Some(url::Host::Ipv6(ip)) => match ip.to_ipv4_mapped() {\n            Some(v4) => {\n                !(v4.is_private() || v4.is_link_local() || v4.is_broadcast() || v4.is_unspecified())\n            }\n            None => true,\n        },\n        _ => true,\n    }\n}\n\nif !mapped_ipv4_host_allowed(&endpoint) {\n    bail_user_config!(\"endpoint uses IPv4-mapped private address: {}\", endpoint);\n}","typeGuard":"fn is_allowed_ipv6_literal(url: &Url) -> Option<bool> {\n    match url.host() {\n        Some(url::Host::Ipv6(ip)) => Some(\n            ip.to_ipv4_mapped()\n                .map(|v4| !(v4.is_private() || v4.is_link_local() || v4.is_broadcast() || v4.is_unspecified()))\n                .unwrap_or(true),\n        ),\n        _ => None,\n    }\n}","tryCatchPattern":"match validate_oauth_url(&endpoint) {\n    Err(err) if err.to_string().contains(\"::ffff:\") => {\n        // mapped-IPv6 bypass attempt — reject and audit the source metadata\n        audit_ssrf_attempt(&endpoint);\n        Err(err)\n    }\n    other => other,\n}","preventionTips":["Normalize all endpoint URLs to domain names in your metadata so no IP-literal forms (v4, mapped-v6, ULA) ever appear","Include mapped-IPv6 and ULA literals in your own server-side URL linting; naive blocklists miss them","Treat any partner metadata containing ::ffff: literals as hostile until proven otherwise"],"tags":["oauth","mcp","ssrf","ipv6","url-validation","security"],"backgroundTag":"ssrf-private-ip-blocked","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}