From e9cdf322c6fbdc738d417ff2f13898ec171d86ce Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 25 Jun 2018 11:23:32 +0530 Subject: [PATCH] [security][fix] Sanitize search fields to avoid sql injection (#5713) * [security][fix] Sanitize search fields to avoid sql injection * Test Cases for Sanitizer * Test Cases fix * [fix] test case --- frappe/desk/search.py | 28 ++++++++++++++++++++++++++++ frappe/tests/test_search.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 frappe/tests/test_search.py diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 8527076afe..67e52be218 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -8,6 +8,31 @@ from frappe.utils import cstr, unique from frappe import _ from six import string_types + +def sanitize_searchfield(searchfield): + blacklisted_keywords = ['select', 'delete', 'drop', 'update', 'case', 'and', 'or', 'like'] + + def _raise_exception(): + frappe.throw(_('Invalid Search Field'), frappe.DataError) + + if len(searchfield) >= 3: + + # to avoid 1=1 + if '=' in searchfield: + _raise_exception() + + # in mysql -- is used for commenting the query + elif ' --' in searchfield: + _raise_exception() + + # to avoid and, or and like + elif any(' {0} '.format(keyword) in searchfield.split() for keyword in blacklisted_keywords): + _raise_exception() + + # to avoid select, delete, drop, update and case + elif any(keyword in searchfield.split() for keyword in blacklisted_keywords): + _raise_exception() + # this is called by the Link Field @frappe.whitelist() def search_link(doctype, txt, query=None, filters=None, page_length=20, searchfield=None): @@ -24,6 +49,9 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, meta = frappe.get_meta(doctype) + if searchfield: + sanitize_searchfield(searchfield) + if not searchfield: searchfield = "name" diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py new file mode 100644 index 0000000000..ee4dd3cc53 --- /dev/null +++ b/frappe/tests/test_search.py @@ -0,0 +1,28 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import unittest +import frappe +from frappe.desk.search import search_link + + +class TestSearch(unittest.TestCase): + def test_search_field_sanitizer(self): + # pass + search_link('DocType', 'User', query=None, filters=None, page_length=20, searchfield='name') + result = frappe.response['results'][0] + self.assertTrue('User' in result['value']) + + #raise exception on injection + self.assertRaises(frappe.DataError, + search_link, 'DocType', 'Customer', query=None, filters=None, + page_length=20, searchfield='1=1') + + self.assertRaises(frappe.DataError, + search_link, 'DocType', 'Customer', query=None, filters=None, + page_length=20, searchfield='select * from tabSessions) --') + + self.assertRaises(frappe.DataError, + search_link, 'DocType', 'Customer', query=None, filters=None, + page_length=20, searchfield='name or (select * from tabSessions)')