# Note: keep in sync with setuptools & pip
PYTHON3_VERSION_MAJOR:=3
PYTHON3_VERSION_MINOR:=7
-PYTHON3_VERSION_MICRO:=4
+PYTHON3_VERSION_MICRO:=5
PYTHON3_VERSION:=$(PYTHON3_VERSION_MAJOR).$(PYTHON3_VERSION_MINOR)
PYTHON3_SETUPTOOLS_PKG_RELEASE:=1
PYTHON3_PIP_PKG_RELEASE:=1
-PYTHON3_SETUPTOOLS_VERSION:=40.8.0
-PYTHON3_PIP_VERSION:=19.0.3
+PYTHON3_SETUPTOOLS_VERSION:=41.2.0
+PYTHON3_PIP_VERSION:=19.2.3
PYTHON_VERSION_MICRO:=$(PYTHON3_VERSION_MICRO)
PKG_NAME:=python3
-PKG_RELEASE:=4
+PKG_RELEASE:=1
PKG_VERSION:=$(PYTHON_VERSION).$(PYTHON_VERSION_MICRO)
PKG_SOURCE:=Python-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://www.python.org/ftp/python/$(PKG_VERSION)
-PKG_HASH:=fb799134b868199930b75f26678f18932214042639cd52b16da7fd134cd9b13f
+PKG_HASH:=e85a76ea9f3d6c485ec1780fca4e500725a4a7bbc63c78ebc44170de9b619d94
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>, Jeffery To <jeffery.to@gmail.com>
PKG_LICENSE:=Python/2.0
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
-@@ -1391,6 +1391,7 @@ libinstall: build_all $(srcdir)/Modules/
+@@ -1400,6 +1400,7 @@ libinstall: build_all $(srcdir)/Modules/
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py \
$(DESTDIR)$(LIBDEST); \
$(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt
if test -d $(DESTDIR)$(LIBDEST)/distutils/tests; then \
$(INSTALL_DATA) $(srcdir)/Modules/xxmodule.c \
$(DESTDIR)$(LIBDEST)/distutils/tests ; \
-@@ -1426,6 +1427,7 @@ libinstall: build_all $(srcdir)/Modules/
+@@ -1435,6 +1436,7 @@ libinstall: build_all $(srcdir)/Modules/
$(PYTHON_FOR_BUILD) -m lib2to3.pgen2.driver $(DESTDIR)$(LIBDEST)/lib2to3/Grammar.txt
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
$(PYTHON_FOR_BUILD) -m lib2to3.pgen2.driver $(DESTDIR)$(LIBDEST)/lib2to3/PatternGrammar.txt
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
-@@ -753,6 +753,16 @@ regen-all: regen-opcode regen-opcode-tar
+@@ -754,6 +754,16 @@ regen-all: regen-opcode regen-opcode-tar
############################################################################
# Special rules for object files
Modules/getbuildinfo.o: $(PARSER_OBJS) \
$(OBJECT_OBJS) \
$(PYTHON_OBJS) \
-@@ -760,6 +770,8 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
+@@ -761,6 +771,8 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \
$(MODOBJS) \
$(srcdir)/Modules/getbuildinfo.c
$(CC) -c $(PY_CORE_CFLAGS) \
--- a/configure
+++ b/configure
-@@ -15273,7 +15273,7 @@ $as_echo_n "checking ABIFLAGS... " >&6;
+@@ -15246,7 +15246,7 @@ $as_echo_n "checking ABIFLAGS... " >&6;
$as_echo "$ABIFLAGS" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5
$as_echo_n "checking SOABI... " >&6; }
--- a/configure.ac
+++ b/configure.ac
-@@ -4736,7 +4736,7 @@ AC_SUBST(SOABI)
+@@ -4739,7 +4739,7 @@ AC_SUBST(SOABI)
AC_MSG_CHECKING(ABIFLAGS)
AC_MSG_RESULT($ABIFLAGS)
AC_MSG_CHECKING(SOABI)
abi=sys.abiflags,
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
-@@ -1400,7 +1400,7 @@ libinstall: build_all $(srcdir)/Modules/
+@@ -1409,7 +1409,7 @@ libinstall: build_all $(srcdir)/Modules/
esac; \
done; \
done
$(DESTDIR)$(LIBDEST); \
$(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt
ifeq (@COMPILE_ALL_TESTS@,yes)
-@@ -1545,7 +1545,7 @@ sharedinstall: sharedmods
+@@ -1554,7 +1554,7 @@ sharedinstall: sharedmods
--install-scripts=$(BINDIR) \
--install-platlib=$(DESTSHARED) \
--root=$(DESTDIR)/
# Here are a couple of targets for MacOSX again, to install a full
--- a/configure
+++ b/configure
-@@ -15292,7 +15292,7 @@ LDVERSION='$(VERSION)$(ABIFLAGS)'
+@@ -15265,7 +15265,7 @@ LDVERSION='$(VERSION)$(ABIFLAGS)'
$as_echo "$LDVERSION" >&6; }
LIBPL='$(prefix)'"/lib/python${VERSION}/config-${LDVERSION}-${PLATFORM_TRIPLET}"
--- a/configure.ac
+++ b/configure.ac
-@@ -4753,7 +4753,7 @@ AC_MSG_RESULT($LDVERSION)
+@@ -4756,7 +4756,7 @@ AC_MSG_RESULT($LDVERSION)
dnl define LIBPL after ABIFLAGS and LDVERSION is defined.
AC_SUBST(PY_ENABLE_SHARED)
+++ /dev/null
---- a/Lib/lib2to3/refactor.py
-+++ b/Lib/lib2to3/refactor.py
-@@ -37,6 +37,12 @@ def get_all_fix_names(fixer_pkg, remove_
- if remove_prefix:
- name = name[4:]
- fix_names.append(name[:-3])
-+ if name.startswith("fix_") and name.endswith(".pyc"):
-+ if remove_prefix:
-+ name = name[4:]
-+ name = name[:-4]
-+ if name not in fix_names:
-+ fix_names.append(name)
- return fix_names
-
-
+++ /dev/null
-From 391511ccaaf0050970dfbe95bf2df1bcf6c33440 Mon Sep 17 00:00:00 2001
-From: "Miss Islington (bot)"
- <31488909+miss-islington@users.noreply.github.com>
-Date: Wed, 17 Jul 2019 10:02:05 -0700
-Subject: [PATCH] bpo-37461: Fix infinite loop in parsing of specially crafted
- email headers (GH-14794)
-
-* bpo-37461: Fix infinite loop in parsing of specially crafted email headers.
-
-Some crafted email header would cause the get_parameter method to run in an
-infinite loop causing a DoS attack surface when parsing those headers. This
-patch fixes that by making sure the DQUOTE character is handled to prevent
-going into an infinite loop.
-(cherry picked from commit a4a994bd3e619cbaff97610a1cee8ffa87c672f5)
-
-Co-authored-by: Abhilash Raj <maxking@users.noreply.github.com>
----
- Lib/email/_header_value_parser.py | 3 +++
- Lib/test/test_email/test__header_value_parser.py | 7 +++++++
- .../next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst | 2 ++
- 3 files changed, 12 insertions(+)
- create mode 100644 Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst
-
---- a/Lib/email/_header_value_parser.py
-+++ b/Lib/email/_header_value_parser.py
-@@ -2387,6 +2387,9 @@ def get_parameter(value):
- while value:
- if value[0] in WSP:
- token, value = get_fws(value)
-+ elif value[0] == '"':
-+ token = ValueTerminal('"', 'DQUOTE')
-+ value = value[1:]
- else:
- token, value = get_qcontent(value)
- v.append(token)
---- a/Lib/test/test_email/test__header_value_parser.py
-+++ b/Lib/test/test_email/test__header_value_parser.py
-@@ -2621,6 +2621,13 @@ class Test_parse_mime_parameters(TestPar
- # Defects are apparent missing *0*, and two 'out of sequence'.
- [errors.InvalidHeaderDefect]*3),
-
-+ # bpo-37461: Check that we don't go into an infinite loop.
-+ 'extra_dquote': (
-+ 'r*="\'a\'\\"',
-+ ' r="\\""',
-+ 'r*=\'a\'"',
-+ [('r', '"')],
-+ [errors.InvalidHeaderDefect]*2),
- }
-
- @parameterize
---- /dev/null
-+++ b/Misc/NEWS.d/next/Security/2019-07-16-08-11-00.bpo-37461.1Ahz7O.rst
-@@ -0,0 +1,2 @@
-+Fix an inifite loop when parsing specially crafted email headers. Patch by
-+Abhilash Raj.
+++ /dev/null
-From ea21389dda401457198fb214aa2c981a45ed9528 Mon Sep 17 00:00:00 2001
-From: Ashwin Ramaswami <aramaswamis@gmail.com>
-Date: Tue, 3 Sep 2019 09:42:53 -0700
-Subject: [PATCH] [3.7] bpo-37764: Fix infinite loop when parsing unstructured
- email headers. (GH-15239) (GH-15654)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-…aders. (GH-15239)
-
-Fixes a case in which email._header_value_parser.get_unstructured hangs the system for some invalid headers. This covers the cases in which the header contains either:
-- a case without trailing whitespace
-- an invalid encoded word
-
-https://bugs.python.org/issue37764
-
-This fix should also be backported to 3.7 and 3.8
-
-https://bugs.python.org/issue37764
-(cherry picked from commit c5b242f87f31286ad38991bc3868cf4cfbf2b681)
-
-Co-authored-by: Ashwin Ramaswami <aramaswamis@gmail.com>
-
-
-
-
-
-https://bugs.python.org/issue37764
----
- Lib/email/_header_value_parser.py | 19 ++++++++++++++---
- .../test_email/test__header_value_parser.py | 16 ++++++++++++++
- Lib/test/test_email/test_email.py | 21 +++++++++++++++++++
- Misc/ACKS | 1 +
- .../2019-08-27-01-13-05.bpo-37764.qv67PQ.rst | 1 +
- 5 files changed, 55 insertions(+), 3 deletions(-)
- create mode 100644 Misc/NEWS.d/next/Security/2019-08-27-01-13-05.bpo-37764.qv67PQ.rst
-
---- a/Lib/email/_header_value_parser.py
-+++ b/Lib/email/_header_value_parser.py
-@@ -931,6 +931,10 @@ class EWWhiteSpaceTerminal(WhiteSpaceTer
- return ''
-
-
-+class _InvalidEwError(errors.HeaderParseError):
-+ """Invalid encoded word found while parsing headers."""
-+
-+
- # XXX these need to become classes and used as instances so
- # that a program can't change them in a parse tree and screw
- # up other parse trees. Maybe should have tests for that, too.
-@@ -1035,7 +1039,10 @@ def get_encoded_word(value):
- raise errors.HeaderParseError(
- "expected encoded word but found {}".format(value))
- remstr = ''.join(remainder)
-- if len(remstr) > 1 and remstr[0] in hexdigits and remstr[1] in hexdigits:
-+ if (len(remstr) > 1 and
-+ remstr[0] in hexdigits and
-+ remstr[1] in hexdigits and
-+ tok.count('?') < 2):
- # The ? after the CTE was followed by an encoded word escape (=XX).
- rest, *remainder = remstr.split('?=', 1)
- tok = tok + '?=' + rest
-@@ -1047,7 +1054,7 @@ def get_encoded_word(value):
- try:
- text, charset, lang, defects = _ew.decode('=?' + tok + '?=')
- except ValueError:
-- raise errors.HeaderParseError(
-+ raise _InvalidEwError(
- "encoded word format invalid: '{}'".format(ew.cte))
- ew.charset = charset
- ew.lang = lang
-@@ -1097,9 +1104,12 @@ def get_unstructured(value):
- token, value = get_fws(value)
- unstructured.append(token)
- continue
-+ valid_ew = True
- if value.startswith('=?'):
- try:
- token, value = get_encoded_word(value)
-+ except _InvalidEwError:
-+ valid_ew = False
- except errors.HeaderParseError:
- # XXX: Need to figure out how to register defects when
- # appropriate here.
-@@ -1121,7 +1131,10 @@ def get_unstructured(value):
- # Split in the middle of an atom if there is a rfc2047 encoded word
- # which does not have WSP on both sides. The defect will be registered
- # the next time through the loop.
-- if rfc2047_matcher.search(tok):
-+ # This needs to only be performed when the encoded word is valid;
-+ # otherwise, performing it on an invalid encoded word can cause
-+ # the parser to go in an infinite loop.
-+ if valid_ew and rfc2047_matcher.search(tok):
- tok, *remainder = value.partition('=?')
- vtext = ValueTerminal(tok, 'vtext')
- _validate_xtext(vtext)
---- a/Lib/test/test_email/test__header_value_parser.py
-+++ b/Lib/test/test_email/test__header_value_parser.py
-@@ -383,6 +383,22 @@ class TestParser(TestParserMixin, TestEm
- [errors.InvalidHeaderDefect],
- '')
-
-+ def test_get_unstructured_without_trailing_whitespace_hang_case(self):
-+ self._test_get_x(self._get_unst,
-+ '=?utf-8?q?somevalue?=aa',
-+ 'somevalueaa',
-+ 'somevalueaa',
-+ [errors.InvalidHeaderDefect],
-+ '')
-+
-+ def test_get_unstructured_invalid_ew(self):
-+ self._test_get_x(self._get_unst,
-+ '=?utf-8?q?=somevalue?=',
-+ '=?utf-8?q?=somevalue?=',
-+ '=?utf-8?q?=somevalue?=',
-+ [],
-+ '')
-+
- # get_qp_ctext
-
- def test_get_qp_ctext_only(self):
---- a/Lib/test/test_email/test_email.py
-+++ b/Lib/test/test_email/test_email.py
-@@ -5367,6 +5367,27 @@ Content-Type: application/x-foo;
- eq(language, 'en-us')
- eq(s, 'My Document For You')
-
-+ def test_should_not_hang_on_invalid_ew_messages(self):
-+ messages = ["""From: user@host.com
-+To: user@host.com
-+Bad-Header:
-+ =?us-ascii?Q?LCSwrV11+IB0rSbSker+M9vWR7wEDSuGqmHD89Gt=ea0nJFSaiz4vX3XMJPT4vrE?=
-+ =?us-ascii?Q?xGUZeOnp0o22pLBB7CYLH74Js=wOlK6Tfru2U47qR?=
-+ =?us-ascii?Q?72OfyEY2p2=2FrA9xNFyvH+fBTCmazxwzF8nGkK6D?=
-+
-+Hello!
-+""", """From: ����� �������� <xxx@xxx>
-+To: "xxx" <xxx@xxx>
-+Subject: ��� ���������� ����� ����� � ��������� �� ����
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset="windows-1251";
-+Content-Transfer-Encoding: 8bit
-+
-+�� ����� � ���� ������ ��� ��������
-+"""]
-+ for m in messages:
-+ with self.subTest(m=m):
-+ msg = email.message_from_string(m)
-
-
- # Tests to ensure that signed parts of an email are completely preserved, as
---- a/Misc/ACKS
-+++ b/Misc/ACKS
-@@ -1305,6 +1305,7 @@ Burton Radons
- Abhilash Raj
- Shorya Raj
- Dhushyanth Ramasamy
-+Ashwin Ramaswami
- Jeff Ramnani
- Bayard Randel
- Varpu Rantala
---- /dev/null
-+++ b/Misc/NEWS.d/next/Security/2019-08-27-01-13-05.bpo-37764.qv67PQ.rst
-@@ -0,0 +1 @@
-+Fixes email._header_value_parser.get_unstructured going into an infinite loop for a specific case in which the email header does not have trailing whitespace, and the case in which it contains an invalid encoded word. Patch by Ashwin Ramaswami.
-\ No newline at end of file
+++ /dev/null
-From 39a0c7555530e31c6941a78da19b6a5b61170687 Mon Sep 17 00:00:00 2001
-From: "Miss Islington (bot)"
- <31488909+miss-islington@users.noreply.github.com>
-Date: Fri, 27 Sep 2019 13:18:14 -0700
-Subject: [PATCH] bpo-38243, xmlrpc.server: Escape the server_title (GH-16373)
-
-Escape the server title of xmlrpc.server.DocXMLRPCServer
-when rendering the document page as HTML.
-(cherry picked from commit e8650a4f8c7fb76f570d4ca9c1fbe44e91c8dfaa)
-
-Co-authored-by: Dong-hee Na <donghee.na92@gmail.com>
----
- Lib/test/test_docxmlrpc.py | 16 ++++++++++++++++
- Lib/xmlrpc/server.py | 3 ++-
- .../2019-09-25-13-21-09.bpo-38243.1pfz24.rst | 3 +++
- 3 files changed, 21 insertions(+), 1 deletion(-)
- create mode 100644 Misc/NEWS.d/next/Security/2019-09-25-13-21-09.bpo-38243.1pfz24.rst
-
---- a/Lib/test/test_docxmlrpc.py
-+++ b/Lib/test/test_docxmlrpc.py
-@@ -1,5 +1,6 @@
- from xmlrpc.server import DocXMLRPCServer
- import http.client
-+import re
- import sys
- import threading
- from test import support
-@@ -193,6 +194,21 @@ class DocXMLRPCHTTPGETServer(unittest.Te
- b'method_annotation</strong></a>(x: bytes)</dt></dl>'),
- response.read())
-
-+ def test_server_title_escape(self):
-+ # bpo-38243: Ensure that the server title and documentation
-+ # are escaped for HTML.
-+ self.serv.set_server_title('test_title<script>')
-+ self.serv.set_server_documentation('test_documentation<script>')
-+ self.assertEqual('test_title<script>', self.serv.server_title)
-+ self.assertEqual('test_documentation<script>',
-+ self.serv.server_documentation)
-+
-+ generated = self.serv.generate_html_documentation()
-+ title = re.search(r'<title>(.+?)</title>', generated).group()
-+ documentation = re.search(r'<p><tt>(.+?)</tt></p>', generated).group()
-+ self.assertEqual('<title>Python: test_title<script></title>', title)
-+ self.assertEqual('<p><tt>test_documentation<script></tt></p>', documentation)
-+
-
- if __name__ == '__main__':
- unittest.main()
---- a/Lib/xmlrpc/server.py
-+++ b/Lib/xmlrpc/server.py
-@@ -108,6 +108,7 @@ from xmlrpc.client import Fault, dumps,
- from http.server import BaseHTTPRequestHandler
- from functools import partial
- from inspect import signature
-+import html
- import http.server
- import socketserver
- import sys
-@@ -894,7 +895,7 @@ class XMLRPCDocGenerator:
- methods
- )
-
-- return documenter.page(self.server_title, documentation)
-+ return documenter.page(html.escape(self.server_title), documentation)
-
- class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
- """XML-RPC and documentation request handler class.
---- /dev/null
-+++ b/Misc/NEWS.d/next/Security/2019-09-25-13-21-09.bpo-38243.1pfz24.rst
-@@ -0,0 +1,3 @@
-+Escape the server title of :class:`xmlrpc.server.DocXMLRPCServer`
-+when rendering the document page as HTML.
-+(Contributed by Dong-hee Na in :issue:`38243`.)
+++ /dev/null
-From 77bb21f6e06aabc81d672dbdd6f8834c40544351 Mon Sep 17 00:00:00 2001
-From: jpic <jpic@users.noreply.github.com>
-Date: Wed, 17 Jul 2019 23:54:25 +0200
-Subject: [PATCH] bpo-34155: Dont parse domains containing @ (GH-13079)
-
-Before:
-
- >>> email.message_from_string('From: a@malicious.org@important.com', policy=email.policy.default)['from'].addresses
- (Address(display_name='', username='a', domain='malicious.org'),)
-
- >>> parseaddr('a@malicious.org@important.com')
- ('', 'a@malicious.org')
-
- After:
-
- >>> email.message_from_string('From: a@malicious.org@important.com', policy=email.policy.default)['from'].addresses
- (Address(display_name='', username='', domain=''),)
-
- >>> parseaddr('a@malicious.org@important.com')
- ('', 'a@')
-
-https://bugs.python.org/issue34155
-(cherry picked from commit 8cb65d1381b027f0b09ee36bfed7f35bb4dec9a9)
-
-Co-authored-by: jpic <jpic@users.noreply.github.com>
----
- Lib/email/_header_value_parser.py | 2 ++
- Lib/email/_parseaddr.py | 11 ++++++++++-
- Lib/test/test_email/test__header_value_parser.py | 10 ++++++++++
- Lib/test/test_email/test_email.py | 14 ++++++++++++++
- .../2019-05-04-13-33-37.bpo-34155.MJll68.rst | 1 +
- 5 files changed, 37 insertions(+), 1 deletion(-)
- create mode 100644 Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst
-
-diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
-index 801ae728dd136..c09f4f121ffb6 100644
---- a/Lib/email/_header_value_parser.py
-+++ b/Lib/email/_header_value_parser.py
-@@ -1585,6 +1585,8 @@ def get_domain(value):
- token, value = get_dot_atom(value)
- except errors.HeaderParseError:
- token, value = get_atom(value)
-+ if value and value[0] == '@':
-+ raise errors.HeaderParseError('Invalid Domain')
- if leader is not None:
- token[:0] = [leader]
- domain.append(token)
-diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py
-index cdfa3729adc79..41ff6f8c000d5 100644
---- a/Lib/email/_parseaddr.py
-+++ b/Lib/email/_parseaddr.py
-@@ -379,7 +379,12 @@ def getaddrspec(self):
- aslist.append('@')
- self.pos += 1
- self.gotonext()
-- return EMPTYSTRING.join(aslist) + self.getdomain()
-+ domain = self.getdomain()
-+ if not domain:
-+ # Invalid domain, return an empty address instead of returning a
-+ # local part to denote failed parsing.
-+ return EMPTYSTRING
-+ return EMPTYSTRING.join(aslist) + domain
-
- def getdomain(self):
- """Get the complete domain name from an address."""
-@@ -394,6 +399,10 @@ def getdomain(self):
- elif self.field[self.pos] == '.':
- self.pos += 1
- sdlist.append('.')
-+ elif self.field[self.pos] == '@':
-+ # bpo-34155: Don't parse domains with two `@` like
-+ # `a@malicious.org@important.com`.
-+ return EMPTYSTRING
- elif self.field[self.pos] in self.atomends:
- break
- else:
-diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py
-index 9e862feab10c9..0f19f8bcc2e0f 100644
---- a/Lib/test/test_email/test__header_value_parser.py
-+++ b/Lib/test/test_email/test__header_value_parser.py
-@@ -1448,6 +1448,16 @@ def test_get_addr_spec_dot_atom(self):
- self.assertEqual(addr_spec.domain, 'example.com')
- self.assertEqual(addr_spec.addr_spec, 'star.a.star@example.com')
-
-+ def test_get_addr_spec_multiple_domains(self):
-+ with self.assertRaises(errors.HeaderParseError):
-+ parser.get_addr_spec('star@a.star@example.com')
-+
-+ with self.assertRaises(errors.HeaderParseError):
-+ parser.get_addr_spec('star@a@example.com')
-+
-+ with self.assertRaises(errors.HeaderParseError):
-+ parser.get_addr_spec('star@172.17.0.1@example.com')
-+
- # get_obs_route
-
- def test_get_obs_route_simple(self):
-diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
-index c29cc56203b1f..aa775881c5521 100644
---- a/Lib/test/test_email/test_email.py
-+++ b/Lib/test/test_email/test_email.py
-@@ -3041,6 +3041,20 @@ def test_parseaddr_empty(self):
- self.assertEqual(utils.parseaddr('<>'), ('', ''))
- self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '')
-
-+ def test_parseaddr_multiple_domains(self):
-+ self.assertEqual(
-+ utils.parseaddr('a@b@c'),
-+ ('', '')
-+ )
-+ self.assertEqual(
-+ utils.parseaddr('a@b.c@c'),
-+ ('', '')
-+ )
-+ self.assertEqual(
-+ utils.parseaddr('a@172.17.0.1@c'),
-+ ('', '')
-+ )
-+
- def test_noquote_dump(self):
- self.assertEqual(
- utils.formataddr(('A Silly Person', 'person@dom.ain')),
-diff --git a/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst b/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst
-new file mode 100644
-index 0000000000000..50292e29ed1d2
---- /dev/null
-+++ b/Misc/NEWS.d/next/Security/2019-05-04-13-33-37.bpo-34155.MJll68.rst
-@@ -0,0 +1 @@
-+Fix parsing of invalid email addresses with more than one ``@`` (e.g. a@b@c.com.) to not return the part before 2nd ``@`` as valid email address. Patch by maxking & jpic.