Skip to content

Commit 976841c

Browse files
nillebvadmiumIvo Bellin Salarin
authored andcommitted
gh-68552: fix defects policy (GH-138579)
Extend defect handling via policy to a couple of missed defects. --------- (cherry picked from commit 9d707d8) Co-authored-by: Ivo Bellin Salarin <[email protected]> Co-authored-by: Martin Panter <[email protected]> Co-authored-by: Ivo Bellin Salarin <[email protected]>
1 parent 90ca216 commit 976841c

File tree

4 files changed

+48
-94
lines changed

4 files changed

+48
-94
lines changed

Lib/email/feedparser.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -504,10 +504,9 @@ def _parse_headers(self, lines):
504504
self._input.unreadline(line)
505505
return
506506
else:
507-
# Weirdly placed unix-from line. Note this as a defect
508-
# and ignore it.
507+
# Weirdly placed unix-from line.
509508
defect = errors.MisplacedEnvelopeHeaderDefect(line)
510-
self._cur.defects.append(defect)
509+
self.policy.handle_defect(self._cur, defect)
511510
continue
512511
# Split the line on the colon separating field name from value.
513512
# There will always be a colon, because if there wasn't the part of
@@ -519,7 +518,7 @@ def _parse_headers(self, lines):
519518
# message. Track the error but keep going.
520519
if i == 0:
521520
defect = errors.InvalidHeaderDefect("Missing header name.")
522-
self._cur.defects.append(defect)
521+
self.policy.handle_defect(self._cur, defect)
523522
continue
524523

525524
assert i>0, "_parse_headers fed line with no : and no leading WS"

Lib/test/test_email/test_defect_handling.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,10 @@ def test_multipart_invalid_cte(self):
126126
errors.InvalidMultipartContentTransferEncodingDefect)
127127

128128
def test_multipart_no_cte_no_defect(self):
129-
if self.raise_expected: return
130129
msg = self._str_msg(self.multipart_msg.format(''))
131130
self.assertEqual(len(self.get_defects(msg)), 0)
132131

133132
def test_multipart_valid_cte_no_defect(self):
134-
if self.raise_expected: return
135133
for cte in ('7bit', '8bit', 'BINary'):
136134
msg = self._str_msg(
137135
self.multipart_msg.format("\nContent-Transfer-Encoding: "+cte))
@@ -300,6 +298,47 @@ def test_missing_ending_boundary(self):
300298
self.assertDefectsEqual(self.get_defects(msg),
301299
[errors.CloseBoundaryNotFoundDefect])
302300

301+
def test_line_beginning_colon(self):
302+
string = (
303+
"Subject: Dummy subject\r\n: faulty header line\r\n\r\nbody\r\n"
304+
)
305+
306+
with self._raise_point(errors.InvalidHeaderDefect):
307+
msg = self._str_msg(string)
308+
self.assertEqual(len(self.get_defects(msg)), 1)
309+
self.assertDefectsEqual(
310+
self.get_defects(msg), [errors.InvalidHeaderDefect]
311+
)
312+
313+
if msg:
314+
self.assertEqual(msg.items(), [("Subject", "Dummy subject")])
315+
self.assertEqual(msg.get_payload(), "body\r\n")
316+
317+
def test_misplaced_envelope(self):
318+
string = (
319+
"Subject: Dummy subject\r\nFrom wtf\r\nTo: abc\r\n\r\nbody\r\n"
320+
)
321+
with self._raise_point(errors.MisplacedEnvelopeHeaderDefect):
322+
msg = self._str_msg(string)
323+
self.assertEqual(len(self.get_defects(msg)), 1)
324+
self.assertDefectsEqual(
325+
self.get_defects(msg), [errors.MisplacedEnvelopeHeaderDefect]
326+
)
327+
328+
if msg:
329+
headers = [("Subject", "Dummy subject"), ("To", "abc")]
330+
self.assertEqual(msg.items(), headers)
331+
self.assertEqual(msg.get_payload(), "body\r\n")
332+
333+
334+
335+
class TestCompat32(TestDefectsBase, TestEmailBase):
336+
337+
policy = policy.compat32
338+
339+
def get_defects(self, obj):
340+
return obj.defects
341+
303342

304343
class TestDefectDetection(TestDefectsBase, TestEmailBase):
305344

@@ -332,6 +371,9 @@ def _raise_point(self, defect):
332371
with self.assertRaises(defect):
333372
yield
334373

374+
def get_defects(self, obj):
375+
return obj.defects
376+
335377

336378
if __name__ == '__main__':
337379
unittest.main()

Lib/test/test_email/test_email.py

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,70 +2237,6 @@ def test_parse_missing_minor_type(self):
22372237
eq(msg.get_content_maintype(), 'text')
22382238
eq(msg.get_content_subtype(), 'plain')
22392239

2240-
# test_defect_handling
2241-
def test_same_boundary_inner_outer(self):
2242-
msg = self._msgobj('msg_15.txt')
2243-
# XXX We can probably eventually do better
2244-
inner = msg.get_payload(0)
2245-
self.assertHasAttr(inner, 'defects')
2246-
self.assertEqual(len(inner.defects), 1)
2247-
self.assertIsInstance(inner.defects[0],
2248-
errors.StartBoundaryNotFoundDefect)
2249-
2250-
# test_defect_handling
2251-
def test_multipart_no_boundary(self):
2252-
msg = self._msgobj('msg_25.txt')
2253-
self.assertIsInstance(msg.get_payload(), str)
2254-
self.assertEqual(len(msg.defects), 2)
2255-
self.assertIsInstance(msg.defects[0],
2256-
errors.NoBoundaryInMultipartDefect)
2257-
self.assertIsInstance(msg.defects[1],
2258-
errors.MultipartInvariantViolationDefect)
2259-
2260-
multipart_msg = textwrap.dedent("""\
2261-
Date: Wed, 14 Nov 2007 12:56:23 GMT
2262-
2263-
2264-
Subject: Content-Transfer-Encoding: base64 and multipart
2265-
MIME-Version: 1.0
2266-
Content-Type: multipart/mixed;
2267-
boundary="===============3344438784458119861=="{}
2268-
2269-
--===============3344438784458119861==
2270-
Content-Type: text/plain
2271-
2272-
Test message
2273-
2274-
--===============3344438784458119861==
2275-
Content-Type: application/octet-stream
2276-
Content-Transfer-Encoding: base64
2277-
2278-
YWJj
2279-
2280-
--===============3344438784458119861==--
2281-
""")
2282-
2283-
# test_defect_handling
2284-
def test_multipart_invalid_cte(self):
2285-
msg = self._str_msg(
2286-
self.multipart_msg.format("\nContent-Transfer-Encoding: base64"))
2287-
self.assertEqual(len(msg.defects), 1)
2288-
self.assertIsInstance(msg.defects[0],
2289-
errors.InvalidMultipartContentTransferEncodingDefect)
2290-
2291-
# test_defect_handling
2292-
def test_multipart_no_cte_no_defect(self):
2293-
msg = self._str_msg(self.multipart_msg.format(''))
2294-
self.assertEqual(len(msg.defects), 0)
2295-
2296-
# test_defect_handling
2297-
def test_multipart_valid_cte_no_defect(self):
2298-
for cte in ('7bit', '8bit', 'BINary'):
2299-
msg = self._str_msg(
2300-
self.multipart_msg.format(
2301-
"\nContent-Transfer-Encoding: {}".format(cte)))
2302-
self.assertEqual(len(msg.defects), 0)
2303-
23042240
# test_headerregistry.TestContentTypeHeader invalid_1 and invalid_2.
23052241
def test_invalid_content_type(self):
23062242
eq = self.assertEqual
@@ -2377,30 +2313,6 @@ def test_missing_start_boundary(self):
23772313
self.assertIsInstance(bad.defects[0],
23782314
errors.StartBoundaryNotFoundDefect)
23792315

2380-
# test_defect_handling
2381-
def test_first_line_is_continuation_header(self):
2382-
eq = self.assertEqual
2383-
m = ' Line 1\nSubject: test\n\nbody'
2384-
msg = email.message_from_string(m)
2385-
eq(msg.keys(), ['Subject'])
2386-
eq(msg.get_payload(), 'body')
2387-
eq(len(msg.defects), 1)
2388-
self.assertDefectsEqual(msg.defects,
2389-
[errors.FirstHeaderLineIsContinuationDefect])
2390-
eq(msg.defects[0].line, ' Line 1\n')
2391-
2392-
# test_defect_handling
2393-
def test_missing_header_body_separator(self):
2394-
# Our heuristic if we see a line that doesn't look like a header (no
2395-
# leading whitespace but no ':') is to assume that the blank line that
2396-
# separates the header from the body is missing, and to stop parsing
2397-
# headers and start parsing the body.
2398-
msg = self._str_msg('Subject: test\nnot a header\nTo: abc\n\nb\n')
2399-
self.assertEqual(msg.keys(), ['Subject'])
2400-
self.assertEqual(msg.get_payload(), 'not a header\nTo: abc\n\nb\n')
2401-
self.assertDefectsEqual(msg.defects,
2402-
[errors.MissingHeaderBodySeparatorDefect])
2403-
24042316
def test_string_payload_with_extra_space_after_cte(self):
24052317
# https://github.com/python/cpython/issues/98188
24062318
cte = "base64 "
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``MisplacedEnvelopeHeaderDefect`` and ``Missing header name`` defects are now correctly passed to the ``handle_defect`` method of ``policy`` in :class:`~email.parser.FeedParser`.

0 commit comments

Comments
 (0)