Spaces:
Sleeping
Sleeping
| // Shared URL policy. Used both at request entry (server.js) and per-request | |
| // inside the renderer (renderer.js) so redirects to private IPs are blocked. | |
| const PRIVATE_HOSTNAMES = new Set([ | |
| "localhost", | |
| "127.0.0.1", | |
| "0.0.0.0", | |
| "[::1]", | |
| "::1", | |
| ]); | |
| /** | |
| * Returns true if `urlStr` is a public http(s) URL we're willing to fetch. | |
| * Rejects: non-http(s) schemes, localhost, IPv6 literals, RFC1918 IPv4, | |
| * link-local, and anything that fails to parse. | |
| */ | |
| export function isAllowedUrl(urlStr) { | |
| let parsed; | |
| try { | |
| parsed = new URL(urlStr); | |
| } catch { | |
| return false; | |
| } | |
| if (!["http:", "https:"].includes(parsed.protocol)) return false; | |
| const host = parsed.hostname; | |
| if (PRIVATE_HOSTNAMES.has(host)) return false; | |
| if (host.startsWith("[")) return false; // any IPv6 literal — overly cautious by design | |
| if (host === "::1") return false; | |
| // RFC1918 + link-local IPv4 ranges | |
| const parts = host.split("."); | |
| if (parts[0] === "10") return false; | |
| if (parts[0] === "172" && +parts[1] >= 16 && +parts[1] <= 31) return false; | |
| if (parts[0] === "192" && parts[1] === "168") return false; | |
| if (parts[0] === "169" && parts[1] === "254") return false; | |
| return true; | |
| } | |