feat: implement security, error resilience, and query optimization proposals
Security Validation (enhance-security-validation): - JWT secret validation with entropy checking and pattern detection - CSRF protection middleware with token generation/validation - Frontend CSRF token auto-injection for DELETE/PUT/PATCH requests - MIME type validation with magic bytes detection for file uploads Error Resilience (add-error-resilience): - React ErrorBoundary component with fallback UI and retry functionality - ErrorBoundaryWithI18n wrapper for internationalization support - Page-level and section-level error boundaries in App.tsx Query Performance (optimize-query-performance): - Query monitoring utility with threshold warnings - N+1 query fixes using joinedload/selectinload - Optimized project members, tasks, and subtasks endpoints Bug Fixes: - WebSocket session management (P0): Return primitives instead of ORM objects - LIKE query injection (P1): Escape special characters in search queries Tests: 543 backend tests, 56 frontend tests passing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -286,11 +286,15 @@ class FileStorageService:
|
||||
return filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
|
||||
|
||||
@staticmethod
|
||||
def validate_file(file: UploadFile) -> Tuple[str, str]:
|
||||
def validate_file(file: UploadFile, validate_mime: bool = True) -> Tuple[str, str]:
|
||||
"""
|
||||
Validate file size and type.
|
||||
Validate file size, type, and optionally MIME content.
|
||||
Returns (extension, mime_type) if valid.
|
||||
Raises HTTPException if invalid.
|
||||
|
||||
Args:
|
||||
file: The uploaded file
|
||||
validate_mime: If True, validate MIME type using magic bytes detection
|
||||
"""
|
||||
# Check file size
|
||||
file.file.seek(0, 2) # Seek to end
|
||||
@@ -323,7 +327,35 @@ class FileStorageService:
|
||||
detail=f"File type '.{extension}' is not supported"
|
||||
)
|
||||
|
||||
mime_type = file.content_type or "application/octet-stream"
|
||||
# Validate MIME type using magic bytes detection
|
||||
if validate_mime:
|
||||
from app.services.mime_validation_service import mime_validation_service
|
||||
|
||||
# Read first 16 bytes for magic detection (enough for most signatures)
|
||||
file_header = file.file.read(16)
|
||||
file.file.seek(0) # Reset
|
||||
|
||||
is_valid, detected_mime, error_message = mime_validation_service.validate_file_content(
|
||||
file_content=file_header,
|
||||
declared_extension=extension,
|
||||
declared_mime_type=file.content_type
|
||||
)
|
||||
|
||||
if not is_valid:
|
||||
logger.warning(
|
||||
"MIME validation failed for file '%s': %s (detected: %s)",
|
||||
file.filename, error_message, detected_mime
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=error_message or "File type validation failed"
|
||||
)
|
||||
|
||||
# Use detected MIME type if available, otherwise fall back to declared
|
||||
mime_type = detected_mime if detected_mime else (file.content_type or "application/octet-stream")
|
||||
else:
|
||||
mime_type = file.content_type or "application/octet-stream"
|
||||
|
||||
return extension, mime_type
|
||||
|
||||
async def save_file(
|
||||
|
||||
Reference in New Issue
Block a user