// 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" ) // blockedCIDRStrings is the canonical list of CIDR strings that should never // be contacted by review-bot. See IsBlockedIP for the full list of covered // address families. // // These are hard-coded literals: any parse failure is a programming error. // Validity is verified by TestBlockedCIDRsValid in ipcheck_test.go. var blockedCIDRStrings = []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", } // blockedCIDRs is the parsed form of blockedCIDRStrings. // Any entry that fails to parse is recorded in blockedCIDRParseErrors instead // of panicking; tests verify this slice is always empty via TestBlockedCIDRsValid. var ( blockedCIDRs []*net.IPNet blockedCIDRParseErrors []string ) func init() { blockedCIDRs = make([]*net.IPNet, 0, len(blockedCIDRStrings)) for _, r := range blockedCIDRStrings { _, cidr, err := net.ParseCIDR(r) if err != nil { // Record the error rather than panicking; TestBlockedCIDRsValid // will catch this during tests, and the CI build will fail. blockedCIDRParseErrors = append(blockedCIDRParseErrors, fmt.Sprintf("ipcheck: invalid built-in CIDR %q: %v", r, err)) continue } blockedCIDRs = append(blockedCIDRs, cidr) } } // 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. // // Based on: // - RFC1918 private ranges // - RFC5735 / RFC4193 special-use IPv4/IPv6 ranges // - RFC4291 IPv6 link-local / loopback 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 }