# Vulnerable: FileResponse with user-controlled paths
from django.http import FileResponse, JsonResponse, Http404
from django.views import View
from django.conf import settings
import os
# Dangerous: Direct file serving with user input
class FileDownloadView(View):
def get(self, request, file_id):
filename = request.GET.get('filename', '')
path = request.GET.get('path', '')
# CRITICAL: User controls file path
if path:
file_path = os.path.join(settings.MEDIA_ROOT, path, filename)
else:
file_path = os.path.join(settings.MEDIA_ROOT, 'downloads', filename)
try:
return FileResponse(open(file_path, 'rb'), as_attachment=True, filename=filename)
except FileNotFoundError:
raise Http404('File not found')
# Another dangerous pattern
def serve_user_document(request):
doc_path = request.POST.get('document_path', '')
# Dangerous: Direct path from request
full_path = os.path.join('/var/documents', doc_path)
try:
response = FileResponse(open(full_path, 'rb'))
response['Content-Disposition'] = f'attachment; filename="{os.path.basename(doc_path)}"'
return response
except Exception as e:
return JsonResponse({'error': str(e)})
# Report download with path traversal
def download_report(request):
report_type = request.GET.get('type', '')
date = request.GET.get('date', '')
format_type = request.GET.get('format', 'pdf')
# Dangerous: User-controlled path construction
report_filename = f'{report_type}_{date}.{format_type}'
report_path = os.path.join(settings.MEDIA_ROOT, 'reports', report_filename)
if os.path.exists(report_path):
return FileResponse(open(report_path, 'rb'), as_attachment=True)
return JsonResponse({'error': 'Report not found'})
# Backup file access
def download_backup(request):
backup_name = request.GET.get('backup', '')
directory = request.GET.get('dir', 'daily')
# Dangerous: Multiple user inputs in path
backup_path = f'/backups/{directory}/{backup_name}'
try:
return FileResponse(open(backup_path, 'rb'), as_attachment=True, filename=backup_name)
except FileNotFoundError:
return JsonResponse({'error': 'Backup not found'})
# Log file download
def download_log(request):
log_name = request.GET.get('log', '')
server = request.GET.get('server', 'web')
# Dangerous: Server and log name from user
log_path = f'/var/log/{server}/{log_name}.log'
if os.path.exists(log_path):
return FileResponse(open(log_path, 'rb'), as_attachment=True)
raise Http404('Log file not found')
# Configuration file download
def export_config(request):
config_file = request.POST.get('config_file', '')
environment = request.POST.get('environment', '')
# Dangerous: Config path from user input
config_path = os.path.join(settings.BASE_DIR, 'config', environment, config_file)
try:
response = FileResponse(open(config_path, 'rb'))
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = f'attachment; filename="{config_file}"'
return response
except Exception:
return JsonResponse({'error': 'Config export failed'})
# Secure: Safe FileResponse with validation
from django.http import FileResponse, JsonResponse, Http404
from django.views import View
from django.conf import settings
from django.core.exceptions import ValidationError
from pathlib import Path
import os
import re
# Safe: Validated file download
class SafeFileDownloadView(View):
def get(self, request, file_id):
try:
# Validate file ID
validated_file_id = self.validate_file_id(file_id)
# Get file info from database or allowlist
file_info = self.get_file_info(validated_file_id)
# Construct safe file path
safe_path = self.get_safe_file_path(file_info)
# Serve file securely
return self.serve_file_safely(safe_path, file_info)
except ValidationError as e:
return JsonResponse({'error': str(e)}, status=400)
except FileNotFoundError:
raise Http404('File not found')
def validate_file_id(self, file_id):
# Validate file ID format
if not file_id or not file_id.isdigit():
raise ValidationError('Invalid file ID')
file_id = int(file_id)
if file_id <= 0:
raise ValidationError('Invalid file ID')
return file_id
def get_file_info(self, file_id):
# This would typically query a database
# Using a mock allowlist for demonstration
allowed_files = {
1: {'filename': 'document1.pdf', 'path': 'documents'},
2: {'filename': 'report.xlsx', 'path': 'reports'},
3: {'filename': 'image.jpg', 'path': 'images'}
}
if file_id not in allowed_files:
raise ValidationError('File not found')
return allowed_files[file_id]
def get_safe_file_path(self, file_info):
# Define safe base directory
downloads_dir = Path(settings.MEDIA_ROOT) / 'downloads'
# Construct path using validated components
file_dir = downloads_dir / file_info['path']
file_path = file_dir / file_info['filename']
# Validate path is within downloads directory
try:
resolved_path = file_path.resolve()
downloads_dir_resolved = downloads_dir.resolve()
resolved_path.relative_to(downloads_dir_resolved)
return resolved_path
except ValueError:
raise ValidationError('File path outside allowed directory')
def serve_file_safely(self, file_path, file_info):
try:
if not file_path.exists():
raise FileNotFoundError('File not found')
# Check file size
max_size = 100 * 1024 * 1024 # 100MB
if file_path.stat().st_size > max_size:
raise ValidationError('File too large')
# Serve file
response = FileResponse(
open(file_path, 'rb'),
as_attachment=True,
filename=file_info['filename']
)
return response
except PermissionError:
raise ValidationError('Access denied')
# Safe: Document serving with validation
def safe_serve_user_document(request):
document_id = request.POST.get('document_id', '')
try:
# Validate document access
validated_doc = validate_document_access(request, document_id)
# Get safe file path
file_path = get_safe_document_path(validated_doc)
# Serve document
return serve_document_safely(file_path, validated_doc)
except ValidationError as e:
return JsonResponse({'error': str(e)}, status=400)
def validate_document_access(request, document_id):
# Validate document ID
if not document_id.isdigit():
raise ValidationError('Invalid document ID')
document_id = int(document_id)
# Check user access (mock implementation)
if not request.user.is_authenticated:
raise ValidationError('Authentication required')
# This would typically check database permissions
user_documents = [1, 2, 3] # Mock user's accessible documents
if document_id not in user_documents:
raise ValidationError('Access denied')
return {
'id': document_id,
'filename': f'document_{document_id}.pdf',
'owner': request.user.id
}
def get_safe_document_path(document_info):
# Define safe base directory
documents_dir = Path(settings.MEDIA_ROOT) / 'user_documents'
# Construct safe path
file_path = documents_dir / str(document_info['owner']) / document_info['filename']
# Validate path
try:
resolved_path = file_path.resolve()
documents_dir_resolved = documents_dir.resolve()
resolved_path.relative_to(documents_dir_resolved)
return resolved_path
except ValueError:
raise ValidationError('Document path outside allowed directory')
def serve_document_safely(file_path, document_info):
try:
if not file_path.exists():
raise ValidationError('Document not found')
response = FileResponse(
open(file_path, 'rb'),
as_attachment=True,
filename=document_info['filename']
)
# Set security headers
response['X-Content-Type-Options'] = 'nosniff'
response['Content-Security-Policy'] = "default-src 'none'"
return response
except PermissionError:
raise ValidationError('Access denied')
# Safe: Report download with allowlists
def safe_download_report(request):
report_type = request.GET.get('type', '')
date = request.GET.get('date', '')
try:
# Validate inputs
validated_data = validate_report_request(report_type, date)
# Get report file
report_path = get_safe_report_path(validated_data)
# Serve report
return serve_report_safely(report_path, validated_data)
except ValidationError as e:
return JsonResponse({'error': str(e)}, status=400)
def validate_report_request(report_type, date):
# Validate report type
allowed_reports = ['sales', 'inventory', 'users', 'analytics']
if report_type not in allowed_reports:
raise ValidationError('Report type not allowed')
# Validate date format
if not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
raise ValidationError('Invalid date format')
return {
'type': report_type,
'date': date,
'filename': f'{report_type}_report_{date}.pdf'
}
def get_safe_report_path(report_data):
# Define safe reports directory
reports_dir = Path(settings.MEDIA_ROOT) / 'reports' / report_data['type']
# Construct file path
file_path = reports_dir / report_data['filename']
# Validate path
try:
resolved_path = file_path.resolve()
reports_base = Path(settings.MEDIA_ROOT).resolve() / 'reports'
resolved_path.relative_to(reports_base)
return resolved_path
except ValueError:
raise ValidationError('Report path outside allowed directory')
def serve_report_safely(file_path, report_data):
try:
if not file_path.exists():
raise ValidationError('Report not found')
response = FileResponse(
open(file_path, 'rb'),
as_attachment=True,
filename=report_data['filename']
)
response['Content-Type'] = 'application/pdf'
return response
except Exception:
raise ValidationError('Report serving failed')
# Safe: Backup access with strict controls
def safe_download_backup(request):
backup_id = request.GET.get('backup_id', '')
try:
# Only allow staff access
if not request.user.is_staff:
raise ValidationError('Access denied')
# Validate backup access
validated_backup = validate_backup_access(backup_id)
# Get backup file
backup_path = get_safe_backup_path(validated_backup)
# Serve backup
return serve_backup_safely(backup_path, validated_backup)
except ValidationError as e:
return JsonResponse({'error': str(e)}, status=400)
def validate_backup_access(backup_id):
# Validate backup ID format
if not backup_id.isdigit():
raise ValidationError('Invalid backup ID')
# This would query a database for valid backups
valid_backups = {
1: 'backup_20231201.tar.gz',
2: 'backup_20231202.tar.gz',
3: 'backup_20231203.tar.gz'
}
backup_id = int(backup_id)
if backup_id not in valid_backups:
raise ValidationError('Backup not found')
return {
'id': backup_id,
'filename': valid_backups[backup_id]
}
def get_safe_backup_path(backup_info):
# Define safe backup directory
backup_dir = Path(settings.BASE_DIR) / 'backups'
# Construct file path
file_path = backup_dir / backup_info['filename']
# Validate path
try:
resolved_path = file_path.resolve()
backup_dir_resolved = backup_dir.resolve()
resolved_path.relative_to(backup_dir_resolved)
return resolved_path
except ValueError:
raise ValidationError('Backup path outside allowed directory')
def serve_backup_safely(file_path, backup_info):
try:
if not file_path.exists():
raise ValidationError('Backup file not found')
response = FileResponse(
open(file_path, 'rb'),
as_attachment=True,
filename=backup_info['filename']
)
response['Content-Type'] = 'application/gzip'
return response
except Exception:
raise ValidationError('Backup serving failed')