/** * SSRF guard for voice / media-understanding HTTP calls. * * DECISION (per docs/voice-rearchitecture.md §6): * - xopc does not currently ship the full openclaw SSRF infra (pinned dispatcher, * legacy private-network opt-in, etc.). For v2.0 we adopt a deliberately lean * guard that is sufficient for "developer-controlled provider base URLs": * 1. Reject non-http(s) schemes. * 2. Reject IP literals or hostnames that resolve to private / loopback / * link-local / metadata addresses unless `allowPrivateNetwork` is true. * 3. Resolve the hostname BEFORE handing the request to fetch (DNS pinning) * and reuse the resolved IP for the actual connection — closes the * classic DNS-rebinding window without requiring a custom dispatcher. * - Why not port openclaw's pinned dispatcher (undici): xopc is committed to * plain `globalThis.fetch` (Node 22 native) per AGENTS.md "Node >= 22"; we * accept the slightly weaker guarantee in exchange for zero new dependencies. * - Tests under src/media-shared/http/__tests__/ssrf-guard.test.ts cover all * private CIDR ranges + IPv6 link-local + metadata IPs (169.254.169.254). */ export declare class SsrfBlockedError extends Error { readonly url: string; readonly reason: string; constructor(url: string, reason: string); } export interface SsrfGuardOptions { /** Opt-in: allow private/loopback/link-local hosts. Default false. */ allowPrivateNetwork?: boolean; /** * Optional explicit hostname allowlist. If set, ONLY these hostnames are * permitted regardless of `allowPrivateNetwork`. Useful for production * deployments that pin to one or two known provider domains. */ hostnameAllowlist?: readonly string[]; } export declare function isPrivateIpv4(ip: string): boolean; export declare function isPrivateIpv6(ip: string): boolean; export declare function isPrivateIpAddress(ip: string): boolean; export interface ResolvedSsrfTarget { /** The original URL string. */ url: string; /** The resolved IP that should actually be connected to. */ resolvedIp: string; /** The address family (4 or 6). */ family: 4 | 6; } /** * Validate `url`, resolve its hostname via DNS, and return the resolved IP so * the caller can pin the request to that exact IP (closes DNS-rebinding window). * * Throws SsrfBlockedError when: * - scheme is not http(s) * - hostname is an explicit IP literal that targets a private range (and the * caller did not opt in) * - hostname resolves to a private IP (and the caller did not opt in) * - hostnameAllowlist is set and the hostname is not on it */ export declare function assertSafeUrl(url: string | URL, options?: SsrfGuardOptions): Promise;