"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assertPublicUrl = assertPublicUrl;
const promises_1 = require("node:dns/promises");
const PRIVATE_RANGES = [
    { start: ip4ToNum('10.0.0.0'), end: ip4ToNum('10.255.255.255') },
    { start: ip4ToNum('172.16.0.0'), end: ip4ToNum('172.31.255.255') },
    { start: ip4ToNum('192.168.0.0'), end: ip4ToNum('192.168.255.255') },
    { start: ip4ToNum('127.0.0.0'), end: ip4ToNum('127.255.255.255') },
    { start: ip4ToNum('169.254.0.0'), end: ip4ToNum('169.254.255.255') },
    { start: ip4ToNum('0.0.0.0'), end: ip4ToNum('0.255.255.255') },
    { start: ip4ToNum('100.64.0.0'), end: ip4ToNum('100.127.255.255') },
    { start: ip4ToNum('192.0.0.0'), end: ip4ToNum('192.0.0.255') },
    { start: ip4ToNum('198.18.0.0'), end: ip4ToNum('198.19.255.255') },
    { start: ip4ToNum('240.0.0.0'), end: ip4ToNum('255.255.255.255') },
];
const PRIVATE_IPV6_PREFIXES = ['::1', 'fe80:', 'fd', 'fc'];
const IPV4_MAPPED_IPV6_DOTTED = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i;
const IPV4_MAPPED_IPV6_HEX = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
function ip4ToNum(ip) {
    const parts = ip.split('.').map(Number);
    return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0;
}
function isPrivateIPv4(ip) {
    const num = ip4ToNum(ip);
    return PRIVATE_RANGES.some((r) => num >= r.start && num <= r.end);
}
function extractMappedIPv4(ip) {
    const dottedMatch = IPV4_MAPPED_IPV6_DOTTED.exec(ip);
    if (dottedMatch)
        return dottedMatch[1];
    const hexMatch = IPV4_MAPPED_IPV6_HEX.exec(ip);
    if (hexMatch) {
        const high = parseInt(hexMatch[1], 16);
        const low = parseInt(hexMatch[2], 16);
        return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
    }
    return null;
}
function isPrivateIPv6(ip) {
    const lower = ip.toLowerCase();
    const mappedIPv4 = extractMappedIPv4(lower);
    if (mappedIPv4 !== null) {
        return isPrivateIPv4(mappedIPv4);
    }
    return PRIVATE_IPV6_PREFIXES.some((prefix) => lower.startsWith(prefix));
}
async function assertPublicUrl(url) {
    const parsed = new URL(url);
    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
        throw new Error(`Blocked: scheme "${parsed.protocol}" is not allowed. Only HTTP(S) is supported.`);
    }
    const hostname = parsed.hostname;
    if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
        if (isPrivateIPv4(hostname)) {
            throw new Error(`Blocked: ${hostname} resolves to a private IP address.`);
        }
        return;
    }
    if (hostname.startsWith('[') || hostname.includes(':')) {
        const bare = hostname.replace(/^\[|\]$/g, '');
        if (isPrivateIPv6(bare)) {
            throw new Error(`Blocked: ${hostname} resolves to a private IPv6 address.`);
        }
        return;
    }
    try {
        const result = await (0, promises_1.lookup)(hostname, { all: true });
        for (const { address, family } of result) {
            if (family === 4 && isPrivateIPv4(address)) {
                throw new Error(`Blocked: ${hostname} resolves to a private IP address (${address}).`);
            }
            if (family === 6 && isPrivateIPv6(address)) {
                throw new Error(`Blocked: ${hostname} resolves to a private IPv6 address (${address}).`);
            }
        }
    }
    catch (error) {
        if (error instanceof Error && error.message.startsWith('Blocked:')) {
            throw error;
        }
        throw new Error(`DNS resolution failed for ${hostname}: ${error instanceof Error ? error.message : String(error)}`);
    }
}
//# sourceMappingURL=ssrf-guard.js.map