src/services/urlSafety.ts

import dns from "node:dns/promises"; import net from "node:net";

function isPrivateIp(value) { const ipType = net.isIP(String(value || "")); if (!ipType) return false;

if (ipType === 4) { const parts = String(value || "") .split(".") .map((part) => Number(part || 0)); if (parts[0] === 10 || parts[0] === 127 || parts[0] === 0) return true; if (parts[0] === 169 && parts[1] === 254) return true; if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true; if (parts[0] === 192 && parts[1] === 168) return true; return false; }

const compact = String(value || "").replace(/^[|]$/g, "").toLowerCase(); if (compact === "::1") return true; if (compact.startsWith("fc") || compact.startsWith("fd")) return true; return compact.startsWith("fe80"); }

export function isBlockedHost(hostname) { const host = String(hostname || "").toLowerCase(); if (!host) return true; if (host === "localhost" || host.endsWith(".local")) return true; return isPrivateIp(host); }

export async function assertPublicUrl(rawUrl) { const parsed = new URL(String(rawUrl || "")); const host = String(parsed.hostname || "").toLowerCase(); if (isBlockedHost(host)) { throw new Error(blocked host: ${host}); }

const records = await dns.lookup(host, { all: true }); for (const record of records) { if (isPrivateIp(record?.address)) { throw new Error(blocked private address for host ${host}); } } }