diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 4843219179..446f842a0b 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -7,45 +7,18 @@ import re import frappe from frappe import _, is_whitelisted +from frappe.database.schema import SPECIAL_CHAR_PATTERN from frappe.permissions import has_permission from frappe.utils import cint, cstr, unique def sanitize_searchfield(searchfield): - blacklisted_keywords = ["select", "delete", "drop", "update", "case", "and", "or", "like"] + if not searchfield: + return - def _raise_exception(searchfield): + if SPECIAL_CHAR_PATTERN.search(searchfield): frappe.throw(_("Invalid Search Field {0}").format(searchfield), frappe.DataError) - if len(searchfield) == 1: - # do not allow special characters to pass as searchfields - regex = re.compile(r'^.*[=;*,\'"$\-+%#@()_].*') - if regex.match(searchfield): - _raise_exception(searchfield) - - if len(searchfield) >= 3: - - # to avoid 1=1 - if "=" in searchfield: - _raise_exception(searchfield) - - # in mysql -- is used for commenting the query - elif " --" in searchfield: - _raise_exception(searchfield) - - # to avoid and, or and like - elif any(f" {keyword} " in searchfield.split() for keyword in blacklisted_keywords): - _raise_exception(searchfield) - - # to avoid select, delete, drop, update and case - elif any(keyword in searchfield.split() for keyword in blacklisted_keywords): - _raise_exception(searchfield) - - else: - regex = re.compile(r'^.*[=;*,\'"$\-+%#@()].*') - if any(regex.match(f) for f in searchfield.split()): - _raise_exception(searchfield) - # this is called by the Link Field @frappe.whitelist() diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index e010d2abc0..5d98d6f49f 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -1,10 +1,16 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import re import frappe from frappe.app import make_form_dict -from frappe.desk.search import get_names_for_mentions, search_link, search_widget +from frappe.desk.search import ( + get_names_for_mentions, + sanitize_searchfield, + search_link, + search_widget, +) from frappe.tests.utils import FrappeTestCase from frappe.utils import set_request from frappe.website.serve import get_response @@ -179,6 +185,17 @@ class TestSearch(FrappeTestCase): search_link("User", "user@random", searchfield="name") self.assertListEqual(frappe.response["results"], []) + def test_sanitize_searchfield(self): + for searchfield in ("1=1", "name or (select * from tabSessions)", ";", "`tabSessions`"): + self.assertRaisesRegex( + frappe.DataError, + re.compile(r"^(Invalid Search Field .*)$"), + sanitize_searchfield, + searchfield, + ) + + sanitize_searchfield("name") + @frappe.validate_and_sanitize_search_inputs def get_data(doctype, txt, searchfield, start, page_len, filters):