fix(resilience): include RFC1918, CGNAT, and mDNS hosts in isLocalProvider (#11091) (#11122)

Cherry-picked both value commits (a1fa49a8 + a91f6e93, incl. the fail-open guard for unparseable hostnames) onto the current tip, dropping the stale base-red sync commits. Focused tests: is-local-provider-11091 + the isLocalProvider consumer suites (provider-validation-specialty, ollama-local-provider) 127/127; mutation coverage no-drift. Fixes #11091 — RFC1918/CGNAT/link-local/mDNS hosts now classify as local via the existing isPrivateHost. Thank you @rqzbeh!
This commit is contained in:
Rouzbeh†
2026-08-22 22:38:22 +03:30
committed by GitHub
parent 50fc0d7299
commit 4220c810ee
2 changed files with 41 additions and 5 deletions

View File

@@ -10,6 +10,7 @@ export {
} from "./providers/registry/alibaba/index.ts";
export { REGISTRY } from "./providers/index.ts";
import { REGISTRY } from "./providers/index.ts";
import { isPrivateHost } from "@/shared/network/outboundUrlGuard";
import {
RegistryModel,
REASONING_UNSUPPORTED,
@@ -132,11 +133,8 @@ export function isLocalProvider(baseUrl?: string | null): boolean {
try {
const url = new URL(baseUrl);
const hostname = url.hostname;
// Strictly matching 172.16.0.0/12 (Docker/local) and explicitly blocking ::1 per SSRF hardening
return (
LOCAL_HOSTNAMES.has(hostname) ||
/^172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}$/.test(hostname)
);
if (!hostname) return false;
return LOCAL_HOSTNAMES.has(hostname) || isPrivateHost(hostname);
} catch {
return false;
}

View File

@@ -0,0 +1,38 @@
import test from "node:test";
import assert from "node:assert/strict";
import { isLocalProvider } from "../../open-sse/config/providerRegistry.ts";
test("isLocalProvider detects RFC1918, CGNAT/Tailscale, and mDNS private hosts", () => {
// Local / loopback
assert.equal(isLocalProvider("http://localhost:11434/v1"), true);
assert.equal(isLocalProvider("http://127.0.0.1:11434/v1"), true);
// Docker 172.16/12
assert.equal(isLocalProvider("http://172.18.0.2:11434/v1"), true);
// RFC1918 LAN hosts (Issue #11091)
assert.equal(isLocalProvider("http://192.168.1.50:11434/v1"), true);
assert.equal(isLocalProvider("http://10.0.0.5:11434/v1"), true);
// Tailscale / CGNAT (100.64/10)
assert.equal(isLocalProvider("http://100.64.1.2:11434/v1"), true);
// Link-local (169.254/16)
assert.equal(isLocalProvider("http://169.254.1.1:11434/v1"), true);
// mDNS / private suffixes
assert.equal(isLocalProvider("http://studio.local:11434/v1"), true);
assert.equal(isLocalProvider("http://mybox.internal:11434/v1"), true);
// Public hosts (should be false)
assert.equal(isLocalProvider("https://api.openai.com/v1"), false);
assert.equal(isLocalProvider("https://api.anthropic.com/v1"), false);
assert.equal(isLocalProvider("http://8.8.8.8:8080/v1"), false);
// Fails open on missing or unparseable input (Issue #11091 review finding)
assert.equal(isLocalProvider(null), false);
assert.equal(isLocalProvider(undefined), false);
assert.equal(isLocalProvider(""), false);
assert.equal(isLocalProvider("not a url"), false);
assert.equal(isLocalProvider("file:///models"), false);
});