diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index 0fc2231e5cbd29..671279c2a2a5a4 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -300,7 +300,13 @@ def parse(cls, value, kwds): kwds['parse_tree'] = parser.TokenList() return if isinstance(value, str): + kwds['decoded'] = value value = utils.parsedate_to_datetime(value) + if value is None: + kwds['defects'].append(errors.InvalidHeaderDefect('Invalid value in date')) + kwds['datetime'] = None + kwds['parse_tree'] = parser.TokenList() + return kwds['datetime'] = value kwds['decoded'] = utils.format_datetime(kwds['datetime']) kwds['parse_tree'] = cls.value_parser(kwds['decoded']) diff --git a/Lib/email/utils.py b/Lib/email/utils.py index a759d23308d302..b694d2aa91156d 100644 --- a/Lib/email/utils.py +++ b/Lib/email/utils.py @@ -207,11 +207,19 @@ def make_msgid(idstring=None, domain=None): def parsedate_to_datetime(data): - *dtuple, tz = _parsedate_tz(data) - if tz is None: - return datetime.datetime(*dtuple[:6]) - return datetime.datetime(*dtuple[:6], - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + try: + *dtuple, tz = _parsedate_tz(data) + except TypeError: + # _parsedate_tz(data) returned None due to failure to parse + return None + try: + if tz is None: + return datetime.datetime(*dtuple[:6]) + return datetime.datetime(*dtuple[:6], + tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) + except ValueError: + # Date parsed ok, but one or more component values are invalid + return None def parseaddr(addr): diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py index 888751e9e617f6..e618e6e7dc83d8 100644 --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -37,9 +37,11 @@ def __init__(self, *args, **kw): # Backward compatibility to minimize test_email test changes. ndiffAssertEqual = unittest.TestCase.assertEqual - def _msgobj(self, filename): + def _msgobj(self, filename, policy=None): + if policy is None: + policy = self.policy with openfile(filename) as fp: - return email.message_from_file(fp, policy=self.policy) + return email.message_from_file(fp, policy=policy) def _str_msg(self, string, message=None, policy=None): if policy is None: diff --git a/Lib/test/test_email/data/msg_47.txt b/Lib/test/test_email/data/msg_47.txt new file mode 100644 index 00000000000000..7361dc7fea5d83 --- /dev/null +++ b/Lib/test/test_email/data/msg_47.txt @@ -0,0 +1,6 @@ +Date: Tue, 06 Jun 2017 27:39:33 +0600 +From: "123@abuse.net" <123@example.com> +To: <123@abuse.net> +Subject: VIARGA|CIALIS|LEVITRA + +### PHARRMACY ON1INE 24/7 ### diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index f97ccc6711cc73..5d04971ed8de52 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -39,6 +39,7 @@ from email import iterators from email import base64mime from email import quoprimime +from email._policybase import compat32 from test.support import unlink, start_threads from test.test_email import openfile, TestEmailBase @@ -759,6 +760,10 @@ def test_unicode_body_defaults_to_utf8_encoding(self): w4kgdGVzdGFiYwo= """)) + def test_date_header_from_message_with_invalid_date(self): + msg = self._msgobj('msg_47.txt', policy=email.policy.default) + self.assertEqual(msg['date'], 'Tue, 06 Jun 2017 27:39:33 +0600') + # Test the email.encoders module class TestEncoders(unittest.TestCase): @@ -2710,10 +2715,12 @@ class TestIdempotent(TestEmailBase): linesep = '\n' - def _msgobj(self, filename): + def _msgobj(self, filename, policy=None): + if policy is None: + policy = self.policy with openfile(filename) as fp: data = fp.read() - msg = email.message_from_string(data) + msg = email.message_from_string(data, policy=policy) return msg, data def _idempotent(self, msg, text, unixfrom=False): @@ -2815,6 +2822,10 @@ def test_message_signed_idempotent(self): msg, text = self._msgobj('msg_45.txt') self._idempotent(msg, text) + def test_invalid_date(self): + msg, text = self._msgobj('msg_47.txt', policy=email.policy.default) + self._idempotent(msg, text) + def test_content_type(self): eq = self.assertEqual # Get a message object and reset the seek pointer for other tests @@ -3012,6 +3023,16 @@ def test_parsedate_acceptable_to_time_functions(self): eq(time.localtime(t)[:6], timetup[:6]) eq(int(time.strftime('%Y', timetup[:9])), 2003) + def test_parsedate_to_datetime_returns_None_for_invalid_strings(self): + self.assertIsNone(utils.parsedate_to_datetime('')) + self.assertIsNone(utils.parsedate_to_datetime('0')) + self.assertIsNone(utils.parsedate_to_datetime('A Complete Waste of Time')) + + def test_parsedate_to_datetime_returns_None_for_invalid_dates(self): + self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 27:39:33 +0600')) + self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 07:39:33 +2600')) + self.assertIsNone(utils.parsedate_to_datetime('Tue, 06 Jun 2017 27:39:33')) + def test_mktime_tz(self): self.assertEqual(utils.mktime_tz((1970, 1, 1, 0, 0, 0, -1, -1, -1, 0)), 0) @@ -4154,11 +4175,13 @@ class BaseTestBytesGeneratorIdempotent: maxDiff = None - def _msgobj(self, filename): + def _msgobj(self, filename, policy=None): + if policy is None: + policy = self.policy with openfile(filename, 'rb') as fp: data = fp.read() data = self.normalize_linesep_regex.sub(self.blinesep, data) - msg = email.message_from_bytes(data) + msg = email.message_from_bytes(data, policy=policy) return msg, data def _idempotent(self, msg, data, unixfrom=False):