# SPDX-FileCopyrightText: 2026 Dennis Fink # # SPDX-License-Identifier: BSD-3-Clause from unittest.mock import Mock, patch import dns.resolver import pytest from webmentions_ssg.url_security import ( AddressResolutionError, is_http_url, is_public_url, ) @patch("webmentions_ssg.url_security.dns.resolver.resolve_name") @pytest.mark.parametrize( ("url", "resolved_addresses"), [ ("http://127.0.0.1/", ["127.0.0.1"]), ("http://127.0.0.1:8080/test", ["127.0.0.1"]), ("http://[::1]/", ["::1"]), ("http://10.0.0.1/", ["10.0.0.1"]), ("http://172.16.0.1/", ["172.16.0.1"]), ("http://192.168.1.1/", ["192.168.1.1"]), ("http://169.254.169.254/", ["169.254.169.254"]), ("http://localhost/", ["127.0.0.1", "::1"]), ("http://internal.example/", ["192.168.1.10"]), ("https://example.com/", ["93.184.216.34", "192.168.1.10"]), ], ) def test_is_public_url_returns_false_for_non_public_addresses( resolve_name: Mock, url: str, resolved_addresses: list[str] ) -> None: resolve_name.return_value.addresses.return_value = resolved_addresses assert not is_public_url(url) @patch("webmentions_ssg.url_security.dns.resolver.resolve_name") @pytest.mark.parametrize( ("url", "resolved_addresses"), [ ("https://example.com/", ["93.184.216.34"]), ( "https://example.com/", ["93.184.216.34", "2606:2800:220:1:248:1893:25c8:1946"], ), ], ) def test_is_public_url_returns_true_for_public_addresses( resolve_name: Mock, url: str, resolved_addresses: list[str] ) -> None: resolve_name.return_value.addresses.return_value = resolved_addresses assert is_public_url(url) @patch( "webmentions_ssg.url_security.dns.resolver.resolve_name", side_effect=dns.resolver.NXDOMAIN(), ) def test_is_public_url_raises_for_resolution_failure(resolve_name: Mock) -> None: with pytest.raises(AddressResolutionError, match="Could not resolve hostname"): is_public_url("https://nonexistent.example/") resolve_name.assert_called_once_with("nonexistent.example") @patch("webmentions_ssg.url_security.dns.resolver.resolve_name") def test_is_public_url_raises_when_url_has_no_hostname(resolve_name: Mock) -> None: with pytest.raises(ValueError, match="No hostname was specified"): is_public_url("/relative/url") resolve_name.assert_not_called() def test_is_http_url_rejects_malformed_url() -> None: assert not is_http_url("http://[::1")