mirror of https://github.com/akuker/RASCSI.git
Refactor file upload code to make it safer
This commit is contained in:
parent
a3e6ff6015
commit
4eca0495a4
|
@ -30,6 +30,8 @@ from util import unarchiver
|
||||||
FILE_READ_ERROR = "Unhandled exception when reading file: %s"
|
FILE_READ_ERROR = "Unhandled exception when reading file: %s"
|
||||||
FILE_WRITE_ERROR = "Unhandled exception when writing to file: %s"
|
FILE_WRITE_ERROR = "Unhandled exception when writing to file: %s"
|
||||||
URL_SAFE = "/:?&"
|
URL_SAFE = "/:?&"
|
||||||
|
# Common file sharing protocol meta data dirs to filter out from target upload dirs
|
||||||
|
EXCLUDED_DIRS = ["Network Trash Folder", "Temporary Items", "TheVolumeSettingsFolder"]
|
||||||
|
|
||||||
|
|
||||||
class FileCmds:
|
class FileCmds:
|
||||||
|
@ -60,15 +62,14 @@ class FileCmds:
|
||||||
Returns a (list) of (str) subdir_list.
|
Returns a (list) of (str) subdir_list.
|
||||||
"""
|
"""
|
||||||
subdir_list = []
|
subdir_list = []
|
||||||
# Filter out file sharing meta data dirs
|
|
||||||
excluded_dirs = ("Network Trash Folder", "Temporary Items", "TheVolumeSettingsFolder")
|
|
||||||
for root, dirs, _files in walk(directory, topdown=True):
|
for root, dirs, _files in walk(directory, topdown=True):
|
||||||
# Strip out dirs that begin with .
|
# Strip out dirs that begin with .
|
||||||
dirs[:] = [d for d in dirs if d[0] != "."]
|
dirs[:] = [d for d in dirs if d[0] != "."]
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
if dir not in excluded_dirs:
|
if dir not in EXCLUDED_DIRS:
|
||||||
dirpath = path.join(root, dir)
|
dirpath = path.join(root, dir)
|
||||||
subdir_list.append(dirpath.replace(directory, "", 1))
|
# Remove the section of the path up until the first subdir
|
||||||
|
subdir_list.append(dirpath.replace(directory + "/", "", 1))
|
||||||
|
|
||||||
subdir_list.sort()
|
subdir_list.sort()
|
||||||
return subdir_list
|
return subdir_list
|
||||||
|
|
|
@ -401,7 +401,7 @@
|
||||||
{% for dir in images_subdirs %}
|
{% for dir in images_subdirs %}
|
||||||
<option value="{{dir}}">{{dir}}</option>
|
<option value="{{dir}}">{{dir}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<option value="/" selected>/</option>
|
<option value="" selected>/</option>
|
||||||
</select>
|
</select>
|
||||||
{% if file_server_dir_exists %}
|
{% if file_server_dir_exists %}
|
||||||
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
||||||
|
@ -411,7 +411,7 @@
|
||||||
{% for dir in shared_subdirs %}
|
{% for dir in shared_subdirs %}
|
||||||
<option value="{{dir}}">{{dir}}</option>
|
<option value="{{dir}}">{{dir}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<option value="/" selected>/</option>
|
<option value="" selected>/</option>
|
||||||
</select>
|
</select>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
|
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
|
||||||
|
|
|
@ -58,6 +58,7 @@ from web_utils import (
|
||||||
auth_active,
|
auth_active,
|
||||||
is_bridge_configured,
|
is_bridge_configured,
|
||||||
is_safe_path,
|
is_safe_path,
|
||||||
|
validate_target_dir,
|
||||||
browser_supports_modern_themes,
|
browser_supports_modern_themes,
|
||||||
)
|
)
|
||||||
from settings import (
|
from settings import (
|
||||||
|
@ -992,19 +993,15 @@ def download_file():
|
||||||
images_subdir = request.form.get("images_subdir")
|
images_subdir = request.form.get("images_subdir")
|
||||||
shared_subdir = request.form.get("shared_subdir")
|
shared_subdir = request.form.get("shared_subdir")
|
||||||
if destination == "disk_images":
|
if destination == "disk_images":
|
||||||
safe_path = is_safe_path(Path("." + images_subdir))
|
|
||||||
if not safe_path["status"]:
|
|
||||||
return make_response(safe_path["msg"], 403)
|
|
||||||
server_info = piscsi_cmd.get_server_info()
|
server_info = piscsi_cmd.get_server_info()
|
||||||
destination_dir = server_info["image_dir"] + images_subdir
|
destination_dir = Path(server_info["image_dir"]) / images_subdir
|
||||||
elif destination == "shared_files":
|
elif destination == "shared_files":
|
||||||
safe_path = is_safe_path(Path("." + shared_subdir))
|
destination_dir = Path(FILE_SERVER_DIR) / shared_subdir
|
||||||
if not safe_path["status"]:
|
|
||||||
return make_response(safe_path["msg"], 403)
|
|
||||||
destination_dir = FILE_SERVER_DIR + shared_subdir
|
|
||||||
else:
|
else:
|
||||||
return response(error=True, message=_("Unknown destination"))
|
return response(error=True, message=_("Unknown destination"))
|
||||||
|
|
||||||
|
validate_target_dir(destination_dir)
|
||||||
|
|
||||||
process = file_cmd.download_to_dir(url, Path(destination_dir) / Path(url).name)
|
process = file_cmd.download_to_dir(url, Path(destination_dir) / Path(url).name)
|
||||||
process = ReturnCodeMapper.add_msg(process)
|
process = ReturnCodeMapper.add_msg(process)
|
||||||
if process["status"]:
|
if process["status"]:
|
||||||
|
@ -1034,21 +1031,17 @@ def upload_file():
|
||||||
images_subdir = request.form.get("images_subdir")
|
images_subdir = request.form.get("images_subdir")
|
||||||
shared_subdir = request.form.get("shared_subdir")
|
shared_subdir = request.form.get("shared_subdir")
|
||||||
if destination == "disk_images":
|
if destination == "disk_images":
|
||||||
safe_path = is_safe_path(Path("." + images_subdir))
|
|
||||||
if not safe_path["status"]:
|
|
||||||
return make_response(safe_path["msg"], 403)
|
|
||||||
server_info = piscsi_cmd.get_server_info()
|
server_info = piscsi_cmd.get_server_info()
|
||||||
destination_dir = Path(server_info["image_dir"]) / images_subdir
|
destination_dir = Path(server_info["image_dir"]) / images_subdir
|
||||||
elif destination == "shared_files":
|
elif destination == "shared_files":
|
||||||
safe_path = is_safe_path(Path("." + shared_subdir))
|
|
||||||
if not safe_path["status"]:
|
|
||||||
return make_response(safe_path["msg"], 403)
|
|
||||||
destination_dir = Path(FILE_SERVER_DIR) / shared_subdir
|
destination_dir = Path(FILE_SERVER_DIR) / shared_subdir
|
||||||
elif destination == "piscsi_config":
|
elif destination == "piscsi_config":
|
||||||
destination_dir = Path(CFG_DIR)
|
destination_dir = Path(CFG_DIR)
|
||||||
else:
|
else:
|
||||||
return make_response(_("Unknown destination"), 403)
|
return make_response(_("Unknown destination"), 403)
|
||||||
|
|
||||||
|
validate_target_dir(destination_dir)
|
||||||
|
|
||||||
log = logging.getLogger("pydrop")
|
log = logging.getLogger("pydrop")
|
||||||
file_object = request.files["file"]
|
file_object = request.files["file"]
|
||||||
file_name = secure_filename(file_object.filename)
|
file_name = secure_filename(file_object.filename)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from pathlib import Path
|
||||||
from ua_parser import user_agent_parser
|
from ua_parser import user_agent_parser
|
||||||
from re import findall
|
from re import findall
|
||||||
|
|
||||||
from flask import request, abort
|
from flask import request, abort, make_response
|
||||||
from flask_babel import _
|
from flask_babel import _
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
|
@ -324,6 +324,16 @@ def is_safe_path(file_name):
|
||||||
return {"status": True, "msg": ""}
|
return {"status": True, "msg": ""}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_target_dir(target_dir):
|
||||||
|
"""
|
||||||
|
Takes (Path) target_dir on the host and validates that it is a valid dir for uploading.
|
||||||
|
"""
|
||||||
|
safe_path = is_safe_path(Path(".") / target_dir)
|
||||||
|
if not safe_path["status"]:
|
||||||
|
return make_response(safe_path["msg"], 403)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def browser_supports_modern_themes():
|
def browser_supports_modern_themes():
|
||||||
"""
|
"""
|
||||||
Determines if the browser supports the HTML/CSS/JS features used in non-legacy themes.
|
Determines if the browser supports the HTML/CSS/JS features used in non-legacy themes.
|
||||||
|
|
Loading…
Reference in New Issue