Abhilash Raj pushed to branch master at GNU Mailman / Mailman Core

Commits:
53d9d0ab by Mark Sapiro at 2017-11-11T13:54:27-08:00
Defend against certain malformed messages.

Accommodate https://bugs.python.org/issue27321 by overriding as_string() in
mailman.email.message.Message to catch the KeyError, get the message
as_bytes() and convert it to a string.  This obviates !197 as a fix for #256
so that is reverted.

Also fix mailman.mta.connection.Connection.sendmail to ensure msgtext is
pure ascii.

- - - - -
066832bb by Abhilash Raj at 2017-12-14T22:13:58+00:00
Merge branch 'as_string' into 'master'

Defend against certain malformed messages.

Closes #406 and #256

See merge request mailman/mailman!322
- - - - -


8 changed files:

- src/mailman/docs/NEWS.rst
- src/mailman/email/message.py
- src/mailman/rest/tests/data/__init__.py → 
src/mailman/email/tests/data/__init__.py
- src/mailman/rest/tests/data/bad_email.eml → 
src/mailman/email/tests/data/bad_email.eml
- src/mailman/email/tests/test_message.py
- src/mailman/mta/connection.py
- src/mailman/rest/post_moderation.py
- src/mailman/rest/tests/test_moderation.py


Changes:

=====================================
src/mailman/docs/NEWS.rst
=====================================
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -41,6 +41,9 @@ Bugs
 * The ``admin`` subaddress, a synonym for ``bounces`` and deprecated since
   Mailman 2.1, has been removed.  (Closes #435)
 * Better support for changing the ``list_name`` property.  (Closes #428)
+* Raw Message text is now coerced to pure ascii before sending, and
+  https://bugs.python.org/issue27321 is now defended against by overriding
+  Message.as_string() to cover more cases than before.  (Closes #406)
 
 Command line
 ------------


=====================================
src/mailman/email/message.py
=====================================
--- a/src/mailman/email/message.py
+++ b/src/mailman/email/message.py
@@ -47,6 +47,15 @@ class Message(email.message.Message):
     def __setstate__(self, values):
         self.__dict__ = values
 
+    def as_string(self):
+        # Work around for https://bugs.python.org/issue27321.
+        try:
+            value = email.message.Message.as_string(self)
+        except KeyError:
+            value = email.message.Message.as_bytes(self).decode(
+                'ascii', 'replace')
+        return value
+
     @property
     def sender(self):
         """The address considered to be the author of the email.


=====================================
src/mailman/rest/tests/data/__init__.py → 
src/mailman/email/tests/data/__init__.py
=====================================


=====================================
src/mailman/rest/tests/data/bad_email.eml → 
src/mailman/email/tests/data/bad_email.eml
=====================================


=====================================
src/mailman/email/tests/test_message.py
=====================================
--- a/src/mailman/email/tests/test_message.py
+++ b/src/mailman/email/tests/test_message.py
@@ -19,12 +19,14 @@
 
 import unittest
 
+from email import message_from_binary_file
 from email.header import Header
 from email.parser import FeedParser
 from mailman.app.lifecycle import create_list
 from mailman.email.message import Message, UserNotification
 from mailman.testing.helpers import get_queue_messages
 from mailman.testing.layers import ConfigLayer
+from pkg_resources import resource_filename
 
 
 class TestMessage(unittest.TestCase):
@@ -88,3 +90,12 @@ Test content
         msg['From'] = Header('t...@example.com')
         # Make sure the senders property does not fail
         self.assertEqual(msg.senders, ['t...@example.com'])
+
+    def test_as_string_python_bug_27321(self):
+        email_path = resource_filename(
+            'mailman.email.tests.data', 'bad_email.eml')
+        with open(email_path, 'rb') as fp:
+            msg = message_from_binary_file(fp, Message)
+            fp.seek(0)
+            text = fp.read().decode('ascii', 'replace')
+        self.assertEqual(msg.as_string(), text)


=====================================
src/mailman/mta/connection.py
=====================================
--- a/src/mailman/mta/connection.py
+++ b/src/mailman/mta/connection.py
@@ -78,6 +78,10 @@ class Connection:
             recipients = [config.devmode.recipient] * len(recipients)
         if self._connection is None:
             self._connect()
+        # smtplib.SMTP.sendmail requires the message string to be pure ascii.
+        # We have seen malformed messages with non-ascii unicodes, so ensure
+        # we have pure ascii.
+        msgtext = msgtext.encode('ascii', 'replace').decode('ascii')
         try:
             log.debug('envsender: %s, recipients: %s, size(msgtext): %s',
                       envsender, recipients, len(msgtext))


=====================================
src/mailman/rest/post_moderation.py
=====================================
--- a/src/mailman/rest/post_moderation.py
+++ b/src/mailman/rest/post_moderation.py
@@ -74,14 +74,7 @@ class _HeldMessageBase(_ModerationBase):
         # resource.  XXX See LP: #967954
         key = resource.pop('key')
         msg = getUtility(IMessageStore).get_message_by_id(key)
-        try:
-            resource['msg'] = msg.as_string()
-        except KeyError:
-            # If the message can't be parsed, return a generic message instead
-            # of raising an error.
-            #
-            # See http://bugs.python.org/issue27321 and GL#256
-            resource['msg'] = 'This message is defective'
+        resource['msg'] = msg.as_string()
         # Some of the _mod_* keys we want to rename and place into the JSON
         # resource.  Others we can drop.  Since we're mutating the dictionary,
         # we need to make a copy of the keys.  When you port this to Python 3,


=====================================
src/mailman/rest/tests/test_moderation.py
=====================================
--- a/src/mailman/rest/tests/test_moderation.py
+++ b/src/mailman/rest/tests/test_moderation.py
@@ -19,7 +19,6 @@
 
 import unittest
 
-from email import message_from_binary_file
 from mailman.app.lifecycle import create_list
 from mailman.app.moderator import hold_message
 from mailman.database.transaction import transaction
@@ -32,7 +31,6 @@ from mailman.testing.helpers import (
     call_api, get_queue_messages, set_preferred,
     specialized_message_from_string as mfs)
 from mailman.testing.layers import RESTLayer
-from pkg_resources import resource_filename
 from urllib.error import HTTPError
 from zope.component import getUtility
 
@@ -227,24 +225,6 @@ class TestSubscriptionModeration(unittest.TestCase):
         emails = set(entry['email'] for entry in json['entries'])
         self.assertEqual(emails, {'a...@example.com', 'b...@example.com'})
 
-    def test_view_malformed_held_message(self):
-        # Opening a bad (i.e. bad structure) email and holding it.
-        email_path = resource_filename(
-            'mailman.rest.tests.data', 'bad_email.eml')
-        with open(email_path, 'rb') as fp:
-            msg = message_from_binary_file(fp)
-        msg.sender = 'aper...@example.com'
-        with transaction():
-            hold_message(self._mlist, msg)
-        # Now trying to access held messages from REST API should not give
-        # 500 server error if one of the messages can't be parsed properly.
-        json, response = call_api(
-            'http://localhost:9001/3.0/lists/a...@example.com/held')
-        self.assertEqual(response.status_code, 200)
-        self.assertEqual(len(json['entries']), 1)
-        self.assertEqual(json['entries'][0]['msg'],
-                         'This message is defective')
-
     def test_individual_request(self):
         # We can view an individual request.
         with transaction():



View it on GitLab: 
https://gitlab.com/mailman/mailman/compare/06764cdd23f11539ac47b21b6b066b1ca40ababb...066832bb23b744d84710aa35c33908e36021f7ce

---
View it on GitLab: 
https://gitlab.com/mailman/mailman/compare/06764cdd23f11539ac47b21b6b066b1ca40ababb...066832bb23b744d84710aa35c33908e36021f7ce
You're receiving this email because of your account on gitlab.com.
_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to