From f737392867b7e7cb04f7c3098d0b4b4aec47fb6b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 15 Jul 2026 12:57:48 -0700 Subject: [PATCH] fix(cli): bind the ephemeral autoroute proxy to loopback only proxy_cli.py defaults --host to 0.0.0.0 when not passed explicitly. launch_proxy never passed it, so the ephemeral proxy -- despite every base_url in this module being built from 127.0.0.1 -- was actually reachable from other hosts on the network, including its unauthenticated-until-config-lands routes before the master key is wired in. --- .../client/cli/commands/autoroute/process.py | 12 +++++++++++- .../proxy/client/cli/autoroute/test_process.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/client/cli/commands/autoroute/process.py b/litellm/proxy/client/cli/commands/autoroute/process.py index d1b798c40cf..e1cd38531d5 100644 --- a/litellm/proxy/client/cli/commands/autoroute/process.py +++ b/litellm/proxy/client/cli/commands/autoroute/process.py @@ -47,7 +47,17 @@ def launch_proxy(config_path: Path, port: int, log_path: Path) -> "subprocess.Po log_path.parent.mkdir(parents=True, exist_ok=True) with open(log_path, "w") as log_file: return subprocess.Popen( - [sys.executable, "-m", "litellm.proxy.proxy_cli", "--config", str(config_path), "--port", str(port)], + [ + sys.executable, + "-m", + "litellm.proxy.proxy_cli", + "--config", + str(config_path), + "--port", + str(port), + "--host", + "127.0.0.1", + ], stdout=log_file, stderr=subprocess.STDOUT, ) diff --git a/tests/test_litellm/proxy/client/cli/autoroute/test_process.py b/tests/test_litellm/proxy/client/cli/autoroute/test_process.py index bffd8b598f4..d182ebfffc2 100644 --- a/tests/test_litellm/proxy/client/cli/autoroute/test_process.py +++ b/tests/test_litellm/proxy/client/cli/autoroute/test_process.py @@ -1,6 +1,7 @@ import os import socket from typing import Optional +from unittest.mock import patch import pytest @@ -11,6 +12,7 @@ from litellm.proxy.client.cli.commands.autoroute.process import ( allocate_free_port, clear_pid_record, is_running, + launch_proxy, poll_liveliness, read_pid_record, write_pid_record, @@ -36,6 +38,22 @@ def test_allocate_free_port_returns_a_bindable_port(): sock.bind(("127.0.0.1", port)) +class TestLaunchProxy: + def test_binds_loopback_only_not_all_interfaces(self, tmp_path): + """proxy_cli.py's own --host default is 0.0.0.0 -- without an explicit override here, the + ephemeral proxy would be reachable from other hosts on the network despite base_url always + being built from 127.0.0.1, exposing its unauthenticated-until-master-key-lands routes.""" + config_path = tmp_path / "config.yaml" + log_path = tmp_path / "proxy.log" + + with patch.object(process_module.subprocess, "Popen") as mock_popen: + launch_proxy(config_path, 12345, log_path) + + args = mock_popen.call_args[0][0] + assert "--host" in args + assert args[args.index("--host") + 1] == "127.0.0.1" + + class TestPidRecordRoundTrip: def test_write_then_read_round_trips(self, tmp_path): path = tmp_path / "pid.json"