From 12bd8fb99ff32ec8b35172b57c101b0a396d3372 Mon Sep 17 00:00:00 2001 From: John Menke Date: Sun, 1 Nov 2026 14:16:41 -0500 Subject: [PATCH] Fail committed URLs whose host is an integer-form address. Decimal and hex hosts never matched the dotted-quad scan, so a public address written that way passed. Co-authored-by: Cursor --- src/local_coding_slm/safety.py | 31 ++++++++++++++++++++++++++++--- tests/test_safety.py | 9 +++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/local_coding_slm/safety.py b/src/local_coding_slm/safety.py index 4f4f6a6..cd75ce1 100644 --- a/src/local_coding_slm/safety.py +++ b/src/local_coding_slm/safety.py @@ -58,6 +58,10 @@ ) IPV4_RE = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b") +_INTEGER_URL_HOST_RE = re.compile( + r"https?://(0x[0-9a-f]+|\d+)(?=[:/?#]|\s|$)", + re.IGNORECASE, +) UNSAFE_MODEL_RE = re.compile(r"://|[\\/]|\.\.|@", re.IGNORECASE) # Decimal 2130706433 or 0x7f000001 disguise a dotted IPv4. _INTEGER_FORM_IP_RE = re.compile(r"^(?:\d+|0x[0-9a-f]+)$", re.IGNORECASE) @@ -286,13 +290,34 @@ def _is_committed_ip_hit(match: str) -> bool: return bool(addr.is_private or addr.is_global) +def _integer_host_is_hit(host: str) -> bool: + """Decimal and hex URL hosts are IPv4, including values ipaddress dotted form rejects.""" + try: + number = int(host, 16) if host.lower().startswith("0x") else int(host, 10) + except ValueError: + return False + if number > 0xFFFFFFFF: + return False + addr = ipaddress.IPv4Address(number) + if addr.is_loopback or addr.is_unspecified: + return False + return bool(addr.is_private or addr.is_global) + + +def _committed_ip_matches(text: str) -> list[str]: + matches = [match for match in IPV4_RE.findall(text) if _is_committed_ip_hit(match)] + for host in _INTEGER_URL_HOST_RE.findall(text): + if _integer_host_is_hit(host): + matches.append(host) + return matches + + def classify_tracked_ipv4(paths_and_text: Iterable[tuple[str, str]]) -> CheckResult: """Fail committed files that contain non-placeholder private or public IPs.""" hits: list[str] = [] for path, text in paths_and_text: - for match in IPV4_RE.findall(text): - if _is_committed_ip_hit(match): - hits.append(f"{path}:{match}") + for match in _committed_ip_matches(text): + hits.append(f"{path}:{match}") if hits: return CheckResult( "committed_ips", diff --git a/tests/test_safety.py b/tests/test_safety.py index f4ee001..70c04c6 100644 --- a/tests/test_safety.py +++ b/tests/test_safety.py @@ -179,6 +179,15 @@ def test_leading_zero_ipv4_fails(self) -> None: self.assertEqual(result.status, "fail") self.assertIn(text, result.message) + def test_integer_form_url_host_fails(self) -> None: + """Decimal and hex URL hosts are addresses even though they are not dotted quads.""" + for text in ("http://134744072:11434", "http://0x08080808/"): + with self.subTest(text=text): + result = classify_tracked_ipv4([("README.md", f"OLLAMA_BASE_URL={text}")]) + self.assertEqual(result.status, "fail") + loopback = classify_tracked_ipv4([("README.md", "see http://2130706433/")]) + self.assertEqual(loopback.status, "pass") + def test_committed_workflow_yaml_ip_fails(self) -> None: """GitHub workflow YAML is committed text, so an address there must fail.""" import subprocess