mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:10:45 +00:00
fix(agents): broaden local-baseUrl detection to 127/8 and RFC 6598 CGNAT
This commit is contained in:
@@ -89,16 +89,43 @@ describe("resolveLlmIdleTimeoutMs", () => {
|
||||
it.each([
|
||||
"http://localhost:11434",
|
||||
"http://127.0.0.1:11434",
|
||||
"http://127.0.0.2:11434",
|
||||
"http://127.255.255.254:11434",
|
||||
"http://0.0.0.0:11434",
|
||||
"http://[::1]:11434",
|
||||
"http://my-rig.local:11434",
|
||||
"http://10.0.0.5:11434",
|
||||
"http://172.16.5.10:11434",
|
||||
"http://172.31.99.1:11434",
|
||||
"http://192.168.1.20:11434",
|
||||
"http://100.64.0.5:11434",
|
||||
"http://100.127.255.254:11434",
|
||||
])("disables the default idle watchdog for local provider baseUrl %s", (baseUrl) => {
|
||||
expect(resolveLlmIdleTimeoutMs({ model: { baseUrl } })).toBe(0);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"http://172.32.0.1:11434",
|
||||
"http://192.169.1.1:11434",
|
||||
"http://100.63.255.254:11434",
|
||||
"http://100.128.0.1:11434",
|
||||
])("keeps the default idle watchdog for non-private IPv4 baseUrl %s", (baseUrl) => {
|
||||
expect(resolveLlmIdleTimeoutMs({ model: { baseUrl } })).toBe(DEFAULT_LLM_IDLE_TIMEOUT_MS);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"http://10.0.0.5evil:11434",
|
||||
"http://127.0.0.1foo:11434",
|
||||
"http://192.168.1.20attacker.com:11434",
|
||||
"http://10.0.0.5.evil.com:11434",
|
||||
"http://1.2.3.4.5:11434",
|
||||
])(
|
||||
"keeps the default idle watchdog for numeric-looking hostnames that are not IPv4 literals (%s)",
|
||||
(baseUrl) => {
|
||||
expect(resolveLlmIdleTimeoutMs({ model: { baseUrl } })).toBe(DEFAULT_LLM_IDLE_TIMEOUT_MS);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps the default idle watchdog for remote provider baseUrls", () => {
|
||||
expect(resolveLlmIdleTimeoutMs({ model: { baseUrl: "https://api.openai.com/v1" } })).toBe(
|
||||
DEFAULT_LLM_IDLE_TIMEOUT_MS,
|
||||
|
||||
@@ -33,7 +33,6 @@ function isLocalProviderBaseUrl(baseUrl: string): boolean {
|
||||
}
|
||||
if (
|
||||
host === "localhost" ||
|
||||
host === "127.0.0.1" ||
|
||||
host === "0.0.0.0" ||
|
||||
host === "::1" ||
|
||||
host === "::ffff:7f00:1" ||
|
||||
@@ -42,13 +41,26 @@ function isLocalProviderBaseUrl(baseUrl: string): boolean {
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// Require a strict IPv4 literal before parsing; `Number.parseInt` is
|
||||
// permissive and would otherwise let `10.0.0.5evil` parse to [10,0,0,5]
|
||||
// and disable the watchdog for a non-IP hostname.
|
||||
if (!/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
|
||||
return false;
|
||||
}
|
||||
const octets = host.split(".").map((part) => Number.parseInt(part, 10));
|
||||
if (octets.length !== 4 || octets.some((p) => !Number.isInteger(p) || p < 0 || p > 255)) {
|
||||
if (octets.some((p) => !Number.isInteger(p) || p < 0 || p > 255)) {
|
||||
return false;
|
||||
}
|
||||
const [a, b] = octets;
|
||||
// RFC 5735 loopback (127/8 — full range, not just .0.1; container/sandbox
|
||||
// setups commonly bind 127.0.0.2+), RFC 1918 private IPv4, and RFC 6598
|
||||
// shared CGNAT (100.64/10 — used by Tailscale and similar mesh VPNs).
|
||||
return (
|
||||
a === 10 || (a === 172 && b !== undefined && b >= 16 && b <= 31) || (a === 192 && b === 168)
|
||||
a === 127 ||
|
||||
a === 10 ||
|
||||
(a === 172 && b !== undefined && b >= 16 && b <= 31) ||
|
||||
(a === 192 && b === 168) ||
|
||||
(a === 100 && b !== undefined && b >= 64 && b <= 127)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user