fix(security): pin DNS-rebinding check on user/operator outbound hosts

Add requirePublicHost to the three outbound safeFetch sites whose host is
user- or operator-controlled and lacked the connect-time pin: the OpenAI
and Anthropic clients (BYO base-URL override) and the IP-geo lookup
(IP_GEO_LOOKUP_URL env). A base URL resolving to a private/metadata
address is now rejected. The LOCAL AI client intentionally stays
conditional (LAN escape hatch). Redirect-follow is already closed by the
safeFetch defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-04 12:20:23 +02:00
1 parent fcb41b8780
commit bd61374351
3 files changed
+11 -3

No files matched your search

+3 -1
View File
@@ -54,7 +54,9 @@ export class AnthropicClient implements AIProvider {
}),
},
// 60 s ceiling — see openai-client.ts for the rationale.
{ timeoutMs: 60_000 },
// v1.11.2 — base URL is user/admin-overridable; pin the connect-time DNS
// check so a private/metadata address is rejected (SSRF/rebinding).
{ timeoutMs: 60_000, requirePublicHost: true },
);
if (!res.ok) {
+4 -1
View File
@@ -52,7 +52,10 @@ export class OpenAIClient implements AIProvider {
},
// 60 s ceiling so a tar-pit upstream cannot pin a worker
// indefinitely. Real completions land well inside this budget.
{ timeoutMs: 60_000 },
// v1.11.2 — the base URL is user/admin-overridable (BYO gateway), so pin
// the connect-time DNS check: a base URL resolving to a private/metadata
// address is rejected, closing the SSRF/rebinding surface.
{ timeoutMs: 60_000, requirePublicHost: true },
);
if (!res.ok) {
+4 -1
View File
@@ -300,7 +300,10 @@ async function lookupIpLocationOnline(ip: string): Promise<string | null> {
Accept: "application/json",
},
},
{ timeoutMs: 3_000 },
// v1.11.2 — the lookup URL comes from the operator IP_GEO_LOOKUP_URL env;
// pin the connect-time DNS check so it can't be pointed at a private /
// metadata address (SSRF/rebinding).
{ timeoutMs: 3_000, requirePublicHost: true },
);
if (!res.ok) return null;