ยปCore Development>Code coverage>Lib/test/test_email/test_defect_handling.py

Python code coverage for Lib/test/test_email/test_defect_handling.py

#countcontent
1n/aimport textwrap
2n/aimport unittest
3n/aimport contextlib
4n/afrom email import policy
5n/afrom email import errors
6n/afrom test.test_email import TestEmailBase
7n/a
8n/a
9n/aclass TestDefectsBase:
10n/a
11n/a policy = policy.default
12n/a raise_expected = False
13n/a
14n/a @contextlib.contextmanager
15n/a def _raise_point(self, defect):
16n/a yield
17n/a
18n/a def test_same_boundary_inner_outer(self):
19n/a source = textwrap.dedent("""\
20n/a Subject: XX
21n/a From: xx@xx.dk
22n/a To: XX
23n/a Mime-version: 1.0
24n/a Content-type: multipart/mixed;
25n/a boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
26n/a
27n/a --MS_Mac_OE_3071477847_720252_MIME_Part
28n/a Content-type: multipart/alternative;
29n/a boundary="MS_Mac_OE_3071477847_720252_MIME_Part"
30n/a
31n/a --MS_Mac_OE_3071477847_720252_MIME_Part
32n/a Content-type: text/plain; charset="ISO-8859-1"
33n/a Content-transfer-encoding: quoted-printable
34n/a
35n/a text
36n/a
37n/a --MS_Mac_OE_3071477847_720252_MIME_Part
38n/a Content-type: text/html; charset="ISO-8859-1"
39n/a Content-transfer-encoding: quoted-printable
40n/a
41n/a <HTML></HTML>
42n/a
43n/a --MS_Mac_OE_3071477847_720252_MIME_Part--
44n/a
45n/a --MS_Mac_OE_3071477847_720252_MIME_Part
46n/a Content-type: image/gif; name="xx.gif";
47n/a Content-disposition: attachment
48n/a Content-transfer-encoding: base64
49n/a
50n/a Some removed base64 encoded chars.
51n/a
52n/a --MS_Mac_OE_3071477847_720252_MIME_Part--
53n/a
54n/a """)
55n/a # XXX better would be to actually detect the duplicate.
56n/a with self._raise_point(errors.StartBoundaryNotFoundDefect):
57n/a msg = self._str_msg(source)
58n/a if self.raise_expected: return
59n/a inner = msg.get_payload(0)
60n/a self.assertTrue(hasattr(inner, 'defects'))
61n/a self.assertEqual(len(self.get_defects(inner)), 1)
62n/a self.assertIsInstance(self.get_defects(inner)[0],
63n/a errors.StartBoundaryNotFoundDefect)
64n/a
65n/a def test_multipart_no_boundary(self):
66n/a source = textwrap.dedent("""\
67n/a Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800)
68n/a From: foobar
69n/a Subject: broken mail
70n/a MIME-Version: 1.0
71n/a Content-Type: multipart/report; report-type=delivery-status;
72n/a
73n/a --JAB03225.986577786/zinfandel.lacita.com
74n/a
75n/a One part
76n/a
77n/a --JAB03225.986577786/zinfandel.lacita.com
78n/a Content-Type: message/delivery-status
79n/a
80n/a Header: Another part
81n/a
82n/a --JAB03225.986577786/zinfandel.lacita.com--
83n/a """)
84n/a with self._raise_point(errors.NoBoundaryInMultipartDefect):
85n/a msg = self._str_msg(source)
86n/a if self.raise_expected: return
87n/a self.assertIsInstance(msg.get_payload(), str)
88n/a self.assertEqual(len(self.get_defects(msg)), 2)
89n/a self.assertIsInstance(self.get_defects(msg)[0],
90n/a errors.NoBoundaryInMultipartDefect)
91n/a self.assertIsInstance(self.get_defects(msg)[1],
92n/a errors.MultipartInvariantViolationDefect)
93n/a
94n/a multipart_msg = textwrap.dedent("""\
95n/a Date: Wed, 14 Nov 2007 12:56:23 GMT
96n/a From: foo@bar.invalid
97n/a To: foo@bar.invalid
98n/a Subject: Content-Transfer-Encoding: base64 and multipart
99n/a MIME-Version: 1.0
100n/a Content-Type: multipart/mixed;
101n/a boundary="===============3344438784458119861=="{}
102n/a
103n/a --===============3344438784458119861==
104n/a Content-Type: text/plain
105n/a
106n/a Test message
107n/a
108n/a --===============3344438784458119861==
109n/a Content-Type: application/octet-stream
110n/a Content-Transfer-Encoding: base64
111n/a
112n/a YWJj
113n/a
114n/a --===============3344438784458119861==--
115n/a """)
116n/a
117n/a def test_multipart_invalid_cte(self):
118n/a with self._raise_point(
119n/a errors.InvalidMultipartContentTransferEncodingDefect):
120n/a msg = self._str_msg(
121n/a self.multipart_msg.format(
122n/a "\nContent-Transfer-Encoding: base64"))
123n/a if self.raise_expected: return
124n/a self.assertEqual(len(self.get_defects(msg)), 1)
125n/a self.assertIsInstance(self.get_defects(msg)[0],
126n/a errors.InvalidMultipartContentTransferEncodingDefect)
127n/a
128n/a def test_multipart_no_cte_no_defect(self):
129n/a if self.raise_expected: return
130n/a msg = self._str_msg(self.multipart_msg.format(''))
131n/a self.assertEqual(len(self.get_defects(msg)), 0)
132n/a
133n/a def test_multipart_valid_cte_no_defect(self):
134n/a if self.raise_expected: return
135n/a for cte in ('7bit', '8bit', 'BINary'):
136n/a msg = self._str_msg(
137n/a self.multipart_msg.format("\nContent-Transfer-Encoding: "+cte))
138n/a self.assertEqual(len(self.get_defects(msg)), 0, "cte="+cte)
139n/a
140n/a def test_lying_multipart(self):
141n/a source = textwrap.dedent("""\
142n/a From: "Allison Dunlap" <xxx@example.com>
143n/a To: yyy@example.com
144n/a Subject: 64423
145n/a Date: Sun, 11 Jul 2004 16:09:27 -0300
146n/a MIME-Version: 1.0
147n/a Content-Type: multipart/alternative;
148n/a
149n/a Blah blah blah
150n/a """)
151n/a with self._raise_point(errors.NoBoundaryInMultipartDefect):
152n/a msg = self._str_msg(source)
153n/a if self.raise_expected: return
154n/a self.assertTrue(hasattr(msg, 'defects'))
155n/a self.assertEqual(len(self.get_defects(msg)), 2)
156n/a self.assertIsInstance(self.get_defects(msg)[0],
157n/a errors.NoBoundaryInMultipartDefect)
158n/a self.assertIsInstance(self.get_defects(msg)[1],
159n/a errors.MultipartInvariantViolationDefect)
160n/a
161n/a def test_missing_start_boundary(self):
162n/a source = textwrap.dedent("""\
163n/a Content-Type: multipart/mixed; boundary="AAA"
164n/a From: Mail Delivery Subsystem <xxx@example.com>
165n/a To: yyy@example.com
166n/a
167n/a --AAA
168n/a
169n/a Stuff
170n/a
171n/a --AAA
172n/a Content-Type: message/rfc822
173n/a
174n/a From: webmaster@python.org
175n/a To: zzz@example.com
176n/a Content-Type: multipart/mixed; boundary="BBB"
177n/a
178n/a --BBB--
179n/a
180n/a --AAA--
181n/a
182n/a """)
183n/a # The message structure is:
184n/a #
185n/a # multipart/mixed
186n/a # text/plain
187n/a # message/rfc822
188n/a # multipart/mixed [*]
189n/a #
190n/a # [*] This message is missing its start boundary
191n/a with self._raise_point(errors.StartBoundaryNotFoundDefect):
192n/a outer = self._str_msg(source)
193n/a if self.raise_expected: return
194n/a bad = outer.get_payload(1).get_payload(0)
195n/a self.assertEqual(len(self.get_defects(bad)), 1)
196n/a self.assertIsInstance(self.get_defects(bad)[0],
197n/a errors.StartBoundaryNotFoundDefect)
198n/a
199n/a def test_first_line_is_continuation_header(self):
200n/a with self._raise_point(errors.FirstHeaderLineIsContinuationDefect):
201n/a msg = self._str_msg(' Line 1\nSubject: test\n\nbody')
202n/a if self.raise_expected: return
203n/a self.assertEqual(msg.keys(), ['Subject'])
204n/a self.assertEqual(msg.get_payload(), 'body')
205n/a self.assertEqual(len(self.get_defects(msg)), 1)
206n/a self.assertDefectsEqual(self.get_defects(msg),
207n/a [errors.FirstHeaderLineIsContinuationDefect])
208n/a self.assertEqual(self.get_defects(msg)[0].line, ' Line 1\n')
209n/a
210n/a def test_missing_header_body_separator(self):
211n/a # Our heuristic if we see a line that doesn't look like a header (no
212n/a # leading whitespace but no ':') is to assume that the blank line that
213n/a # separates the header from the body is missing, and to stop parsing
214n/a # headers and start parsing the body.
215n/a with self._raise_point(errors.MissingHeaderBodySeparatorDefect):
216n/a msg = self._str_msg('Subject: test\nnot a header\nTo: abc\n\nb\n')
217n/a if self.raise_expected: return
218n/a self.assertEqual(msg.keys(), ['Subject'])
219n/a self.assertEqual(msg.get_payload(), 'not a header\nTo: abc\n\nb\n')
220n/a self.assertDefectsEqual(self.get_defects(msg),
221n/a [errors.MissingHeaderBodySeparatorDefect])
222n/a
223n/a def test_bad_padding_in_base64_payload(self):
224n/a source = textwrap.dedent("""\
225n/a Subject: test
226n/a MIME-Version: 1.0
227n/a Content-Type: text/plain; charset="utf-8"
228n/a Content-Transfer-Encoding: base64
229n/a
230n/a dmk
231n/a """)
232n/a msg = self._str_msg(source)
233n/a with self._raise_point(errors.InvalidBase64PaddingDefect):
234n/a payload = msg.get_payload(decode=True)
235n/a if self.raise_expected: return
236n/a self.assertEqual(payload, b'vi')
237n/a self.assertDefectsEqual(self.get_defects(msg),
238n/a [errors.InvalidBase64PaddingDefect])
239n/a
240n/a def test_invalid_chars_in_base64_payload(self):
241n/a source = textwrap.dedent("""\
242n/a Subject: test
243n/a MIME-Version: 1.0
244n/a Content-Type: text/plain; charset="utf-8"
245n/a Content-Transfer-Encoding: base64
246n/a
247n/a dm\x01k===
248n/a """)
249n/a msg = self._str_msg(source)
250n/a with self._raise_point(errors.InvalidBase64CharactersDefect):
251n/a payload = msg.get_payload(decode=True)
252n/a if self.raise_expected: return
253n/a self.assertEqual(payload, b'vi')
254n/a self.assertDefectsEqual(self.get_defects(msg),
255n/a [errors.InvalidBase64CharactersDefect])
256n/a
257n/a def test_missing_ending_boundary(self):
258n/a source = textwrap.dedent("""\
259n/a To: 1@harrydomain4.com
260n/a Subject: Fwd: 1
261n/a MIME-Version: 1.0
262n/a Content-Type: multipart/alternative;
263n/a boundary="------------000101020201080900040301"
264n/a
265n/a --------------000101020201080900040301
266n/a Content-Type: text/plain; charset=ISO-8859-1
267n/a Content-Transfer-Encoding: 7bit
268n/a
269n/a Alternative 1
270n/a
271n/a --------------000101020201080900040301
272n/a Content-Type: text/html; charset=ISO-8859-1
273n/a Content-Transfer-Encoding: 7bit
274n/a
275n/a Alternative 2
276n/a
277n/a """)
278n/a with self._raise_point(errors.CloseBoundaryNotFoundDefect):
279n/a msg = self._str_msg(source)
280n/a if self.raise_expected: return
281n/a self.assertEqual(len(msg.get_payload()), 2)
282n/a self.assertEqual(msg.get_payload(1).get_payload(), 'Alternative 2\n')
283n/a self.assertDefectsEqual(self.get_defects(msg),
284n/a [errors.CloseBoundaryNotFoundDefect])
285n/a
286n/a
287n/aclass TestDefectDetection(TestDefectsBase, TestEmailBase):
288n/a
289n/a def get_defects(self, obj):
290n/a return obj.defects
291n/a
292n/a
293n/aclass TestDefectCapture(TestDefectsBase, TestEmailBase):
294n/a
295n/a class CapturePolicy(policy.EmailPolicy):
296n/a captured = None
297n/a def register_defect(self, obj, defect):
298n/a self.captured.append(defect)
299n/a
300n/a def setUp(self):
301n/a self.policy = self.CapturePolicy(captured=list())
302n/a
303n/a def get_defects(self, obj):
304n/a return self.policy.captured
305n/a
306n/a
307n/aclass TestDefectRaising(TestDefectsBase, TestEmailBase):
308n/a
309n/a policy = TestDefectsBase.policy
310n/a policy = policy.clone(raise_on_defect=True)
311n/a raise_expected = True
312n/a
313n/a @contextlib.contextmanager
314n/a def _raise_point(self, defect):
315n/a with self.assertRaises(defect):
316n/a yield
317n/a
318n/a
319n/aif __name__ == '__main__':
320n/a unittest.main()