import { createHmac } from "node:crypto"; import { isIP } from "node:net"; // The parts of webhooks that need no database (see lib/webhooks.ts), so they can be tested. /** Loopback, private, link-local, carrier-grade NAT, unspecified and multicast addresses. */ export function isPrivateAddress(ip: string) { if (isIP(ip) === 4) { const [a, b] = ip.split(".").map(Number); return ( a === 0 || a === 10 || a === 127 || a >= 224 || (a === 100 && b >= 64 && b <= 127) || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) ); } const v6 = ip.toLowerCase(); if (v6 === "::" || v6 === "::1") return true; const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(v6); if (mapped) return isPrivateAddress(mapped[1]); return /^(fc|fd|fe[89ab]|ff)/.test(v6); } /** What a delivery is signed with: sha256= and the HMAC of the body under the hook's secret, as GitHub does. */ export const signature = (secret: string, body: string) => `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;