From 7c9ce26469d4beb1e9d407c87c7d22fa7df38116 Mon Sep 17 00:00:00 2001 From: AarDG10 Date: Mon, 20 Apr 2026 18:34:08 +0530 Subject: [PATCH 1/2] feat(utils): add util to ensure sandboxing This util can be used in places where sandboxing is needed. --- frappe/core/doctype/file/utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frappe/core/doctype/file/utils.py b/frappe/core/doctype/file/utils.py index 20ad5d7253..b966df9770 100644 --- a/frappe/core/doctype/file/utils.py +++ b/frappe/core/doctype/file/utils.py @@ -480,3 +480,16 @@ def find_file_by_url(path: str, name: str | None = None) -> "File" | None: def get_safe_file_name(file_name: str) -> str: return re.sub(r"[/\\%?#]", "_", file_name) + + +def check_path_safety(base_path: str, requested_path: str) -> bool: + """Util to check path safety by ensuring sandboxing and logging unsuccessful attempts""" + base_path = os.path.realpath(base_path) + requested_path = os.path.realpath(requested_path) + if os.path.commonpath([base_path, requested_path]) != base_path: + frappe.log_error( + title="Attempted Unauthorized File Access", + message=f"Blocked access to: {requested_path}", + ) + return False + return True From 0c660477ee567f458a2ab0a68b56bbd4c8135faa Mon Sep 17 00:00:00 2001 From: AarDG10 Date: Mon, 20 Apr 2026 18:57:46 +0530 Subject: [PATCH 2/2] fix(response): harden download_backup Made use of util `check_path_safety` to ensure sandboxing. --- frappe/utils/response.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 5fdd459c95..ec48d524ea 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -26,6 +26,7 @@ import frappe.sessions import frappe.utils from frappe import _ from frappe.core.doctype.access_log.access_log import make_access_log +from frappe.core.doctype.file.utils import check_path_safety from frappe.utils import format_timedelta, orjson_dumps if TYPE_CHECKING: @@ -280,6 +281,13 @@ def download_backup(path): _("You need to be logged in and have System Manager Role to be able to access backups.") ) + filename = path.split("/backups/", 1)[1] + backup_path = frappe.get_site_path("private", "backups") + requested_path = frappe.get_site_path("private", "backups", filename) + is_safe = check_path_safety(base_path=backup_path, requested_path=requested_path) + if not is_safe: + frappe.throw(_("Invalid backup path"), frappe.PermissionError) + return send_private_file(path)