143 lines
5.3 KiB
Python
143 lines
5.3 KiB
Python
"""
|
|
Security tests for file upload validation and sanitization.
|
|
"""
|
|
import pytest
|
|
from app.core.security import (
|
|
sanitize_filename,
|
|
validate_video_file_extension,
|
|
validate_file_size
|
|
)
|
|
|
|
|
|
class TestFilenameSanitization:
|
|
"""Tests for filename sanitization to prevent security issues"""
|
|
|
|
def test_sanitize_normal_filename(self):
|
|
"""Test that normal filenames pass through correctly"""
|
|
assert sanitize_filename("meeting_video.mp4") == "meeting_video.mp4"
|
|
assert sanitize_filename("sales-call-2024.mov") == "sales-call-2024.mov"
|
|
|
|
def test_sanitize_path_traversal_attempts(self):
|
|
"""Test that path traversal attempts are blocked"""
|
|
# Unix path traversal
|
|
result = sanitize_filename("../../../etc/passwd")
|
|
assert ".." not in result
|
|
assert "/" not in result
|
|
|
|
# Windows path traversal
|
|
result = sanitize_filename("..\\..\\..\\windows\\system32\\config")
|
|
assert ".." not in result
|
|
assert "\\" not in result
|
|
|
|
# Absolute paths
|
|
result = sanitize_filename("/etc/passwd")
|
|
assert not result.startswith("/")
|
|
|
|
result = sanitize_filename("C:\\Windows\\System32\\file.mp4")
|
|
assert ":\\" not in result
|
|
|
|
def test_sanitize_null_bytes(self):
|
|
"""Test that null bytes are removed"""
|
|
result = sanitize_filename("file\x00name.mp4")
|
|
assert "\x00" not in result
|
|
|
|
def test_sanitize_special_characters(self):
|
|
"""Test that dangerous special characters are replaced"""
|
|
result = sanitize_filename("file<name>.mp4")
|
|
assert "<" not in result
|
|
assert ">" not in result
|
|
|
|
result = sanitize_filename('file"name".mp4')
|
|
assert '"' not in result
|
|
|
|
def test_sanitize_preserves_extension(self):
|
|
"""Test that file extension is preserved"""
|
|
assert sanitize_filename("test.mp4").endswith(".mp4")
|
|
assert sanitize_filename("test.MOV").endswith(".MOV")
|
|
|
|
def test_sanitize_empty_filename(self):
|
|
"""Test that empty filename gets default name"""
|
|
result = sanitize_filename("")
|
|
assert result == "unnamed_file"
|
|
|
|
result = sanitize_filename("...")
|
|
assert result == "unnamed_file"
|
|
|
|
def test_sanitize_long_filename(self):
|
|
"""Test that very long filenames are truncated"""
|
|
long_name = "a" * 300 + ".mp4"
|
|
result = sanitize_filename(long_name)
|
|
assert len(result) <= 255
|
|
assert result.endswith(".mp4")
|
|
|
|
|
|
class TestVideoFileValidation:
|
|
"""Tests for video file extension validation"""
|
|
|
|
def test_valid_video_extensions(self):
|
|
"""Test that valid video extensions are accepted"""
|
|
assert validate_video_file_extension("video.mp4") == True
|
|
assert validate_video_file_extension("video.MP4") == True
|
|
assert validate_video_file_extension("video.mov") == True
|
|
assert validate_video_file_extension("video.avi") == True
|
|
assert validate_video_file_extension("video.mkv") == True
|
|
|
|
def test_invalid_video_extensions(self):
|
|
"""Test that invalid extensions are rejected"""
|
|
assert validate_video_file_extension("document.pdf") == False
|
|
assert validate_video_file_extension("image.jpg") == False
|
|
assert validate_video_file_extension("script.exe") == False
|
|
assert validate_video_file_extension("archive.zip") == False
|
|
assert validate_video_file_extension("video.mp3") == False
|
|
|
|
def test_no_extension(self):
|
|
"""Test that files without extension are rejected"""
|
|
assert validate_video_file_extension("videofile") == False
|
|
|
|
|
|
class TestFileSizeValidation:
|
|
"""Tests for file size validation"""
|
|
|
|
def test_valid_file_sizes(self):
|
|
"""Test that reasonable file sizes are accepted"""
|
|
max_size = 2 * 1024 * 1024 * 1024 # 2GB
|
|
|
|
assert validate_file_size(100000, max_size) == True # 100KB
|
|
assert validate_file_size(1024 * 1024, max_size) == True # 1MB
|
|
assert validate_file_size(500 * 1024 * 1024, max_size) == True # 500MB
|
|
assert validate_file_size(max_size, max_size) == True # Exactly 2GB
|
|
|
|
def test_file_too_large(self):
|
|
"""Test that oversized files are rejected"""
|
|
max_size = 2 * 1024 * 1024 * 1024 # 2GB
|
|
assert validate_file_size(max_size + 1, max_size) == False
|
|
|
|
def test_zero_or_negative_size(self):
|
|
"""Test that zero or negative file sizes are rejected"""
|
|
max_size = 2 * 1024 * 1024 * 1024
|
|
assert validate_file_size(0, max_size) == False
|
|
assert validate_file_size(-1, max_size) == False
|
|
|
|
|
|
class TestCombinedSecurityScenarios:
|
|
"""Integration tests for combined security scenarios"""
|
|
|
|
def test_malicious_filename_with_valid_extension(self):
|
|
"""Test malicious path with valid extension"""
|
|
malicious = "../../etc/passwd.mp4"
|
|
sanitized = sanitize_filename(malicious)
|
|
|
|
assert ".." not in sanitized
|
|
assert "/" not in sanitized
|
|
assert sanitized.endswith(".mp4")
|
|
assert validate_video_file_extension(sanitized) == True
|
|
|
|
def test_safe_filename_workflow(self):
|
|
"""Test complete safe filename workflow"""
|
|
original = "My Meeting Video (Jan 2024).mp4"
|
|
sanitized = sanitize_filename(original)
|
|
|
|
# Should preserve alphanumeric and extension
|
|
assert validate_video_file_extension(sanitized) == True
|
|
assert len(sanitized) > 0
|
|
assert sanitized.endswith(".mp4")
|