@@ -230,30 +230,34 @@ def test_is_url_allowed_handles_cidr_blocks() -> None:
230230
231231
232232def test_is_url_allowed_handles_port_matching () -> None :
233- """Port matching: only enforced when explicitly specified ."""
233+ """Port matching: enforced if allow list has explicit port, otherwise any port allowed ."""
234234 config = URLConfig (
235235 url_allow_list = ["https://example.com:8443" , "api.internal.com" ],
236236 allow_subdomains = False ,
237237 allowed_schemes = {"https" },
238238 )
239- # Explicit port 8443 matches allow list
239+ # Explicit port 8443 matches allow list's explicit port → ALLOWED
240240 correct_port , _ , had_scheme1 = _validate_url_security ("https://example.com:8443" , config )
241- # Implicit 443 doesn't match explicit 8443 in allow list
241+ # Implicit 443 doesn't match allow list's explicit 8443 → BLOCKED
242242 wrong_port , _ , had_scheme2 = _validate_url_security ("https://example.com" , config )
243- # Explicit 9000 doesn't match implicit default in allow list (no port = not checking)
244- explicit_port_vs_implicit , _ , had_scheme3 = _validate_url_security ("https://api.internal.com:9000" , config )
245- # Implicit default matches implicit default
243+ # Explicit 9000 with no port restriction in allow list → ALLOWED
244+ explicit_port_no_restriction , _ , had_scheme3 = _validate_url_security ("https://api.internal.com:9000" , config )
245+ # Implicit 443 with no port restriction in allow list → ALLOWED
246246 implicit_match , _ , had_scheme4 = _validate_url_security ("https://api.internal.com" , config )
247+ # Explicit default 443 with no port restriction in allow list → ALLOWED (regression fix)
248+ explicit_default_port , _ , had_scheme5 = _validate_url_security ("https://api.internal.com:443" , config )
247249
248250 assert correct_port is not None # noqa: S101
249251 assert wrong_port is not None # noqa: S101
250- assert explicit_port_vs_implicit is not None # noqa: S101
252+ assert explicit_port_no_restriction is not None # noqa: S101
251253 assert implicit_match is not None # noqa: S101
254+ assert explicit_default_port is not None # noqa: S101
252255
253256 assert _is_url_allowed (correct_port , config .url_allow_list , config .allow_subdomains , had_scheme1 ) is True # noqa: S101
254257 assert _is_url_allowed (wrong_port , config .url_allow_list , config .allow_subdomains , had_scheme2 ) is False # noqa: S101
255- assert _is_url_allowed (explicit_port_vs_implicit , config .url_allow_list , config .allow_subdomains , had_scheme3 ) is False # noqa: S101
258+ assert _is_url_allowed (explicit_port_no_restriction , config .url_allow_list , config .allow_subdomains , had_scheme3 ) is True # noqa: S101
256259 assert _is_url_allowed (implicit_match , config .url_allow_list , config .allow_subdomains , had_scheme4 ) is True # noqa: S101
260+ assert _is_url_allowed (explicit_default_port , config .url_allow_list , config .allow_subdomains , had_scheme5 ) is True # noqa: S101
257261
258262
259263def test_is_url_allowed_handles_query_and_fragment () -> None :
@@ -415,6 +419,31 @@ def test_is_url_allowed_handles_ipv6_addresses() -> None:
415419 assert _is_url_allowed (ipv6_with_ftp , config .url_allow_list , config .allow_subdomains , had_scheme2 ) is True # noqa: S101
416420
417421
422+ def test_is_url_allowed_handles_ipv6_cidr_notation () -> None :
423+ """IPv6 CIDR blocks should be handled correctly (brackets stripped, path concatenated)."""
424+ config = URLConfig (
425+ url_allow_list = ["[2001:db8::]/64" , "[fe80::]/10" ],
426+ allow_subdomains = False ,
427+ allowed_schemes = {"https" },
428+ )
429+ # IP within first CIDR range
430+ ip_in_range1 , _ , had_scheme1 = _validate_url_security ("https://[2001:db8::1234]" , config )
431+ # IP within second CIDR range
432+ ip_in_range2 , _ , had_scheme2 = _validate_url_security ("https://[fe80::5678]" , config )
433+ # IP outside CIDR ranges
434+ ip_outside , _ , had_scheme3 = _validate_url_security ("https://[2001:db9::1]" , config )
435+
436+ assert ip_in_range1 is not None # noqa: S101
437+ assert ip_in_range2 is not None # noqa: S101
438+ assert ip_outside is not None # noqa: S101
439+
440+ # IPs within CIDR ranges should be allowed
441+ assert _is_url_allowed (ip_in_range1 , config .url_allow_list , config .allow_subdomains , had_scheme1 ) is True # noqa: S101
442+ assert _is_url_allowed (ip_in_range2 , config .url_allow_list , config .allow_subdomains , had_scheme2 ) is True # noqa: S101
443+ # IP outside should be blocked
444+ assert _is_url_allowed (ip_outside , config .url_allow_list , config .allow_subdomains , had_scheme3 ) is False # noqa: S101
445+
446+
418447@pytest .mark .asyncio
419448async def test_urls_guardrail_blocks_subdomains_and_paths_correctly () -> None :
420449 """Verify subdomains and paths are still blocked according to allow list rules."""
0 commit comments