mirror of
https://github.com/akuker/RASCSI.git
synced 2024-06-05 01:29:29 +00:00
Correct upload dir path validation logic (#1338)
* Correct upload and download dir path validation logic * Improve file download labels * Clean up tmp file before attempting to upload again
This commit is contained in:
parent
bd30073cb0
commit
8d26807573
|
@ -395,8 +395,8 @@
|
||||||
<label for="download_url">{{ _("Download file from URL:") }}</label>
|
<label for="download_url">{{ _("Download file from URL:") }}</label>
|
||||||
<input name="url" id="download_url" required="" type="url">
|
<input name="url" id="download_url" required="" type="url">
|
||||||
<label for="disk_images" class="hidden">{{ _("Disk Images") }}</label>
|
<label for="disk_images" class="hidden">{{ _("Disk Images") }}</label>
|
||||||
|
<label for="images_subdir">{{ _("To directory:") }}</label>
|
||||||
<input type="radio" name="destination" id="disk_images" value="disk_images" checked="checked">
|
<input type="radio" name="destination" id="disk_images" value="disk_images" checked="checked">
|
||||||
<label for="images_subdir" class="hidden">{{ _("Directory") }}</label>
|
|
||||||
<select name="images_subdir" id="images_subdir">
|
<select name="images_subdir" id="images_subdir">
|
||||||
{% for dir in images_subdirs %}
|
{% for dir in images_subdirs %}
|
||||||
<option value="{{dir}}">{{env['image_root_dir']}}/{{dir}}</option>
|
<option value="{{dir}}">{{env['image_root_dir']}}/{{dir}}</option>
|
||||||
|
@ -406,7 +406,7 @@
|
||||||
{% if file_server_dir_exists %}
|
{% if file_server_dir_exists %}
|
||||||
<label for="shared_files" class="hidden">{{ _("Shared Files") }}</label>
|
<label for="shared_files" class="hidden">{{ _("Shared Files") }}</label>
|
||||||
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
||||||
<label for="shared_subdir" class="hidden">{{ _("Directory") }}</label>
|
<label for="shared_subdir" class="hidden">{{ _("To directory:") }}</label>
|
||||||
<select name="shared_subdir" id="shared_subdir">
|
<select name="shared_subdir" id="shared_subdir">
|
||||||
{% for dir in shared_subdirs %}
|
{% for dir in shared_subdirs %}
|
||||||
<option value="{{dir}}">{{env['shared_root_dir']}}/{{dir}}</option>
|
<option value="{{dir}}">{{env['shared_root_dir']}}/{{dir}}</option>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<legend>{{ _("Destination") }}</legend>
|
<legend>{{ _("Destination") }}</legend>
|
||||||
<label for="disk_images" class="hidden">{{ _("Disk Images") }}</label>
|
<label for="disk_images" class="hidden">{{ _("Disk Images") }}</label>
|
||||||
<input type="radio" name="destination" id="disk_images" value="disk_images" checked="checked">
|
<input type="radio" name="destination" id="disk_images" value="disk_images" checked="checked">
|
||||||
<label for="images_subdir" class="hidden">{{ _("Directory") }}</label>
|
<label for="images_subdir" class="hidden">{{ _("To directory:") }}</label>
|
||||||
<select name="images_subdir" id="images_subdir">
|
<select name="images_subdir" id="images_subdir">
|
||||||
{% for dir in images_subdirs %}
|
{% for dir in images_subdirs %}
|
||||||
<option value="{{dir}}">{{ env['image_root_dir'] }}/{{dir}}</option>
|
<option value="{{dir}}">{{ env['image_root_dir'] }}/{{dir}}</option>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
{% if file_server_dir_exists %}
|
{% if file_server_dir_exists %}
|
||||||
<label for="shared_files" class="hidden">{{ _("Shared Files") }}</label>
|
<label for="shared_files" class="hidden">{{ _("Shared Files") }}</label>
|
||||||
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
<input type="radio" name="destination" id="shared_files" value="shared_files">
|
||||||
<label for="shared_subdir" class="hidden">{{ _("Directory") }}</label>
|
<label for="shared_subdir" class="hidden">{{ _("To directory:") }}</label>
|
||||||
<select name="shared_subdir" id="shared_subdir">
|
<select name="shared_subdir" id="shared_subdir">
|
||||||
{% for dir in shared_subdirs %}
|
{% for dir in shared_subdirs %}
|
||||||
<option value="{{dir}}">{{ env['shared_root_dir'] }}/{{dir}}</option>
|
<option value="{{dir}}">{{ env['shared_root_dir'] }}/{{dir}}</option>
|
||||||
|
|
|
@ -58,7 +58,6 @@ 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 (
|
||||||
|
@ -996,15 +995,19 @@ 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 response(error=True, message=safe_path["msg"])
|
||||||
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 response(error=True, message=safe_path["msg"])
|
||||||
destination_dir = Path(FILE_SERVER_DIR) / shared_subdir
|
destination_dir = Path(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,17 +1037,21 @@ 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)
|
||||||
|
@ -1059,6 +1066,10 @@ def upload_file():
|
||||||
if path.exists(save_path) and current_chunk == 0:
|
if path.exists(save_path) and current_chunk == 0:
|
||||||
return make_response(_("The file already exists!"), 400)
|
return make_response(_("The file already exists!"), 400)
|
||||||
|
|
||||||
|
if path.exists(tmp_save_path) and current_chunk == 0:
|
||||||
|
log.info("Deleting existing temporary file before uploading...")
|
||||||
|
file_cmd.delete_file(tmp_save_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(tmp_save_path, "ab") as save:
|
with open(tmp_save_path, "ab") as save:
|
||||||
save.seek(int(request.form["dzchunkbyteoffset"]))
|
save.seek(int(request.form["dzchunkbyteoffset"]))
|
||||||
|
|
|
@ -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, make_response
|
from flask import request, abort
|
||||||
from flask_babel import _
|
from flask_babel import _
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
|
@ -302,37 +302,27 @@ def is_bridge_configured(interface):
|
||||||
return {"status": True, "msg": return_msg + ", ".join(to_configure)}
|
return {"status": True, "msg": return_msg + ", ".join(to_configure)}
|
||||||
|
|
||||||
|
|
||||||
def is_safe_path(file_name):
|
def is_safe_path(path_name):
|
||||||
"""
|
"""
|
||||||
Takes (Path) file_name with the path to a file on the file system
|
Takes (Path) path_name with the relative path to a file on the file system.
|
||||||
Returns True if the path is safe
|
Returns False if the path is either absolute, or tries to traverse the file system.
|
||||||
Returns False if the path is either absolute, or tries to traverse the file system
|
Returns True if neither of the above criteria are met.
|
||||||
"""
|
"""
|
||||||
error_message = ""
|
error_message = ""
|
||||||
if file_name.is_absolute():
|
if path_name.is_absolute():
|
||||||
error_message = _("Path must not be absolute")
|
error_message = _("Path must not be absolute")
|
||||||
elif "../" in str(file_name):
|
elif "../" in str(path_name):
|
||||||
error_message = _("Path must not traverse the file system")
|
error_message = _("Path must not traverse the file system")
|
||||||
elif str(file_name)[0] == "~":
|
elif str(path_name)[0] == "~":
|
||||||
error_message = _("Path must not start in the home directory")
|
error_message = _("Path must not start in the home directory")
|
||||||
|
|
||||||
if error_message:
|
if error_message:
|
||||||
logging.error("Not an allowed path: %s", str(file_name))
|
logging.error("Not an allowed path: %s", str(path_name))
|
||||||
return {"status": False, "msg": error_message}
|
return {"status": False, "msg": error_message}
|
||||||
|
|
||||||
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
Block a user