ruvnet/ruflo · error

Resolved IP for is internal ( )

Error message

Resolved IP for ${hostname} is internal (${address})

What it means

Thrown by assertSafeIp in urlSafety.ts, an SSRF defense hooked into undici's custom DNS resolution. After a hostname resolves, the IP is checked against internal ranges (IPv4 0.0.0.0/8, 100.64.0.0/10, 127/8, 169.254/16, 172.16/12, 192.168/16; IPv6 loopback, link-local, and IPv4-mapped forms like ::ffff:127.0.0.1; unknown formats are blocked too). Validating at connect time instead of URL-parse time closes the TOCTOU window of DNS rebinding.

Solutions

  1. Use a genuinely public URL — the block is intentional for user-supplied fetch targets.
  2. If you operate the deployment and genuinely need an internal target, whitelist it in the URL-safety layer (extend isValidUrl's explicit localhost/host.docker.internal allowance) instead of disabling assertSafeIp globally.
  3. For local development needs, prefer hostnames already allowed by isValidUrl (localhost, 127.0.0.1, host.docker.internal) rather than other RFC1918 addresses.
  4. Never remove the connect-time check in undici's lookup — that reopens DNS-rebinding SSRF.
Defensive patterns

Strategy: try-catch

Validate before calling

import { isValidUrl } from "$lib/server/urlSafety";

if (!isValidUrl(targetUrl)) {
	return new Response("URL not allowed", { status: 400 });
}

Type guard

import { isIP } from "node:net";

function isPublicIpLiteral(host: string): boolean {
	const h = host.replace(/^\[|]$/g, "");
	return isIP(h) === 0 ? true : !isInternalIp(h); // pair with your own subnet list
}

Try / catch

try {
	await fetchTarget(url);
} catch (err) {
	if (String(err).includes("is internal")) {
		return new Response("Refusing to fetch internal addresses", { status: 400 }); // expected block, not a bug
	}
	throw err;
}

Prevention

When it happens

Trigger: Any outbound fetch through the guarded client whose hostname resolves to a private/loopback/link-local IP: a URL pointing at http://127.0.0.1:8080 or http://192.168.1.10, an internal DNS name (e.g., http://metadata, http://db.internal) that resolves into RFC1918 space, or an external domain that rebinds to an internal address between validation and connection.

Common situations: Users asking the chat to fetch a URL on their LAN; a fetch-url endpoint being probed for cloud metadata (169.254.169.254); dev setups pointing at localhost services through a hostname that resolves internally; DNS records intentionally mapping a public name to an internal address.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/ac5b2de3fa3b5f45. Report an issue: GitHub.

Appendix: source

Thrown at ruflo/src/ruvocal/src/lib/server/urlSafety.ts:75

		// If the hostname is a raw IP literal, validate it
		const cleanHostname = hostname.replace(/^\[|]$/g, "");
		if (isIP(cleanHostname)) {
			return !isUnsafeIp(cleanHostname);
		}
		return true;
	} catch {
		return false;
	}
}

/**
 * Assert that a resolved IP address is safe (not internal/private).
 * Throws if the IP is internal. Used in undici's custom DNS lookup
 * to validate IPs at connection time (prevents TOCTOU DNS rebinding).
 */
export function assertSafeIp(address: string, hostname: string): void {
	if (isUnsafeIp(address)) {
		throw new Error(`Resolved IP for ${hostname} is internal (${address})`);
	}
}

View on GitHub (pinned to fa13ee4ad6)