fix: is_local_endpoint misses Docker/Podman DNS names (#7950)

* fix(tools): neutralize shell injection in _write_to_sandbox via path quoting

_write_to_sandbox interpolated storage_dir and remote_path directly into
a shell command passed to env.execute(). Paths containing shell
metacharacters (spaces, semicolons, $(), backticks) could trigger
arbitrary command execution inside the sandbox.

Fix: wrap both paths with shlex.quote(). Clean paths (alphanumeric +
slashes/hyphens/dots) are left unmodified by shlex.quote, so existing
behavior is unchanged. Paths with unsafe characters get single-quoted.

Tests added for spaces, $(command) substitution, and semicolon injection.

* fix: is_local_endpoint misses Docker/Podman DNS names

host.docker.internal, host.containers.internal, gateway.docker.internal,
and host.lima.internal are well-known DNS names that container runtimes
use to resolve the host machine. Users running Ollama on the host with
the agent in Docker/Podman hit the default 120s stream timeout instead
of the bumped 1800s because these hostnames weren't recognized as local.

Add _CONTAINER_LOCAL_SUFFIXES tuple and suffix check in
is_local_endpoint(). Tests cover all three runtime families plus a
negative case for domains that merely contain the suffix as a substring.
This commit is contained in:
Teknium 2026-04-11 14:46:18 -07:00 committed by GitHub
parent 1e5056ec30
commit 8c3935ebe8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 0 deletions

View file

@ -22,6 +22,9 @@ class TestLocalStreamReadTimeout:
"http://0.0.0.0:5000",
"http://192.168.1.100:8000",
"http://10.0.0.5:1234",
"http://host.docker.internal:11434",
"http://host.containers.internal:11434",
"http://host.lima.internal:11434",
])
def test_local_endpoint_bumps_read_timeout(self, base_url):
"""Local endpoint + default timeout -> bumps to base_timeout."""
@ -68,3 +71,38 @@ class TestLocalStreamReadTimeout:
if _stream_read_timeout == 120.0 and base_url and is_local_endpoint(base_url):
_stream_read_timeout = _base_timeout
assert _stream_read_timeout == 120.0
class TestIsLocalEndpoint:
"""Direct unit tests for is_local_endpoint."""
@pytest.mark.parametrize("url", [
"http://localhost:11434",
"http://127.0.0.1:8080",
"http://0.0.0.0:5000",
"http://[::1]:11434",
"http://192.168.1.100:8000",
"http://10.0.0.5:1234",
"http://172.17.0.1:11434",
])
def test_classic_local_addresses(self, url):
assert is_local_endpoint(url) is True
@pytest.mark.parametrize("url", [
"http://host.docker.internal:11434",
"http://host.docker.internal:8080/v1",
"http://gateway.docker.internal:11434",
"http://host.containers.internal:11434",
"http://host.lima.internal:11434",
])
def test_container_dns_names(self, url):
assert is_local_endpoint(url) is True
@pytest.mark.parametrize("url", [
"https://api.openai.com",
"https://openrouter.ai/api",
"https://api.anthropic.com",
"https://evil.docker.internal.example.com",
])
def test_remote_endpoints(self, url):
assert is_local_endpoint(url) is False