From 0344075f8917a0ff9fc4c3315376100723573e77 Mon Sep 17 00:00:00 2001 From: Johan Nestaas Date: Thu, 19 Jan 2017 17:35:42 -0800 Subject: [PATCH 1/3] fix issue where whitelist always passes for email validation --- validators/email.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/validators/email.py b/validators/email.py index 6c36c2e8..7ff157ae 100644 --- a/validators/email.py +++ b/validators/email.py @@ -51,9 +51,6 @@ def email(value, whitelist=None): :license: BSD """ - if whitelist is None: - whitelist = domain_whitelist - if not value or '@' not in value: return False @@ -62,11 +59,15 @@ def email(value, whitelist=None): if not user_regex.match(user_part): return False - if domain_part not in whitelist and not domain_regex.match(domain_part): + if whitelist is not None and domain_part not in whitelist: # Try for possible IDN domain-part - try: - domain_part = domain_part.encode('idna').decode('ascii') - return domain_regex.match(domain_part) - except UnicodeError: + if not domain_regex.match(domain_part): + try: + domain_part = domain_part.encode('idna').decode('ascii') + return domain_regex.match(domain_part) + except UnicodeError: + return False + # Domain is fine but not in whitelist so it's a fail + else: return False return True From dcd08db3c10bc5a8dd7400b61fd01ed855dbf61b Mon Sep 17 00:00:00 2001 From: Johan Nestaas Date: Fri, 20 Jan 2017 12:34:04 -0800 Subject: [PATCH 2/3] add only_whitelist keyword to email validation so that it only validates listed domains --- tests/test_email.py | 29 +++++++++++++++++++++++++++++ validators/email.py | 27 ++++++++++++++++----------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/tests/test_email.py b/tests/test_email.py index 70fbb23e..149ca197 100644 --- a/tests/test_email.py +++ b/tests/test_email.py @@ -39,3 +39,32 @@ def test_returns_true_on_valid_email(value, whitelist): ]) def test_returns_failed_validation_on_invalid_email(value): assert isinstance(email(value), ValidationFailure) + + +@pytest.mark.parametrize(('value', 'whitelist'), [ + ('email@here.com', ['foo.here.com']), + ('weirder-email@here.and.there.com', ['here.and.there.org']), + ('email@[127.0.0.1]', ['[127.0.0.2]']), + ('example@valid-----hyphens.com', ['-----hyphens.com']), + ('example@valid-with-hyphens.com', ['hyphens.com']), + ('email@localhost', ['localdomain']), + ('"test@test"@example.com', ['example.org', 'example.net', 'foo.org']), + ('"\\\011"@here.com', ['there.com']), +]) +def test_returns_failed_validation_on_bad_whitelist(value, whitelist): + assert isinstance(email(value, whitelist=whitelist, only_whitelist=True), + ValidationFailure) + + +@pytest.mark.parametrize(('value', 'whitelist'), [ + ('email@here.com', ['here.com']), + ('weirder-email@here.and.there.com', ['here.and.there.com']), + ('email@[127.0.0.1]', ['[127.0.0.1]']), + ('example@valid-----hyphens.com', ['valid-----hyphens.com']), + ('example@valid-with-hyphens.com', ['valid-with-hyphens.com']), + ('email@localhost', ['localdomain', 'localhost']), + ('"test@test"@example.com', ['example.org', 'example.com', 'foo.org']), + ('"\\\011"@here.com', ['here.com', 'test.org', 'zeta', 100]), +]) +def test_returns_true_on_valid_email_and_only_whitelist(value, whitelist): + assert email(value, whitelist=whitelist, only_whitelist=True) diff --git a/validators/email.py b/validators/email.py index 7ff157ae..e275083e 100644 --- a/validators/email.py +++ b/validators/email.py @@ -23,7 +23,7 @@ @validator -def email(value, whitelist=None): +def email(value, whitelist=None, only_whitelist=False): """ Validate an email address. @@ -50,24 +50,29 @@ def email(value, whitelist=None): :copyright: (c) Django Software Foundation and individual contributors. :license: BSD """ - if not value or '@' not in value: return False + if whitelist is None: + whitelist = domain_whitelist + user_part, domain_part = value.rsplit('@', 1) if not user_regex.match(user_part): return False - if whitelist is not None and domain_part not in whitelist: + # skip domain regex check if user specified it explicitly in whitelist + if only_whitelist: + return domain_part in whitelist + + # check if domain is good + if not (domain_regex.match(domain_part) or domain_part in whitelist): # Try for possible IDN domain-part - if not domain_regex.match(domain_part): - try: - domain_part = domain_part.encode('idna').decode('ascii') - return domain_regex.match(domain_part) - except UnicodeError: - return False - # Domain is fine but not in whitelist so it's a fail - else: + try: + domain_part = domain_part.encode('idna').decode('ascii') + return domain_regex.match(domain_part) + except UnicodeError: return False + + # No whitelist and email passes user_regex and domain passes domain_regex. return True From 4a223ed04c2e9e8bb8152501fd748ab79365a907 Mon Sep 17 00:00:00 2001 From: Johan Nestaas Date: Fri, 20 Jan 2017 12:35:46 -0800 Subject: [PATCH 3/3] add param comment --- validators/email.py | 1 + 1 file changed, 1 insertion(+) diff --git a/validators/email.py b/validators/email.py index e275083e..d92b5837 100644 --- a/validators/email.py +++ b/validators/email.py @@ -46,6 +46,7 @@ def email(value, whitelist=None, only_whitelist=False): :param value: value to validate :param whitelist: domain names to whitelist + :param only_whitelist: specify whether to fail non whitelisted domains :copyright: (c) Django Software Foundation and individual contributors. :license: BSD