feat: add CIDR/netmask support for trusted proxy IPs

TRUSTED_PROXY_IPS now accepts CIDR notation (e.g., 192.168.1.0/24)
in addition to exact IP addresses. Supports both IPv4 and IPv6.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-03 11:46:04 +00:00
parent 240cf440cb
commit f2145e4827
4 changed files with 250 additions and 5 deletions

View File

@@ -227,6 +227,109 @@ class TestTrustedProxyIPs:
settings = Settings()
assert settings.trusted_proxy_ips == []
def test_cidr_notation_parsed(self):
"""CIDR notation should be parsed into networks."""
import ipaddress
with patch.dict(
os.environ,
{"TRUSTED_PROXY_IPS": "192.168.1.0/24", "CSRF_SECRET": "test-secret"},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
networks = settings.trusted_proxy_networks
assert len(networks) == 1
assert isinstance(networks[0], ipaddress.IPv4Network)
assert str(networks[0]) == "192.168.1.0/24"
def test_plain_ip_parsed_as_single_host_network(self):
"""Plain IP should be parsed as /32 network."""
import ipaddress
with patch.dict(
os.environ,
{"TRUSTED_PROXY_IPS": "10.0.0.1", "CSRF_SECRET": "test-secret"},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
networks = settings.trusted_proxy_networks
assert len(networks) == 1
assert isinstance(networks[0], ipaddress.IPv4Network)
# Plain IP becomes /32 network
assert networks[0].num_addresses == 1
def test_mixed_ips_and_cidrs(self):
"""Mix of plain IPs and CIDR notation should all be parsed."""
import ipaddress
with patch.dict(
os.environ,
{
"TRUSTED_PROXY_IPS": "10.0.0.1,192.168.0.0/16,172.16.0.0/12",
"CSRF_SECRET": "test-secret",
},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
networks = settings.trusted_proxy_networks
assert len(networks) == 3
# All should be network objects
assert all(
isinstance(n, (ipaddress.IPv4Network, ipaddress.IPv6Network)) for n in networks
)
def test_ipv6_cidr_supported(self):
"""IPv6 CIDR notation should be supported."""
import ipaddress
with patch.dict(
os.environ,
{"TRUSTED_PROXY_IPS": "::1,fd00::/8", "CSRF_SECRET": "test-secret"},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
networks = settings.trusted_proxy_networks
assert len(networks) == 2
assert any(isinstance(n, ipaddress.IPv6Network) for n in networks)
def test_invalid_cidr_becomes_literal(self):
"""Invalid CIDR notation should become a literal for string matching."""
with patch.dict(
os.environ,
{"TRUSTED_PROXY_IPS": "192.168.1.0/33", "CSRF_SECRET": "test-secret"},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
# Invalid CIDR should not appear in networks
assert len(settings.trusted_proxy_networks) == 0
# But should appear in literals
assert "192.168.1.0/33" in settings.trusted_proxy_literals
def test_invalid_ip_becomes_literal(self):
"""Invalid IP address should become a literal for string matching."""
with patch.dict(
os.environ,
{"TRUSTED_PROXY_IPS": "not-an-ip", "CSRF_SECRET": "test-secret"},
clear=True,
):
from animaltrack.config import Settings
settings = Settings()
# Invalid IP should not appear in networks
assert len(settings.trusted_proxy_networks) == 0
# But should appear in literals
assert "not-an-ip" in settings.trusted_proxy_literals
class TestCsrfSecretRequired:
"""Test that CSRF_SECRET is required."""