diff --git a/src/openenv/auto/auto_env.py b/src/openenv/auto/auto_env.py index 0bbf1a906..7caf23ecb 100644 --- a/src/openenv/auto/auto_env.py +++ b/src/openenv/auto/auto_env.py @@ -31,12 +31,14 @@ from __future__ import annotations import importlib +import ipaddress import logging import os import shutil import subprocess import sys from typing import Any, Dict, Optional, TYPE_CHECKING +from urllib.parse import urlparse import requests from openenv.core.utils import run_async_safely @@ -199,8 +201,18 @@ def _is_local_url(cls, url: str) -> bool: >>> AutoEnv._is_local_url("https://example.com") False """ - url_lower = url.lower() - return "localhost" in url_lower or "127.0.0.1" in url_lower + try: + hostname = urlparse(url).hostname or "" + except Exception: + return False + if not hostname: + return False + if hostname in ("localhost", "0.0.0.0"): + return True + try: + return ipaddress.ip_address(hostname).is_loopback + except ValueError: + return False @classmethod def _check_server_availability(cls, base_url: str, timeout: float = 2.0) -> bool: diff --git a/tests/envs/test_auto_env.py b/tests/envs/test_auto_env.py index 6a597f3c0..c701952b6 100644 --- a/tests/envs/test_auto_env.py +++ b/tests/envs/test_auto_env.py @@ -271,6 +271,46 @@ def test_resolve_space_url_from_full_url(self): assert url == "https://wukaixingxp-coding-env-test.hf.space" +# ============================================================================ +# _is_local_url Tests +# ============================================================================ + + +class TestIsLocalUrl: + """Test AutoEnv._is_local_url() uses proper URL parsing, not substring matching.""" + + @pytest.mark.parametrize( + "url", + [ + "http://localhost:8000", + "http://localhost", + "http://127.0.0.1:8000", + "http://127.0.0.1", + "http://127.5.5.5", # rest of 127.0.0.0/8 loopback range + "http://[::1]:8000", # IPv6 loopback with port + "http://[::1]", # IPv6 loopback bare + "http://0.0.0.0:8000", + "http://0.0.0.0", + ], + ) + def test_local_urls_return_true(self, url): + assert AutoEnv._is_local_url(url) is True + + @pytest.mark.parametrize( + "url", + [ + "http://localhost.evil.com", # headline false-positive bug + "http://my-127.0.0.1.io", # 127.0.0.1 as substring in domain + "https://example.com", + "https://example.com/path?q=localhost", # only in query string + "not-a-url", + "", + ], + ) + def test_non_local_urls_return_false(self, url): + assert AutoEnv._is_local_url(url) is False + + # ============================================================================ # Git+ URL Installation Tests # ============================================================================