(ip: string)
| 110 | |
| 111 | /** True when an IPv4 string is a public address (not RFC1918/loopback/etc). */ |
| 112 | export function isPublicIPv4(ip: string): boolean { |
| 113 | const m = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/); |
| 114 | if (!m) return false; |
| 115 | const o = m.slice(1, 5).map(Number); |
| 116 | if (o.some((n) => n > 255)) return false; |
| 117 | const [a, b] = o; |
| 118 | if (a === 10) return false; // 10.0.0.0/8 |
| 119 | if (a === 127) return false; // loopback |
| 120 | if (a === 0) return false; // this-network |
| 121 | if (a === 192 && b === 168) return false; // 192.168.0.0/16 |
| 122 | if (a === 169 && b === 254) return false; // link-local |
| 123 | if (a === 172 && b >= 16 && b <= 31) return false; // 172.16.0.0/12 |
| 124 | if (a === 100 && b >= 64 && b <= 127) return false; // CGNAT 100.64.0.0/10 |
| 125 | if (a >= 224) return false; // multicast / reserved |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | // EIP-55 checksum is out of scope (heavy); we require a length+charset match and |
| 130 | // reject all-same-char vanity strings to cut the worst FPs. |
no outgoing calls
no test coverage detected