add extra checks for the default oauth discovery client: * disallow redirects which downgrade to http * disallow redirects to localhost or private ip * disallow the initial call to a private ip * proxy configuration opts out of dial time checking
49 lines
1.4 KiB
Go
49 lines
1.4 KiB
Go
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
|
// Use of this source code is governed by the license
|
|
// that can be found in the LICENSE file.
|
|
package util
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
)
|
|
|
|
func IsLoopback(addr string) bool {
|
|
host, _, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
// If SplitHostPort fails, it might be just a host without a port.
|
|
host = strings.Trim(addr, "[]")
|
|
}
|
|
if host == "localhost" {
|
|
return true
|
|
}
|
|
ip, err := netip.ParseAddr(host)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return ip.IsLoopback()
|
|
}
|
|
|
|
// cgnat is the RFC 6598 carrier-grade NAT range (100.64.0.0/10). netip's
|
|
// IsPrivate does not report it, but it is shared ISP space that is not publicly
|
|
// routable and could point at another tenant behind the same NAT.
|
|
var cgnat = netip.MustParsePrefix("100.64.0.0/10")
|
|
|
|
// IsPrivateOrReserved reports whether ip belongs to a range that must not be
|
|
// reachable through a server-controlled URL, as a defense against SSRF: private
|
|
// (RFC 1918 / RFC 4193 ULA), link-local (including the 169.254.169.254 cloud
|
|
// metadata endpoint), carrier-grade NAT, multicast, and the unspecified address.
|
|
func IsPrivateOrReserved(ip netip.Addr) bool {
|
|
ip = ip.Unmap()
|
|
if !ip.IsValid() {
|
|
return true
|
|
}
|
|
if ip.IsPrivate() ||
|
|
ip.IsLinkLocalUnicast() ||
|
|
ip.IsMulticast() ||
|
|
ip.IsUnspecified() {
|
|
return true
|
|
}
|
|
return cgnat.Contains(ip)
|
|
}
|