Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-95087: Fix IndexError in parsing invalid date in the email module #95201

Merged
merged 1 commit into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/email/_parseaddr.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ def _parsedate_tz(data):
return None
data = data[:5]
[dd, mm, yy, tm, tz] = data
if not (dd and mm and yy):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit?

Suggested change
if not (dd and mm and yy):
if not all((dd, mm, yy)):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks less clear to me (and it is also slower). Do you write sum((dd, mm, yy)) instead of dd + mm + yy?

return None
mm = mm.lower()
if mm not in _monthnames:
dd, mm = mm, dd.lower()
Expand All @@ -110,6 +112,8 @@ def _parsedate_tz(data):
yy, tm = tm, yy
if yy[-1] == ',':
yy = yy[:-1]
if not yy:
return None
if not yy[0].isdigit():
yy, tz = tz, yy
if tm[-1] == ',':
Expand Down
57 changes: 40 additions & 17 deletions Lib/test/test_email/test_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -3047,33 +3047,43 @@ def test_formatdate_usegmt(self):

# parsedate and parsedate_tz will become deprecated interfaces someday
def test_parsedate_returns_None_for_invalid_strings(self):
self.assertIsNone(utils.parsedate(''))
self.assertIsNone(utils.parsedate_tz(''))
self.assertIsNone(utils.parsedate(' '))
self.assertIsNone(utils.parsedate_tz(' '))
self.assertIsNone(utils.parsedate('0'))
self.assertIsNone(utils.parsedate_tz('0'))
self.assertIsNone(utils.parsedate('A Complete Waste of Time'))
self.assertIsNone(utils.parsedate_tz('A Complete Waste of Time'))
self.assertIsNone(utils.parsedate_tz('Wed, 3 Apr 2002 12.34.56.78+0800'))
# See also test_parsedate_to_datetime_with_invalid_raises_valueerror
# in test_utils.
invalid_dates = [
'',
' ',
'0',
'A Complete Waste of Time',
'Wed, 3 Apr 2002 12.34.56.78+0800',
'17 June , 2022',
'Friday, -Nov-82 16:14:55 EST',
'Friday, Nov--82 16:14:55 EST',
'Friday, 19-Nov- 16:14:55 EST',
]
for dtstr in invalid_dates:
with self.subTest(dtstr=dtstr):
self.assertIsNone(utils.parsedate(dtstr))
self.assertIsNone(utils.parsedate_tz(dtstr))
# Not a part of the spec but, but this has historically worked:
self.assertIsNone(utils.parsedate(None))
self.assertIsNone(utils.parsedate_tz(None))

def test_parsedate_compact(self):
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26 +0800'),
(2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800))
# The FWS after the comma is optional
self.assertEqual(utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'),
utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800'))
self.assertEqual(utils.parsedate_tz('Wed,3 Apr 2002 14:58:26 +0800'),
(2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800))
# The comma is optional
self.assertEqual(utils.parsedate_tz('Wed 3 Apr 2002 14:58:26 +0800'),
(2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800))

def test_parsedate_no_dayofweek(self):
eq = self.assertEqual
eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'),
(2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800))

def test_parsedate_compact_no_dayofweek(self):
eq = self.assertEqual
eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'),
(2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800))
eq(utils.parsedate_tz('February 5, 2003 13:47:26 -0800'),
(2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800))

def test_parsedate_no_space_before_positive_offset(self):
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26+0800'),
Expand All @@ -3084,14 +3094,27 @@ def test_parsedate_no_space_before_negative_offset(self):
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26-0800'),
(2002, 4, 3, 14, 58, 26, 0, 1, -1, -28800))


def test_parsedate_accepts_time_with_dots(self):
eq = self.assertEqual
eq(utils.parsedate_tz('5 Feb 2003 13.47.26 -0800'),
(2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800))
eq(utils.parsedate_tz('5 Feb 2003 13.47 -0800'),
(2003, 2, 5, 13, 47, 0, 0, 1, -1, -28800))

def test_parsedate_rfc_850(self):
self.assertEqual(utils.parsedate_tz('Friday, 19-Nov-82 16:14:55 EST'),
(1982, 11, 19, 16, 14, 55, 0, 1, -1, -18000))

def test_parsedate_no_seconds(self):
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58 +0800'),
(2002, 4, 3, 14, 58, 0, 0, 1, -1, 28800))

def test_parsedate_dot_time_delimiter(self):
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14.58.26 +0800'),
(2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800))
self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14.58 +0800'),
(2002, 4, 3, 14, 58, 0, 0, 1, -1, 28800))

def test_parsedate_acceptable_to_time_functions(self):
eq = self.assertEqual
timetup = utils.parsedate('5 Feb 2003 13:47:26 -0800')
Expand Down
21 changes: 15 additions & 6 deletions Lib/test/test_email/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,21 @@ def test_parsedate_to_datetime_naive(self):
self.naive_dt)

def test_parsedate_to_datetime_with_invalid_raises_valueerror(self):
invalid_dates = ['',
'0',
'A Complete Waste of Time'
'Tue, 06 Jun 2017 27:39:33 +0600',
'Tue, 06 Jun 2017 07:39:33 +2600',
'Tue, 06 Jun 2017 27:39:33']
# See also test_parsedate_returns_None_for_invalid_strings in test_email.
invalid_dates = [
'',
' ',
'0',
'A Complete Waste of Time',
'Wed, 3 Apr 2002 12.34.56.78+0800'
'Tue, 06 Jun 2017 27:39:33 +0600',
'Tue, 06 Jun 2017 07:39:33 +2600',
'Tue, 06 Jun 2017 27:39:33',
'17 June , 2022',
'Friday, -Nov-82 16:14:55 EST',
'Friday, Nov--82 16:14:55 EST',
'Friday, 19-Nov- 16:14:55 EST',
]
for dtstr in invalid_dates:
with self.subTest(dtstr=dtstr):
self.assertRaises(ValueError, utils.parsedate_to_datetime, dtstr)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix IndexError in parsing invalid date in the :mod:`email` module.