""" url_security URL安全校验单元测试 覆盖: - validate_url_safety: scheme/主机/端口/SSRF/内网域名/白名单 - is_url_safe: 便捷函数 - UrlSecurityError / NoRedirectHandler - _validate_magic_number: 文件魔数校验 - safe_download_file / safe_download_bytes: mock 网络测试 """ import ipaddress import os import tempfile from unittest.mock import MagicMock, patch import pytest from packages.shared.url_security import ( ALLOWED_AUDIO_MIME_TYPES, ALLOWED_IMAGE_MIME_TYPES, ALLOWED_PORTS, ALLOWED_SCHEMES, MAX_URL_LENGTH, NoRedirectHandler, UrlSecurityError, _check_internal_hostnames, _check_ssrf_ip, _is_trusted_domain, _validate_magic_number, is_url_safe, safe_download_bytes, safe_download_file, validate_url_safety, ) # ── validate_url_safety 基础校验 ───────────────────────────────────────────── class TestValidateUrlSafetyBasics: """URL 安全校验基础测试""" def test_valid_http_url(self): url = "http://example.com/file.mp4" result = validate_url_safety(url) assert result == url def test_valid_https_url(self): url = "https://example.com/file.mp4" result = validate_url_safety(url) assert result == url def test_empty_url_raises(self): with pytest.raises(UrlSecurityError, match="为空"): validate_url_safety("") def test_none_url_raises(self): with pytest.raises(UrlSecurityError): validate_url_safety(None) def test_url_too_long_raises(self): long_url = "https://example.com/" + "a" * 2050 with pytest.raises(UrlSecurityError, match="过长"): validate_url_safety(long_url) def test_url_at_max_length_ok(self): base = "https://example.com/" pad = "a" * (MAX_URL_LENGTH - len(base)) url = base + pad assert len(url) <= MAX_URL_LENGTH result = validate_url_safety(url) assert result == url def test_invalid_scheme_ftp_raises(self): with pytest.raises(UrlSecurityError, match="scheme"): validate_url_safety("ftp://example.com/file") def test_invalid_scheme_file_raises(self): with pytest.raises(UrlSecurityError, match="scheme"): validate_url_safety("file:///etc/passwd") def test_invalid_scheme_data_raises(self): with pytest.raises(UrlSecurityError, match="scheme"): validate_url_safety("data:text/html,