From bdd77f9150fde59d71fdc837ff4cd2cbf0d62896 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Wed, 14 Mar 2018 10:34:45 +0000 Subject: [PATCH] Escape special characters in search by recipient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLAlchemy handles escaping anything that could allow a SQL injection attack. But it doesn’t escape the characters used for wildcard searching. This is the reason we’re able to do `.like('%example%')` at all. But we shouldn’t be letting our users search with wildcard characters, so we need to escape them. Which is what this commit does. --- app/dao/notifications_dao.py | 6 +++ .../notification_dao/test_notification_dao.py | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 3e7e4ff12..a7a8be166 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -455,6 +455,12 @@ def dao_get_notifications_by_to_field(service_id, search_term, notification_type else: raise InvalidRequest("Only email and SMS can use search by recipient", 400) + for special_character in {'_', '%', '/'}: + normalised = normalised.replace( + special_character, + '\\{}'.format(special_character) + ) + filters = [ Notification.service_id == service_id, Notification.normalised_to.like("%{}%".format(normalised)), diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index db9d999a3..0d5756897 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -1769,6 +1769,45 @@ def test_dao_get_notifications_by_to_field_matches_partial_emails(sample_email_t assert notification_2.id not in notification_ids +@pytest.mark.parametrize('search_term, expected_result_count', [ + ('foobar', 1), + ('foo', 2), + ('bar', 2), + ('foo%', 1), + ('%%bar', 1), + ('%_', 1), + ('%', 2), + ('_', 1), + ('/', 1), + ('%foo', 0), + ('%_%', 0), + ('example.com', 4), +]) +def test_dao_get_notifications_by_to_field_escapes( + sample_email_template, + search_term, + expected_result_count, +): + + for email_address in { + 'foo%_@example.com', + '%%bar@example.com', + 'foobar@example.com', + '/@example.com', + }: + create_notification( + template=sample_email_template, + to_field=email_address, + normalised_to=email_address, + ) + + assert len(dao_get_notifications_by_to_field( + sample_email_template.service_id, + search_term, + notification_type='email', + )) == expected_result_count + + @pytest.mark.parametrize('search_term', [ '001', '100',