39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
from __future__ import annotations
|
|
|
|
import socket
|
|
|
|
import pytest
|
|
|
|
from app.url_policy import NetworkRules, UrlPolicyError, normalize_url, resolve_and_validate
|
|
|
|
|
|
def test_normalize_url_adds_https_and_removes_fragment() -> None:
|
|
result = normalize_url("example.com/docs#section", NetworkRules())
|
|
|
|
assert result == "https://example.com/docs"
|
|
|
|
|
|
def test_normalize_url_preserves_encoded_path_delimiters() -> None:
|
|
result = normalize_url("https://example.com/a%2Fb", NetworkRules())
|
|
|
|
assert result == "https://example.com/a%2Fb"
|
|
|
|
|
|
def test_normalize_url_rejects_credentials_and_unsafe_ports() -> None:
|
|
with pytest.raises(UrlPolicyError, match="credentials"):
|
|
normalize_url("https://user:secret@example.com", NetworkRules())
|
|
|
|
with pytest.raises(UrlPolicyError, match="ports 80 and 443"):
|
|
normalize_url("https://example.com:8443", NetworkRules())
|
|
|
|
|
|
def test_resolve_blocks_loopback_by_default(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr(
|
|
socket,
|
|
"getaddrinfo",
|
|
lambda *args, **kwargs: [(socket.AF_INET, socket.SOCK_STREAM, 6, "", ("127.0.0.1", 0))],
|
|
)
|
|
|
|
with pytest.raises(UrlPolicyError, match="Private, local, and reserved"):
|
|
resolve_and_validate("https://example.com", NetworkRules())
|