/** * Shared HTTP security helpers used by both the REST adapter and the MCP * Streamable HTTP transport. Keeping them in one place avoids drift between * the two servers (e.g., one being upgraded with a new defense and the * other forgotten). */ import http from "http"; import type { Logger } from "../../logger.js"; import { parseJsonBody } from "../util.js"; export declare const DEFAULT_HOST = "127.0.0.1"; export declare function isLoopbackHost(host: string): boolean; export declare function defaultAllowedHosts(host: string): string[] | undefined; /** * Validate the Host header against an allowlist (DNS-rebinding defense). * Strips the port before comparison; matches case-insensitively. If * `allowed` is undefined, validation is skipped. */ export declare function isHostAllowed(hostHeader: string | undefined, allowed: string[] | undefined): boolean; /** * Throws if no API key is configured but the bind host is non-loopback. * Call this before binding to surface the misconfiguration as early as * possible — startup time, not first request. */ export declare function enforceNoKeyOnNonLoopback(host: string, apiKey: string | undefined): void; /** * Logs the standard "listening on …" message plus warnings about * non-loopback bind and missing API key. */ export declare function logServerStart(logger: Logger, serverLabel: string, host: string, port: number, apiKey: string | undefined): void; export type GuardedRequestContext = { method: string; path: string; start: number; sendJson: (status: number, body: unknown) => void; /** * Send an empty-body response (e.g. 202, 204, 405). Use this instead of * `sendJson` when the HTTP status forbids a body or when extra headers * (such as `Allow:` on a 405) need to be set without a JSON envelope. */ sendStatus: (status: number, headers?: Record) => void; }; /** * Wraps an http.Server request handler with the standard pipeline: * 1. Host header validation (403 on mismatch) * 2. Authentication (401 on mismatch) * 3. Standard error envelope for body-parse errors / 500s * * The inner handler is only invoked once host + auth have passed. */ export declare function makeGuardedRequestListener(opts: { logger: Logger; apiKey: string | undefined; allowedHosts: string[] | undefined; /** Inner handler invoked after host + auth have passed. */ inner: (req: http.IncomingMessage, res: http.ServerResponse, ctx: GuardedRequestContext) => Promise; }): http.RequestListener; export { parseJsonBody };