From 614f17211c5fc0e5b828be1d3320661d1038fe8f Mon Sep 17 00:00:00 2001 From: Ashwin Ramaswami Date: Sun, 29 Mar 2020 20:38:41 -0400 Subject: [PATCH] bpo-39073: validate Address parts to disallow CRLF (#19007) Disallow CR or LF in email.headerregistry.Address arguments to guard against header injection attacks. --- Lib/email/headerregistry.py | 5 +++++ Lib/test/test_email/test_headerregistry.py | 19 +++++++++++++++++++ .../2020-03-15-01-28-36.bpo-39073.6Szd3i.rst | 1 + 3 files changed, 25 insertions(+) create mode 100644 Misc/NEWS.d/next/Security/2020-03-15-01-28-36.bpo-39073.6Szd3i.rst diff --git a/Lib/email/headerregistry.py b/Lib/email/headerregistry.py index cc1d1912918..5d84fc0d82d 100644 --- a/Lib/email/headerregistry.py +++ b/Lib/email/headerregistry.py @@ -31,6 +31,11 @@ class Address: without any Content Transfer Encoding. """ + + inputs = ''.join(filter(None, (display_name, username, domain, addr_spec))) + if '\r' in inputs or '\n' in inputs: + raise ValueError("invalid arguments; address parts cannot contain CR or LF") + # This clause with its potential 'raise' may only happen when an # application program creates an Address object using an addr_spec # keyword. The email library code itself must always supply username diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index 38f7ddbf06d..82e121350ff 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -1437,6 +1437,25 @@ class TestAddressAndGroup(TestEmailBase): # with self.assertRaises(ValueError): # Address('foo', 'wők', 'example.com') + def test_crlf_in_constructor_args_raises(self): + cases = ( + dict(display_name='foo\r'), + dict(display_name='foo\n'), + dict(display_name='foo\r\n'), + dict(domain='example.com\r'), + dict(domain='example.com\n'), + dict(domain='example.com\r\n'), + dict(username='wok\r'), + dict(username='wok\n'), + dict(username='wok\r\n'), + dict(addr_spec='wok@example.com\r'), + dict(addr_spec='wok@example.com\n'), + dict(addr_spec='wok@example.com\r\n') + ) + for kwargs in cases: + with self.subTest(kwargs=kwargs), self.assertRaisesRegex(ValueError, "invalid arguments"): + Address(**kwargs) + def test_non_ascii_username_in_addr_spec_raises(self): with self.assertRaises(ValueError): Address('foo', addr_spec='wők@example.com') diff --git a/Misc/NEWS.d/next/Security/2020-03-15-01-28-36.bpo-39073.6Szd3i.rst b/Misc/NEWS.d/next/Security/2020-03-15-01-28-36.bpo-39073.6Szd3i.rst new file mode 100644 index 00000000000..6c9447b897b --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-03-15-01-28-36.bpo-39073.6Szd3i.rst @@ -0,0 +1 @@ +Disallow CR or LF in email.headerregistry.Address arguments to guard against header injection attacks.