zed-industries/zed · error

OAuth endpoint must not point to private/reserved IP: ::ffff

Error message

OAuth endpoint must not point to private/reserved IP: ::ffff:{}

What it means

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.

Source

Thrown at crates/context_server/src/oauth.rs:99

                // Loopback is already allowed by require_https_or_loopback.
                if ip.is_private() || ip.is_link_local() || ip.is_broadcast() || ip.is_unspecified()
                {
                    bail!(
                        "OAuth endpoint must not point to private/reserved IP: {}",
                        ip
                    );
                }
            }
            url::Host::Ipv6(ip) => {
                // Check for IPv4-mapped IPv6 addresses (::ffff:a.b.c.d) which
                // could bypass the IPv4 checks above.
                if let Some(mapped_v4) = ip.to_ipv4_mapped() {
                    if mapped_v4.is_private()
                        || mapped_v4.is_link_local()
                        || mapped_v4.is_broadcast()
                        || mapped_v4.is_unspecified()
                    {
                        bail!(
                            "OAuth endpoint must not point to private/reserved IP: ::ffff:{}",
                            mapped_v4
                        );
                    }
                }

                if ip.is_unspecified() || ip.is_multicast() {
                    bail!(
                        "OAuth endpoint must not point to reserved IPv6 address: {}",
                        ip
                    );
                }
                // IPv6 Unique Local Addresses (fc00::/7). is_unique_local() is
                // nightly-only, so check the prefix manually.
                if (ip.segments()[0] & 0xfe00) == 0xfc00 {
                    bail!(
                        "OAuth endpoint must not point to IPv6 unique-local address: {}",
                        ip

View on GitHub (pinned to f4178619ac)

Solutions

  1. Use the endpoint's public DNS name over HTTPS instead of any IP-literal form
  2. If testing locally, use http://localhost or http://127.0.0.1 directly rather than their mapped-IPv6 equivalents
  3. When fuzzing/reviewing an MCP server, treat this exact message as a blocked SSRF bypass attempt, not a bug in Zed

Example fix

// before
Url::parse("https://[::ffff:10.0.0.5]:9443/token")

// after
Url::parse("https://auth.example.com/token")
Defensive patterns

Strategy: validation

Validate before calling

use url::Url;

fn mapped_ipv4_host_allowed(url: &Url) -> bool {
    match url.host() {
        Some(url::Host::Ipv6(ip)) => match ip.to_ipv4_mapped() {
            Some(v4) => {
                !(v4.is_private() || v4.is_link_local() || v4.is_broadcast() || v4.is_unspecified())
            }
            None => true,
        },
        _ => true,
    }
}

if !mapped_ipv4_host_allowed(&endpoint) {
    bail_user_config!("endpoint uses IPv4-mapped private address: {}", endpoint);
}

Type guard

fn is_allowed_ipv6_literal(url: &Url) -> Option<bool> {
    match url.host() {
        Some(url::Host::Ipv6(ip)) => Some(
            ip.to_ipv4_mapped()
                .map(|v4| !(v4.is_private() || v4.is_link_local() || v4.is_broadcast() || v4.is_unspecified()))
                .unwrap_or(true),
        ),
        _ => None,
    }
}

Try / catch

match validate_oauth_url(&endpoint) {
    Err(err) if err.to_string().contains("::ffff:") => {
        // mapped-IPv6 bypass attempt — reject and audit the source metadata
        audit_ssrf_attempt(&endpoint);
        Err(err)
    }
    other => other,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/3e60ea2720544188. Report an issue: GitHub.