// Package gitea provides a client for the Gitea API. // ipcheck.go implements IP-level SSRF protection by checking resolved addresses // against known blocked CIDR ranges (RFC1918, loopback, link-local, etc.). package gitea import ( "fmt" "net" ) // blockedCIDRs is the list of CIDR ranges that should never be contacted by // review-bot. These ranges cover private, loopback, link-local, multicast, // and other special-use address spaces that are not reachable from the internet // but may be reachable from a self-hosted runner. // // Based on: // - RFC1918 private ranges // - RFC5735 / RFC4193 special-use IPv4/IPv6 ranges // - RFC4291 IPv6 link-local / loopback var blockedCIDRs = func() []*net.IPNet { ranges := []string{ // IPv4 loopback "127.0.0.0/8", // IPv4 unspecified / "this network" "0.0.0.0/8", // RFC1918 private ranges "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", // IPv4 link-local (APIPA, also used by AWS instance metadata 169.254.169.254) "169.254.0.0/16", // IPv4 shared address space (RFC6598, carrier-grade NAT) "100.64.0.0/10", // IPv4 multicast "224.0.0.0/4", // IPv4 reserved / broadcast "240.0.0.0/4", // IPv6 loopback "::1/128", // IPv6 unspecified "::/128", // IPv6 link-local "fe80::/10", // IPv6 unique local (ULA) — RFC4193 "fc00::/7", // IPv6 multicast "ff00::/8", } nets := make([]*net.IPNet, 0, len(ranges)) for _, r := range ranges { _, cidr, err := net.ParseCIDR(r) if err != nil { // This is a programming error — panic to catch it at startup/test time. panic(fmt.Sprintf("ipcheck: invalid built-in CIDR %q: %v", r, err)) } nets = append(nets, cidr) } return nets }() // IsBlockedIP reports whether ip is in a blocked address range. // It is exported for use by the validate-url subcommand and tests outside // this package. // // IPv6-mapped IPv4 addresses (e.g. ::ffff:192.168.1.1) are normalized to their // IPv4 form before checking so that IPv4 CIDRs catch them. func IsBlockedIP(ip net.IP) bool { // Normalize IPv6-mapped IPv4 addresses (::ffff:x.x.x.x) to plain IPv4. if v4 := ip.To4(); v4 != nil { ip = v4 } for _, cidr := range blockedCIDRs { if cidr.Contains(ip) { return true } } return false }