84 lines
2.2 KiB
Python
84 lines
2.2 KiB
Python
"""
|
|
Security utilities for file uploads and validation.
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
def sanitize_filename(filename: str) -> str:
|
|
"""
|
|
Sanitize a filename to prevent path traversal and other security issues.
|
|
|
|
- Removes path separators (/ and \)
|
|
- Removes null bytes
|
|
- Removes control characters
|
|
- Limits to safe characters
|
|
- Preserves extension
|
|
|
|
Args:
|
|
filename: Original filename from user
|
|
|
|
Returns:
|
|
Sanitized filename safe for storage
|
|
"""
|
|
# Get the base name (remove any path components)
|
|
filename = Path(filename).name
|
|
|
|
# Remove any null bytes
|
|
filename = filename.replace('\x00', '')
|
|
|
|
# Remove or replace dangerous characters
|
|
# Allow: alphanumeric, dash, underscore, dot, space
|
|
filename = re.sub(r'[^\w\s\-\.]', '_', filename)
|
|
|
|
# Remove multiple dots (except for extension)
|
|
parts = filename.rsplit('.', 1)
|
|
if len(parts) == 2:
|
|
name, ext = parts
|
|
name = name.replace('.', '_')
|
|
filename = f"{name}.{ext}"
|
|
|
|
# Remove leading/trailing spaces and dots
|
|
filename = filename.strip('. ')
|
|
|
|
# Ensure filename is not empty
|
|
if not filename:
|
|
filename = "unnamed_file"
|
|
|
|
# Limit length (keep extension)
|
|
max_length = 255
|
|
if len(filename) > max_length:
|
|
name_part = filename.rsplit('.', 1)[0]
|
|
ext_part = filename.rsplit('.', 1)[1] if '.' in filename else ''
|
|
name_part = name_part[:max_length - len(ext_part) - 1]
|
|
filename = f"{name_part}.{ext_part}" if ext_part else name_part
|
|
|
|
return filename
|
|
|
|
|
|
def validate_video_file_extension(filename: str) -> bool:
|
|
"""
|
|
Validate that filename has an allowed video extension.
|
|
|
|
Args:
|
|
filename: Filename to check
|
|
|
|
Returns:
|
|
True if extension is allowed, False otherwise
|
|
"""
|
|
allowed_extensions = ['.mp4', '.mov', '.avi', '.mkv']
|
|
return any(filename.lower().endswith(ext) for ext in allowed_extensions)
|
|
|
|
|
|
def validate_file_size(file_size: int, max_size: int) -> bool:
|
|
"""
|
|
Validate that file size is within limits.
|
|
|
|
Args:
|
|
file_size: File size in bytes
|
|
max_size: Maximum allowed size in bytes
|
|
|
|
Returns:
|
|
True if size is acceptable, False otherwise
|
|
"""
|
|
return 0 < file_size <= max_size
|