mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-22 23:22:09 +03:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
38
tests/unit/is-local-provider-11091.test.ts
Normal file
38
tests/unit/is-local-provider-11091.test.ts
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user