/** * @license * Copyright 2025 Steven Roussey * SPDX-License-Identifier: Apache-2.0 * * URL classifier used by {@link safeFetch} and {@link FetchUrlTask} to decide * whether a URL targets a private/internal network host. * * The classifier is browser-safe (no Node built-ins) and performs only static * analysis of the URL string — it does NOT perform DNS resolution. DNS-aware * checking + connection pinning happens in `SafeFetch.server.ts` on Node/Bun. */ export type UrlClassificationKind = "public" | "private" | "invalid"; export interface UrlClassification { readonly kind: UrlClassificationKind; /** Human-readable reason, present when kind is "private" or "invalid". */ readonly reason?: string; /** Normalized hostname (lowercase, trailing dots stripped, IPv6 brackets stripped). */ readonly host?: string; /** Canonical dotted-quad IPv4 / RFC5952 IPv6 if the host is a literal IP. */ readonly literalIp?: string; } /** * Accepts an IPv4 literal in any of the legacy forms accepted by inet_aton * (decimal, hex `0x..`, octal `0..`, and 1/2/3/4-part notation) and returns * its canonical dotted-quad form. Returns undefined if the host is not a * valid IPv4 literal in any supported form. * * Examples: * "127.0.0.1" → "127.0.0.1" * "2130706433" → "127.0.0.1" * "0x7f000001" → "127.0.0.1" * "0177.0.0.1" → "127.0.0.1" * "0x7f.1" → "127.0.0.1" */ export declare function tryNormalizeIPv4(host: string): string | undefined; /** * Classifies a literal IP address (v4 or v6) as public or private. * Returns undefined if the address is not a valid IP literal. */ export declare function classifyIpLiteral(host: string): { kind: "public" | "private"; reason?: string; canonical: string; } | undefined; /** * Statically classify a URL as public, private, or invalid. * * This is a pure string-level check — it does NOT perform DNS resolution. * For DNS-aware protection against rebinding, use {@link safeFetch}. */ export declare function classifyUrl(urlStr: string): UrlClassification; /** * Returns the entitlement resource pattern for a URL, used when declaring * a scoped `network:private` entitlement. The pattern covers any path/query * under the same protocol+host(+port). * * Examples: * http://localhost:3000/api → "http://localhost:3000/*" * https://192.168.1.1/admin → "https://192.168.1.1/*" * http://[::1]:8080/ → "http://[::1]:8080/*" */ export declare function urlResourcePattern(urlStr: string): string; /** * Checks whether a (possibly post-redirect) URL falls within one of the * scoped resource patterns granted to the task. The URL is canonicalized so * minor syntactic differences (uppercase host, default port) cannot bypass the * check. The hostname is additionally normalized via `normalizeHost` (strips * trailing dots, lowercases) so that `example.internal.` and `example.internal` * are treated identically. Returns true when at least one pattern matches the * canonical URL via `resourcePatternMatches`. Returns false on URL parse errors * (fail closed). * * Used by `safeFetch` to enforce the task's declared `network:private` * resource scope on every redirect hop, preventing a compromised upstream * from walking across private hosts/ports via Location headers. */ export declare function urlMatchesScope(url: string, patterns: readonly string[]): boolean;