From c4cf2453e2c844e7955f40aa87fcf66e582b52c6 Mon Sep 17 00:00:00 2001 From: didericis Date: Mon, 25 May 2026 16:46:23 -0400 Subject: [PATCH] fix(launch): also set lowercase {http,https,no}_proxy on the agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE-2016-5388 ("httpoxy") mitigation: libcurl ignores uppercase HTTP_PROXY for http:// URLs to prevent untrusted CGI HTTP_* headers from hijacking the proxy. Only lowercase http_proxy is honored for HTTP. Without the lowercase var, plain-HTTP requests from the agent skip egress-proxy entirely — they go direct, which is "network unreachable" on the agent's --internal bridge, not the egress-proxy 403 we expect. Confirmed against a live bottle: `curl http://1.1.1.1/` reported "Immediate connect fail for 1.1.1.1: Network is unreachable" instead of the addon's "host not in allowlist" 403. With both cases set the agent's curl honors the proxy and our allowlist enforcement kicks in. Also set lowercase HTTPS_PROXY + NO_PROXY for symmetry. Some tools check one case only; sending both means we don't have to audit which convention each tool uses. Co-Authored-By: Claude Opus 4.7 --- claude_bottle/backend/docker/launch.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/claude_bottle/backend/docker/launch.py b/claude_bottle/backend/docker/launch.py index 5542343..dc98195 100644 --- a/claude_bottle/backend/docker/launch.py +++ b/claude_bottle/backend/docker/launch.py @@ -240,13 +240,25 @@ def _run_agent_container(plan: DockerBottlePlan, internal_network: str) -> str: conflict races by incrementing the suffix (unless the name was user-pinned). Returns the resolved container name.""" proxy_url = _agent_proxy_url(plan) + no_proxy = _agent_no_proxy(plan) + # Set BOTH cases of every *_PROXY var. libcurl's CVE-2016-5388 + # httpoxy mitigation makes it ignore uppercase `HTTP_PROXY` for + # `http://` URLs and only honor lowercase `http_proxy`. Without + # the lowercase var, plain-HTTP requests from the agent bypass + # egress-proxy entirely (going direct, then failing with + # "network unreachable" because the agent's bridge is + # --internal). Lowercase HTTPS_PROXY isn't strictly needed but + # we set it for symmetry — some tools check one or the other. docker_args: list[str] = [ "--rm", "-d", "--name", plan.container_name, "--network", internal_network, "-e", f"HTTPS_PROXY={proxy_url}", "-e", f"HTTP_PROXY={proxy_url}", - "-e", f"NO_PROXY={_agent_no_proxy(plan)}", + "-e", f"https_proxy={proxy_url}", + "-e", f"http_proxy={proxy_url}", + "-e", f"NO_PROXY={no_proxy}", + "-e", f"no_proxy={no_proxy}", # CA trust trio for the agent process. Docker propagates # run-time env into `docker exec`, so `claude` sees these # without per-exec threading. NODE_EXTRA_CA_CERTS points at