mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-13 14:23:58 +00:00
fix(security): normalize IPv6-mapped IPv4 in loopback check, add tests
- Apply _normalize_addr in _is_allowed_loopback_target so ::ffff:127.0.0.1 is correctly identified as loopback - Add test for contains_internal_url with IPv6-mapped addresses - Add test for whitelist + IPv6-mapped CGNAT interaction
This commit is contained in:
parent
13dec9d2c2
commit
288146315e
@ -149,7 +149,7 @@ def _is_allowed_loopback_target(
|
||||
hostname: str,
|
||||
addrs: list[ipaddress.IPv4Address | ipaddress.IPv6Address],
|
||||
) -> bool:
|
||||
if not addrs or not all(addr.is_loopback for addr in addrs):
|
||||
if not addrs or not all(_normalize_addr(addr).is_loopback for addr in addrs):
|
||||
return False
|
||||
normalized = hostname.rstrip(".").lower()
|
||||
if normalized == "localhost":
|
||||
|
||||
@ -7,7 +7,11 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from nanobot.security.network import configure_ssrf_whitelist, contains_internal_url, validate_url_target
|
||||
from nanobot.security.network import (
|
||||
configure_ssrf_whitelist,
|
||||
contains_internal_url,
|
||||
validate_url_target,
|
||||
)
|
||||
|
||||
|
||||
def _fake_resolve(host: str, results: list[str]):
|
||||
@ -155,6 +159,12 @@ def test_loopback_exception_rejects_metadata():
|
||||
assert contains_internal_url("curl http://169.254.169.254/latest/meta-data/", allow_loopback=True)
|
||||
|
||||
|
||||
def test_detects_ipv6_mapped_loopback():
|
||||
"""contains_internal_url must catch IPv6-mapped loopback in shell commands."""
|
||||
with patch("nanobot.security.network.socket.getaddrinfo", _fake_resolve_v6("evil.com", ["::ffff:127.0.0.1"])):
|
||||
assert contains_internal_url("curl http://evil.com/secret")
|
||||
|
||||
|
||||
def test_allows_normal_curl():
|
||||
with patch("nanobot.security.network.socket.getaddrinfo", _fake_resolve("example.com", ["93.184.216.34"])):
|
||||
assert not contains_internal_url("curl https://example.com/api/data")
|
||||
@ -206,3 +216,14 @@ def test_whitelist_invalid_cidr_ignored():
|
||||
assert ok
|
||||
finally:
|
||||
configure_ssrf_whitelist([])
|
||||
|
||||
|
||||
def test_whitelist_allows_ipv6_mapped_cgnat():
|
||||
"""Whitelist must work when DNS returns IPv6-mapped CGNAT address."""
|
||||
configure_ssrf_whitelist(["100.64.0.0/10"])
|
||||
try:
|
||||
with patch("nanobot.security.network.socket.getaddrinfo", _fake_resolve_v6("ts.local", ["::ffff:100.100.1.1"])):
|
||||
ok, err = validate_url_target("http://ts.local/api")
|
||||
assert ok, f"Whitelisted IPv6-mapped CGNAT should be allowed, got: {err}"
|
||||
finally:
|
||||
configure_ssrf_whitelist([])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user