/** * Worker network egress guard. * * Project workers need outbound network access for public API calls, but user * code must not reach host-internal networks or cloud metadata endpoints. * * Self-hosted deployments can open narrow exceptions: * * - `VERYFRONT_WORKER_ALLOWED_INTERNAL_HOSTS` (or the `allowedInternalHosts` * option, which takes precedence) holds a comma-separated list of DNS * hostnames that may resolve to internal addresses. Entries and the * requested host are both normalized (trimmed, lowercased, IPv6 brackets * stripped) before an exact-match comparison; only the listed hosts bypass * the guard. IP literals and localhost names are ignored on the allowlist * and never bypass the guard through it. * - `VERYFRONT_WORKER_ALLOW_INTERNAL_EGRESS` (or the `allowInternalEgress` * option) disables internal-address blocking entirely and should be reserved * for fully trusted self-hosted environments. * * @module security/sandbox/worker-egress-guard */ import * as dntShim from "../../../_dnt.shims.js"; export declare const WORKER_INTERNAL_EGRESS_OVERRIDE_ENV = "VERYFRONT_WORKER_ALLOW_INTERNAL_EGRESS"; export declare const WORKER_INTERNAL_EGRESS_ALLOWED_HOSTS_ENV = "VERYFRONT_WORKER_ALLOWED_INTERNAL_HOSTS"; export declare class WorkerEgressBlockedError extends Error { name: string; } export type ResolveWorkerHost = (hostname: string) => Promise; export interface WorkerEgressSocksProxyConfig { hostname: string; port: number; username: string; password: string; } export interface WorkerEgressHttpBrokerConfig { url: string; token: string; } export interface WorkerEgressGuardOptions { allowInternalEgress?: boolean; allowedInternalHosts?: readonly string[]; resolveHost?: ResolveWorkerHost; socksProxy?: WorkerEgressSocksProxyConfig; httpBroker?: WorkerEgressHttpBrokerConfig; } export type InstalledWorkerEgressGuardOptions = WorkerEgressGuardOptions & { allowInternalEgress: boolean; }; export type WorkerEgressTcpConnect = (options: dntShim.Deno.ConnectOptions) => Promise; export type WorkerEgressTcpListen = (options: dntShim.Deno.ListenOptions) => dntShim.Deno.TcpListener; export type WorkerEgressStartTls = (conn: dntShim.Deno.TcpConn, options?: dntShim.Deno.StartTlsOptions) => Promise; export type WorkerEgressCreateHttpClient = (options: dntShim.Deno.CreateHttpClientOptions | (dntShim.Deno.CreateHttpClientOptions & dntShim.Deno.TlsCertifiedKeyPem)) => dntShim.Deno.HttpClient; export interface PinnedEgressRuntime { connect: WorkerEgressTcpConnect; listen: WorkerEgressTcpListen; startTls: WorkerEgressStartTls; createHttpClient: WorkerEgressCreateHttpClient; } export declare function isInternalEgressOverrideEnabled(value: string | undefined): boolean; export declare function isInternalEgressIp(address: string): boolean; export declare function assertWorkerEgressAllowed(target: string | URL | Request, options?: WorkerEgressGuardOptions): Promise; export declare function assertWorkerHostEgressAllowed(hostname: string, options?: WorkerEgressGuardOptions): Promise; export interface WorkerEgressSocksProxy { config: WorkerEgressSocksProxyConfig; close(): void; /** Resolves after the listener and every admitted connection have stopped. */ closed: Promise; } export declare function startWorkerEgressSocksProxy(options?: WorkerEgressGuardOptions, runtimeOverride?: Partial): WorkerEgressSocksProxy; /** Fetch shape consumed by the worker egress guard. */ export type WorkerEgressFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise; /** DNS-pinned transport seam used after the guard validates every address. */ export type WorkerEgressPinnedFetch = (url: URL, addresses: readonly string[], init: RequestInit) => Promise; /** Redirect hop whose guarded destination request returned a response. */ export interface WorkerEgressRedirect { status: number; fromUrl: URL; toUrl: URL; } /** Dependencies for {@link guardedEgressFetch} (injectable for tests). */ export interface GuardedEgressFetchDeps { /** Underlying fetch implementation (defaults to the global `fetch`). */ fetchImpl?: WorkerEgressFetch; /** * Trusted DNS-pinned transport replacement. The guard still resolves and * validates every address before invoking this seam. */ pinnedFetch?: WorkerEgressPinnedFetch; /** Egress options applied to the initial URL and every redirect hop. */ options?: WorkerEgressGuardOptions; /** * Optional caller-owned policy applied to the initial URL and every * redirect destination before a connection is opened. */ authorizeUrl?: (url: URL) => void | Promise; /** Observe each redirect after its guarded destination request succeeds. */ onRedirect?: (redirect: WorkerEgressRedirect) => void | Promise; /** Captured runtime primitives used to establish the DNS-pinned tunnel. */ runtime?: Partial; } /** * Egress-checked fetch that re-validates EVERY redirect hop. * * The platform `fetch` follows 3xx redirects transparently, so checking only the * initial URL lets a public host redirect to an internal address (loopback, * RFC1918, link-local, cloud metadata) that the guard never sees. This forces * `redirect: "manual"` on the underlying fetch and re-runs the egress check on * each `Location` before following it. The caller's redirect intent is honored: * `manual` returns the redirect unfollowed, `error` throws, `follow` (default) * follows manually after re-checking. Credential headers are stripped on a * cross-origin hop, matching the platform fetch the guard wraps. */ export declare function guardedEgressFetch(input: RequestInfo | URL, init?: RequestInit, deps?: GuardedEgressFetchDeps): Promise; export interface WorkerEgressBrokerConfig { socksProxy: WorkerEgressSocksProxyConfig; httpBroker: WorkerEgressHttpBrokerConfig; netAllowlist: string[]; } export interface WorkerEgressBroker { config: WorkerEgressBrokerConfig; close(): void; /** Resolves after both listeners and every admitted request have stopped. */ closed: Promise; } export declare function startWorkerEgressBroker(options?: WorkerEgressGuardOptions, runtimeOverride?: Partial): WorkerEgressBroker; export declare function guardedWorkerConnect(connectOptions: dntShim.Deno.ConnectOptions, options?: WorkerEgressGuardOptions, runtimeOverride?: Partial): Promise; export declare function guardedWorkerConnectTls(connectOptions: dntShim.Deno.ConnectTlsOptions | (dntShim.Deno.ConnectTlsOptions & dntShim.Deno.TlsCertifiedKeyPem), options?: WorkerEgressGuardOptions, runtimeOverride?: Partial): Promise; export declare function installWorkerEgressGuard(options: InstalledWorkerEgressGuardOptions): void; //# sourceMappingURL=worker-egress-guard.d.ts.map