Files
BrickTracker/bricktracker/views/data.py
T

61 lines
2.1 KiB
Python

import os
import logging
from flask import Blueprint, current_app, send_from_directory, abort
from werkzeug.utils import secure_filename
logger = logging.getLogger(__name__)
data_page = Blueprint(
'data',
__name__,
url_prefix='/data'
)
@data_page.route('/<path:folder>/<filename>')
def serve_data_file(folder: str, filename: str):
"""
Serve files from the data folder (images, PDFs, etc.)
This replaces serving these files from static/ folder.
Security:
- Only allows serving files from configured data folders
- Uses secure_filename to prevent path traversal
- Returns 404 if file doesn't exist or folder not allowed
"""
# Secure the filename to prevent path traversal attacks
safe_filename = secure_filename(filename)
# Get the configured data folders
allowed_folders = {
'sets': current_app.config.get('SETS_FOLDER', './data/sets'),
'parts': current_app.config.get('PARTS_FOLDER', './data/parts'),
'minifigures': current_app.config.get('MINIFIGURES_FOLDER', './data/minifigures'),
'instructions': current_app.config.get('INSTRUCTIONS_FOLDER', './data/instructions'),
}
# Check if the requested folder is allowed
if folder not in allowed_folders:
logger.warning(f"Attempt to access unauthorized folder: {folder}")
abort(404)
# Get the actual folder path
folder_path = allowed_folders[folder]
# If folder_path is relative (not absolute), make it relative to app root
if not os.path.isabs(folder_path):
folder_path = os.path.join(current_app.root_path, folder_path)
# Check if file exists
file_path = os.path.join(folder_path, safe_filename)
if not os.path.isfile(file_path):
logger.debug(f"File not found: {file_path}")
abort(404)
# Verify the resolved path is still within the allowed folder (security check)
if not os.path.abspath(file_path).startswith(os.path.abspath(folder_path)):
logger.warning(f"Path traversal attempt detected: {filename}")
abort(404)
return send_from_directory(folder_path, safe_filename)