From 9dbaf252f0acaac73ecb5181d0eba453dbcef973 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 19 Jan 2022 19:30:45 +0530 Subject: [PATCH] fix: Check if binary file in Page Renderers * Check if binary before rendering using StaticPage resolver * Check if not binary before rendering using TemplatePage resolver --- frappe/website/page_renderers/static_page.py | 6 +++--- frappe/website/page_renderers/template_page.py | 4 ++-- frappe/website/utils.py | 12 ++++++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/frappe/website/page_renderers/static_page.py b/frappe/website/page_renderers/static_page.py index 632e9b4302..48bf0f2aec 100644 --- a/frappe/website/page_renderers/static_page.py +++ b/frappe/website/page_renderers/static_page.py @@ -6,6 +6,7 @@ from werkzeug.wsgi import wrap_file import frappe from frappe.website.page_renderers.base_renderer import BaseRenderer +from frappe.website.utils import is_binary_file UNSUPPORTED_STATIC_PAGE_TYPES = ('html', 'md', 'js', 'xml', 'css', 'txt', 'py', 'json') @@ -20,21 +21,20 @@ class StaticPage(BaseRenderer): return for app in frappe.get_installed_apps(): file_path = frappe.get_app_path(app, 'www') + '/' + self.path - if os.path.isfile(file_path): + if os.path.isfile(file_path) and is_binary_file(file_path): self.file_path = file_path def can_render(self): return self.is_valid_file_path() and self.file_path def is_valid_file_path(self): - if ('.' not in self.path): - return False extension = self.path.rsplit('.', 1)[-1] if extension in UNSUPPORTED_STATIC_PAGE_TYPES: return False return True def render(self): + # file descriptor to be left open, closed by middleware f = open(self.file_path, 'rb') response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True) response.mimetype = mimetypes.guess_type(self.file_path)[0] or 'application/octet-stream' diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py index cf017be30b..ff3e8509bd 100644 --- a/frappe/website/page_renderers/template_page.py +++ b/frappe/website/page_renderers/template_page.py @@ -7,7 +7,7 @@ from frappe.website.router import get_page_info from frappe.website.page_renderers.base_template_page import BaseTemplatePage from frappe.website.router import get_base_template from frappe.website.utils import (extract_comment_tag, extract_title, get_next_link, - get_toc, get_frontmatter, cache_html, get_sidebar_items) + get_toc, get_frontmatter, is_binary_file, cache_html, get_sidebar_items) WEBPAGE_PY_MODULE_PROPERTIES = ("base_template_path", "template", "no_cache", "sitemap", "condition_field") @@ -39,7 +39,7 @@ class TemplatePage(BaseTemplatePage): for dirname in folders: search_path = os.path.join(app_path, dirname, self.path) for file_path in self.get_index_path_options(search_path): - if os.path.isfile(file_path): + if os.path.isfile(file_path) and not is_binary_file(file_path): self.app = app self.app_path = app_path self.file_dir = dirname diff --git a/frappe/website/utils.py b/frappe/website/utils.py index cb8008277c..27c36923f2 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -1,10 +1,10 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import json import mimetypes import os import re -from functools import wraps +from functools import cache, wraps import yaml from six import iteritems @@ -511,3 +511,11 @@ def add_preload_headers(response): except Exception: import traceback traceback.print_exc() + +@cache +def is_binary_file(path): + # ref: https://stackoverflow.com/a/7392391/10309266 + textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f}) + with open(path, 'rb') as f: + content = f.read(1024) + return bool(content.translate(None, textchars))