GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

...
 
Commits (2)
* FIXES https://gitlab.torproject.org/tpo/anti-censorship/bridgedb/-/issues/33835
If a user sends a multipart email to our autoresponder, we now only parse
the part that has a text/plain encoding.
* FIXES https://bugs.torproject.org/33647
Fix broken link in the README and fix our Makefile's pylint target.
......
......@@ -50,6 +50,7 @@ Servers which interface with clients and distribute bridges over SMTP.
from __future__ import unicode_literals
import email.message
import email.policy
import logging
import io
import socket
......@@ -228,6 +229,20 @@ class SMTPMessage(object):
if not self.ignoring:
self.message = self.getIncomingMessage()
self.responder.reply()
# If a user sends a multipart email, we only consider the part whose
# content type is text/plain.
if self.message.is_multipart():
has_plaintext = False
for part in self.message.get_payload():
if part.get_content_type() != "text/plain":
continue
self.lines = part.get_payload().split("\n")
has_plaintext = True
if not has_plaintext:
logging.warning("User email had no text/plain content type.")
return defer.succeed(None)
def connectionLost(self):
......@@ -242,7 +257,8 @@ class SMTPMessage(object):
:returns: A ``Message`` comprised of all lines received thus far.
"""
return email.message_from_string('\n'.join(self.lines))
return email.message_from_string('\n'.join(self.lines),
policy=email.policy.compat32)
@implementer(smtp.IMessageDelivery)
......
......@@ -100,6 +100,69 @@ class SMTPMessageTests(unittest.TestCase):
self.assertIsInstance(self.message.getIncomingMessage(),
email.message.Message)
def test_SMTPMessage_multipart1(self):
"""`eomReceived` should get rid of HTML multiparts."""
# Gmail's web interface would send a message like this when replying to
# one of BridgeDB's emails.
multipartEmail = [
'MIME-Version: 1.0',
'Date: Mon, 1 Jun 2020 15:55:33 -0700',
'Subject: Re: test',
'From: Foo Bar <foo@bar.com>',
'To: bridges@bridges.torproject.org',
'Content-Type: multipart/alternative; boundary="00000000000041b34105a70db186"',
'',
'--00000000000041b34105a70db186',
'Content-Type: text/plain; charset="UTF-8"',
'',
'This is plaintext.',
'',
'--00000000000041b34105a70db186',
'Content-Type: text/html; charset="UTF-8"',
'Content-Transfer-Encoding: quoted-printable',
'',
'This is HTML.',
'',
'--00000000000041b34105a70db186--']
for line in multipartEmail:
self.message.lineReceived(line)
self.message.eomReceived()
content = "".join(self.message.lines)
self.assertTrue("This is plaintext." in content)
self.assertFalse("This is HTML." in content)
def test_SMTPMessage_multipart2(self):
"""`eomReceived` should get rid of HTML multiparts."""
# Outlook would send a message like this when replying to one of
# BridgeDB's emails.
multipartEmail = [
'Content-Type: multipart/alternative;',
' boundary="_000_VI1PR08MB4351A21D6C4A31C2B0FDA34F8CC10VI1PR08MB4351eurp_"',
'',
'--_000_VI1PR08MB4351A21D6C4A31C2B0FDA34F8CC10VI1PR08MB4351eurp_',
'Content-Type: text/plain; charset="us-ascii"',
'Content-Transfer-Encoding: quoted-printable',
'',
'This is plaintext.',
'',
'--_000_VI1PR08MB4351A21D6C4A31C2B0FDA34F8CC10VI1PR08MB4351eurp_',
'Content-Type: text/html; charset="us-ascii"',
'Content-Transfer-Encoding: quoted-printable',
'',
'This is HTML.',
'',
'--_000_VI1PR08MB4351A21D6C4A31C2B0FDA34F8CC10VI1PR08MB4351eurp_--']
for line in multipartEmail:
self.message.lineReceived(line)
self.message.eomReceived()
content = "".join(self.message.lines)
self.assertTrue("This is plaintext." in content)
self.assertFalse("This is HTML." in content)
class SMTPIncomingDeliveryTests(unittest.TestCase):
"""Unittests for :class:`email.server.SMTPIncomingDelivery`."""
......