avoid breaking out into filestructure

This commit is contained in:
lelo 2025-05-18 22:25:50 +02:00
parent 564d95a5c2
commit a0f972f38a
2 changed files with 40 additions and 1 deletions

34
app.py
View File

@ -15,6 +15,7 @@ import geoip2.database
from functools import lru_cache from functools import lru_cache
from urllib.parse import urlparse, unquote from urllib.parse import urlparse, unquote
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
from pathlib import Path
import re import re
import qrcode import qrcode
import base64 import base64
@ -24,6 +25,7 @@ import analytics as a
import folder_secret_config_editor as fsce import folder_secret_config_editor as fsce
app_config = auth.return_app_config() app_config = auth.return_app_config()
BASE_DIR = os.path.realpath(app_config['BASE_DIR'])
cache_audio = diskcache.Cache('./filecache_audio', size_limit= app_config['filecache_size_limit_audio'] * 1024**3) cache_audio = diskcache.Cache('./filecache_audio', size_limit= app_config['filecache_size_limit_audio'] * 1024**3)
cache_image = diskcache.Cache('./filecache_image', size_limit= app_config['filecache_size_limit_image'] * 1024**3) cache_image = diskcache.Cache('./filecache_image', size_limit= app_config['filecache_size_limit_image'] * 1024**3)
@ -90,6 +92,27 @@ def get_cached_image(size):
resized_img.save(img_byte_arr, format='PNG') resized_img.save(img_byte_arr, format='PNG')
return img_byte_arr.getvalue() return img_byte_arr.getvalue()
def check_path(access_path: str) -> Path:
"""
Take an absolute access_path, then ensure it lives inside BASE_DIR.
Raises ValueError or PermissionError on failure.
"""
p = Path(access_path)
if not p.is_absolute():
raise ValueError(f"Path {access_path} is not a valid absolute path")
# Resolve symlinks & eliminate “..” components
candidate = p.resolve()
base = Path(BASE_DIR).resolve()
try:
# Will raise ValueError if candidate is not under base
candidate.relative_to(base)
except ValueError:
raise PermissionError(f"Access to {access_path} is forbidden")
return candidate
def list_directory_contents(directory, subpath): def list_directory_contents(directory, subpath):
""" """
List only the immediate contents of the given directory. List only the immediate contents of the given directory.
@ -236,6 +259,12 @@ def api_browse(subpath):
directory = os.path.join(base_path, *relative_parts) directory = os.path.join(base_path, *relative_parts)
playfile = None playfile = None
try:
directory = check_path(directory)
except (ValueError, PermissionError) as e:
return jsonify({'error': str(e)}), 403
# Check if the constructed directory exists. # Check if the constructed directory exists.
if not os.path.isdir(directory): if not os.path.isdir(directory):
# Assume the last segment is a filename; remove it. # Assume the last segment is a filename; remove it.
@ -271,6 +300,11 @@ def serve_file(subpath):
base_path = session['folders'].get(root) base_path = session['folders'].get(root)
full_path = os.path.join(base_path or '', *relative_parts) full_path = os.path.join(base_path or '', *relative_parts)
try:
full_path = check_path(full_path)
except (ValueError, PermissionError) as e:
return jsonify({'error': str(e)}), 403
if not os.path.isfile(full_path): if not os.path.isfile(full_path):
app.logger.error(f"File not found: {full_path}") app.logger.error(f"File not found: {full_path}")
return "File not found", 404 return "File not found", 404

View File

@ -213,7 +213,12 @@ function loadDirectory(subpath) {
.then(data => { .then(data => {
clearTimeout(spinnerTimer); clearTimeout(spinnerTimer);
hideSpinner(); hideSpinner();
if (data.breadcrumbs) {
renderContent(data); renderContent(data);
} else if (data.error) {
document.getElementById('content').innerHTML = `<div class="alert alert-warning">${data.error}</div>`;
return;
}
if (data.playfile) { if (data.playfile) {
const playFileLink = document.querySelector(`.play-file[data-url="${data.playfile}"]`); const playFileLink = document.querySelector(`.play-file[data-url="${data.playfile}"]`);
if (playFileLink) { if (playFileLink) {