diff --git a/nanobot/security/network.py b/nanobot/security/network.py index 95523f3a..23daf980 100644 --- a/nanobot/security/network.py +++ b/nanobot/security/network.py @@ -20,6 +20,7 @@ _BLOCKED_NETWORKS = [ ipaddress.ip_network("169.254.0.0/16"), # link-local / cloud metadata ipaddress.ip_network("172.16.0.0/12"), ipaddress.ip_network("192.168.0.0/16"), + ipaddress.ip_network("::/128"), # unspecified; may route to local host ipaddress.ip_network("::1/128"), ipaddress.ip_network("fc00::/7"), # unique local ipaddress.ip_network("fe80::/10"), # link-local v6 diff --git a/tests/providers/test_image_generation_security.py b/tests/providers/test_image_generation_security.py index 466b74cd..44b586a7 100644 --- a/tests/providers/test_image_generation_security.py +++ b/tests/providers/test_image_generation_security.py @@ -28,8 +28,13 @@ def _resolve_public(host: str, port: int | None, *args, **kwargs): ] +@pytest.mark.parametrize( + "url", + ["http://127.0.0.1/admin", "http://[::]/admin"], + ids=["ipv4-loopback", "ipv6-unspecified"], +) @pytest.mark.asyncio -async def test_generated_image_download_blocks_private_target() -> None: +async def test_generated_image_download_blocks_unsafe_target(url: str) -> None: requested = False async def handler(request: httpx.Request) -> httpx.Response: @@ -39,7 +44,7 @@ async def test_generated_image_download_blocks_private_target() -> None: with pytest.raises(ImageGenerationError, match="blocked unsafe generated image URL"): await _download_image_data_url( - "http://127.0.0.1/admin", + url, transport=httpx.MockTransport(handler), ) diff --git a/tests/security/test_security_network.py b/tests/security/test_security_network.py index fc4ea767..9b71022f 100644 --- a/tests/security/test_security_network.py +++ b/tests/security/test_security_network.py @@ -148,6 +148,7 @@ def test_blocks_sampled_addresses_from_internal_networks(): "169.254.0.0/16", "172.16.0.0/12", "192.168.0.0/16", + "::/128", "::1/128", "fc00::/7", "fe80::/10",