/** * SSRF Guard — Safe URL Validation Utility * * Prevents Server-Side Request Forgery by: * 1. Enforcing HTTPS-only (no plain HTTP). * 2. Normalising encoded IPv4 forms (octal, hex, decimal integer, IPv4-mapped IPv6) * to canonical dotted-decimal before rangechecking. * 3. Resolving the hostname for **both** A and AAAA families and rejecting * requests to RFC 1918 private ranges, loopback, link-local, CGNAT, * IPv6 link-local/ULA, and cloud metadata endpoints * (AWS / GCP / Azure / Alibaba). * 4. Re-throwing on DNS failure rather than silently allowing the request. * * **DNS rebinding residual race:** `assertSafeUrl` validates the IP at the * moment of the lookup. If the resolver returns a public IP here and a private * IP at the actual `fetch()` call, the guard is bypassed. To eliminate the * race, use the companion `safeDownload` helper in `safeFetch.ts` which pins * the resolved IP onto the request via an undici Agent dispatcher. * * Usage: * await assertSafeUrl(url); * // ... or, for actual downloads: ... * await safeDownload(url, { maxBytes, label }); * * @module utils/ssrfGuard */ import type { PinnedAddress } from "../types/index.js"; /** * Assert that `url` is safe to fetch server-side. * * @throws {Error} when the URL is non-HTTPS, parses as a blocked IP literal, * or resolves (A or AAAA) to a blocked IP. **Also throws on DNS lookup * failure** (the previous behaviour of silently allowing was a bypass — * an attacker-controlled resolver could force NXDOMAIN here and a private * IP at the actual fetch). */ export declare function assertSafeUrl(url: string): Promise; /** * Validate `url` and return the resolved address set that should be used for * the actual fetch (companion to `safeFetch.ts:safeDownload`). * * For IP-literal hosts, `addresses` is the normalised IP as a singleton. For * hostnames it is **every** validated address, IPv4 first, so the connect * layer can race families (Happy Eyeballs). Pinning a single OS-preferred * address breaks every download on IPv4-only networks whenever the resolver * prefers AAAA: the pinned connection has no second address to fall back to, * so it times out where plain `curl` (which races both families) succeeds. * `ip`/`family` mirror the first entry for callers that need a single pin. * * Same throw semantics as {@link assertSafeUrl}. * * This is the canonical entry point for binary downloads where DNS-rebinding * pinning matters — see `safeFetch.ts`. */ export declare function validateAndResolveUrl(url: string): Promise<{ url: string; ip: string; family: 4 | 6; addresses: readonly PinnedAddress[]; }>;