/**
 * SSRF Protection Module
 * Validates URLs to prevent Server-Side Request Forgery attacks
 */
import { URL } from 'url';
/**
 * Private IP ranges that should be blocked
 */
const PRIVATE_IP_RANGES = [
    /^127\./, // Loopback
    /^10\./, // Private class A
    /^172\.(1[6-9]|2[0-9]|3[01])\./, // Private class B
    /^192\.168\./, // Private class C
    /^169\.254\./, // Link-local
    /^::1$/, // IPv6 loopback
    /^fe80:/, // IPv6 link-local
    /^fc00:/, // IPv6 private
    /^fd00:/, // IPv6 private
];
/**
 * Blocked hostnames (cloud metadata endpoints, etc.)
 * Note: localhost is allowed for development (can be disabled via env var)
 */
const BLOCKED_HOSTS = [
    '169.254.169.254', // AWS/Azure/GCP metadata
    'metadata.google.internal', // GCP metadata
    'metadata', // Generic metadata
    // 'localhost',              // Localhost - ALLOWED for development
    // '0.0.0.0',                // All interfaces - ALLOWED for development
];
/**
 * Allowed URL schemes
 */
const ALLOWED_SCHEMES = ['https:', 'http:'];
/**
 * Validate URL to prevent SSRF attacks
 */
export function validateGatewayUrl(urlString) {
    try {
        // Parse URL
        const url = new URL(urlString);
        // Check scheme
        if (!ALLOWED_SCHEMES.includes(url.protocol)) {
            return {
                valid: false,
                error: `Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are allowed.`
            };
        }
        // Check for empty hostname
        if (!url.hostname) {
            return {
                valid: false,
                error: 'URL must have a hostname.'
            };
        }
        // Block dangerous hostnames (but allow localhost for development)
        const lowerHost = url.hostname.toLowerCase();
        // Strongly recommend HTTPS (but don't spam console for localhost development)
        const isLocalDev = lowerHost === 'localhost' || lowerHost === '127.0.0.1' || lowerHost === '0.0.0.0';
        if (url.protocol === 'http:' && !isLocalDev) {
            console.warn('⚠️  WARNING: Using HTTP (unencrypted). HTTPS is strongly recommended for security.');
        }
        // Allow localhost/127.0.0.1 for development (unless explicitly disabled)
        const allowLocalhost = process.env.ALLOW_LOCALHOST !== 'false';
        const isLocalhost = lowerHost === 'localhost' || lowerHost === '127.0.0.1' || lowerHost === '0.0.0.0' || lowerHost === '::1';
        if (isLocalhost && !allowLocalhost) {
            return {
                valid: false,
                error: `Blocked hostname: ${url.hostname}. Cannot connect to internal services. Set ALLOW_LOCALHOST=true to allow.`
            };
        }
        // Block other dangerous hostnames
        if (BLOCKED_HOSTS.includes(lowerHost)) {
            return {
                valid: false,
                error: `Blocked hostname: ${url.hostname}. Cannot connect to internal services.`
            };
        }
        // Private / link-local ranges (SSRF-style guard for outbound use). For **Gateway connect**, users
        // often point at self‑hosted LAN IPs (10/172.16–31/192.168). Opt in with ALLOW_PRIVATE_GATEWAY.
        const allowPrivateGateway = process.env.ALLOW_PRIVATE_GATEWAY === '1' || process.env.ALLOW_PRIVATE_GATEWAY === 'true';
        if ((!isLocalhost || !allowLocalhost) && !allowPrivateGateway) {
            for (const range of PRIVATE_IP_RANGES) {
                if (range.test(url.hostname)) {
                    return {
                        valid: false,
                        error: `Blocked private IP: ${url.hostname}. For a self-hosted Gateway on your LAN, set ALLOW_PRIVATE_GATEWAY=1 and retry.`
                    };
                }
            }
        }
        // Check for username/password in URL (security risk)
        if (url.username || url.password) {
            return {
                valid: false,
                error: 'Credentials in URL are not allowed. Use API keys or tokens instead.'
            };
        }
        // Validate port (if specified)
        if (url.port) {
            const port = parseInt(url.port, 10);
            if (isNaN(port) || port < 1 || port > 65535) {
                return {
                    valid: false,
                    error: `Invalid port: ${url.port}. Must be between 1 and 65535.`
                };
            }
        }
        return { valid: true };
    }
    catch (error) {
        return {
            valid: false,
            error: `Invalid URL format: ${error.message}`
        };
    }
}
/**
 * Sanitize URL for logging (remove sensitive parts)
 */
export function sanitizeUrlForLogging(urlString) {
    try {
        const url = new URL(urlString);
        // Remove auth, query params, hash
        return `${url.protocol}//${url.hostname}${url.port ? ':' + url.port : ''}${url.pathname}`;
    }
    catch {
        return '[invalid-url]';
    }
}
//# sourceMappingURL=ssrf-protection.js.map