fix(ci): auto-fix lint/format issues
PR Automation / Auto Approve on CI Green (pull_request) Successful in 59s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m1s
AI Code Review / AI Code Review (pull_request) Successful in 8m38s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 23m9s
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been skipped
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 26s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 47s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m19s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 1343h32m8s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1343h34m8s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1343h34m36s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1343h34m37s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1343h34m39s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1343h35m53s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1343h35m52s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1343h35m55s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1343h35m57s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1344h9m29s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1344h9m33s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1344h41m46s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1344h6m53s

This commit is contained in:
CI Auto Fix Bot
2026-07-22 14:51:01 +08:00
parent 911478495a
commit 976f01a7c3
+47 -40
View File
@@ -11,28 +11,27 @@ url_security URL安全校验单元测试
import os
import tempfile
from unittest.mock import MagicMock, patch
import pytest
from unittest.mock import patch, MagicMock
from packages.shared.url_security import (
ALLOWED_SCHEMES,
ALLOWED_PORTS,
MAX_URL_LENGTH,
UrlSecurityError,
NoRedirectHandler,
validate_url_safety,
is_url_safe,
_validate_magic_number,
_check_internal_hostnames,
_is_trusted_domain,
safe_download_file,
safe_download_bytes,
ALLOWED_AUDIO_MIME_TYPES,
ALLOWED_IMAGE_MIME_TYPES,
ALLOWED_PORTS,
ALLOWED_SCHEMES,
MAX_URL_LENGTH,
NoRedirectHandler,
UrlSecurityError,
_check_internal_hostnames,
_is_trusted_domain,
_validate_magic_number,
is_url_safe,
safe_download_bytes,
safe_download_file,
validate_url_safety,
)
# ── validate_url_safety 基础校验 ─────────────────────────────────────────────
@@ -203,9 +202,10 @@ class TestTrustedDomains:
def test_is_trusted_domain_exact_match(self):
with patch("packages.shared.url_security.TRUSTED_DOMAINS", {"example.com", "cdn.example.org"}):
# 重新加载模块以应用环境变量不太现实,直接测函数
from packages.shared.url_security import _is_trusted_domain
# 直接改全局状态再还原
import packages.shared.url_security as mod
from packages.shared.url_security import _is_trusted_domain
original = mod.TRUSTED_DOMAINS
mod.TRUSTED_DOMAINS = {"example.com", "cdn.example.org"}
try:
@@ -216,6 +216,7 @@ class TestTrustedDomains:
def test_is_trusted_domain_subdomain(self):
import packages.shared.url_security as mod
original = mod.TRUSTED_DOMAINS
mod.TRUSTED_DOMAINS = {"example.com"}
try:
@@ -226,6 +227,7 @@ class TestTrustedDomains:
def test_is_trusted_domain_no_match(self):
import packages.shared.url_security as mod
original = mod.TRUSTED_DOMAINS
mod.TRUSTED_DOMAINS = {"example.com"}
try:
@@ -237,6 +239,7 @@ class TestTrustedDomains:
def test_validate_with_trusted_domains_restricted(self):
"""白名单非空时,不在白名单中的域名被拒"""
import packages.shared.url_security as mod
original = mod.TRUSTED_DOMAINS
mod.TRUSTED_DOMAINS = {"trusted.com"}
try:
@@ -297,8 +300,12 @@ class TestNoRedirectHandler:
def test_redirect_request_returns_none(self):
handler = NoRedirectHandler()
result = handler.redirect_request(
MagicMock(), MagicMock(), 302, "Found",
{"Location": "http://other.com"}, "http://other.com",
MagicMock(),
MagicMock(),
302,
"Found",
{"Location": "http://other.com"},
"http://other.com",
)
assert result is None
@@ -312,80 +319,80 @@ class TestMagicNumberValidation:
def test_valid_png(self, tmp_path):
f = tmp_path / "test.png"
# PNG 文件头: 89 50 4E 47 0D 0A 1A 0A
f.write_bytes(b'\x89PNG\r\n\x1a\n' + b'\x00' * 100)
f.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 100)
# 不抛异常 = 通过
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_jpeg(self, tmp_path):
f = tmp_path / "test.jpg"
# JPEG 文件头: FF D8 FF
f.write_bytes(b'\xff\xd8\xff\xe0' + b'\x00' * 100)
f.write_bytes(b"\xff\xd8\xff\xe0" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_gif87a(self, tmp_path):
f = tmp_path / "test.gif"
f.write_bytes(b'GIF87a' + b'\x00' * 100)
f.write_bytes(b"GIF87a" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_gif89a(self, tmp_path):
f = tmp_path / "test.gif"
f.write_bytes(b'GIF89a' + b'\x00' * 100)
f.write_bytes(b"GIF89a" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_webp(self, tmp_path):
f = tmp_path / "test.webp"
# RIFF....WEBP
data = bytearray(b'RIFF')
data += b'\x00\x00\x00\x00' # size placeholder
data += b'WEBP'
data += b'\x00' * 100
data = bytearray(b"RIFF")
data += b"\x00\x00\x00\x00" # size placeholder
data += b"WEBP"
data += b"\x00" * 100
f.write_bytes(bytes(data))
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_bmp(self, tmp_path):
f = tmp_path / "test.bmp"
f.write_bytes(b'BM' + b'\x00' * 100)
f.write_bytes(b"BM" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_valid_wav(self, tmp_path):
f = tmp_path / "test.wav"
# RIFF....WAVE
data = bytearray(b'RIFF')
data += b'\x00\x00\x00\x00'
data += b'WAVE'
data += b'\x00' * 100
data = bytearray(b"RIFF")
data += b"\x00\x00\x00\x00"
data += b"WAVE"
data += b"\x00" * 100
f.write_bytes(bytes(data))
_validate_magic_number(str(f), ALLOWED_AUDIO_MIME_TYPES)
def test_valid_mp3_id3(self, tmp_path):
f = tmp_path / "test.mp3"
f.write_bytes(b'ID3\x03\x00\x00\x00\x00\x00\x00' + b'\x00' * 100)
f.write_bytes(b"ID3\x03\x00\x00\x00\x00\x00\x00" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_AUDIO_MIME_TYPES)
def test_valid_mp3_adts(self, tmp_path):
f = tmp_path / "test.mp3"
f.write_bytes(b'\xff\xfb\x90\x00' + b'\x00' * 100)
f.write_bytes(b"\xff\xfb\x90\x00" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_AUDIO_MIME_TYPES)
def test_valid_ogg(self, tmp_path):
f = tmp_path / "test.ogg"
f.write_bytes(b'OggS\x00\x02\x00\x00' + b'\x00' * 100)
f.write_bytes(b"OggS\x00\x02\x00\x00" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_AUDIO_MIME_TYPES)
def test_valid_flac(self, tmp_path):
f = tmp_path / "test.flac"
f.write_bytes(b'fLaC' + b'\x00' * 100)
f.write_bytes(b"fLaC" + b"\x00" * 100)
_validate_magic_number(str(f), ALLOWED_AUDIO_MIME_TYPES)
def test_invalid_file_content_raises(self, tmp_path):
f = tmp_path / "test.bin"
f.write_bytes(b'this is not an image file at all')
f.write_bytes(b"this is not an image file at all")
with pytest.raises(UrlSecurityError, match="魔数"):
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
def test_empty_file_raises(self, tmp_path):
f = tmp_path / "empty.bin"
f.write_bytes(b'')
f.write_bytes(b"")
with pytest.raises(UrlSecurityError, match="为空"):
_validate_magic_number(str(f), ALLOWED_IMAGE_MIME_TYPES)
@@ -396,14 +403,14 @@ class TestMagicNumberValidation:
def test_no_allowed_mime_types_skips(self, tmp_path):
"""allowed_mime_types 为空时跳过校验"""
f = tmp_path / "test.bin"
f.write_bytes(b'random data here')
f.write_bytes(b"random data here")
# 不抛异常
_validate_magic_number(str(f), set())
def test_unknown_mime_types_skips(self, tmp_path):
"""没有已知魔数的 MIME 类型跳过校验"""
f = tmp_path / "test.bin"
f.write_bytes(b'random data')
f.write_bytes(b"random data")
_validate_magic_number(str(f), {"application/x-unknown-type"})
@@ -441,7 +448,7 @@ class TestSafeDownloadFile:
def test_download_with_mime_check_passes(self, tmp_path):
# PNG 文件
test_content = b'\x89PNG\r\n\x1a\n' + b'\x00' * 200
test_content = b"\x89PNG\r\n\x1a\n" + b"\x00" * 200
dest = str(tmp_path / "test.png")
mock_resp = MagicMock()
@@ -463,7 +470,7 @@ class TestSafeDownloadFile:
assert size == len(test_content)
def test_download_mime_type_rejected(self, tmp_path):
test_content = b'GIF89a' + b'\x00' * 50
test_content = b"GIF89a" + b"\x00" * 50
dest = str(tmp_path / "test.gif")
mock_resp = MagicMock()