From b89e2bb0b3657aa830907ae57c86ba0487d06685 Mon Sep 17 00:00:00 2001 From: David Novakovic Date: Wed, 4 Feb 2015 11:27:08 +1000 Subject: [PATCH 1/3] Disable SSLv3 in the mail client. Also added some basic unit tests for mail.py. --- cyclone/mail.py | 16 +++++++-- cyclone/tests/test_mail.py | 69 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 cyclone/tests/test_mail.py diff --git a/cyclone/mail.py b/cyclone/mail.py index 19e17cfbb6..2ac1fafb00 100644 --- a/cyclone/mail.py +++ b/cyclone/mail.py @@ -26,7 +26,7 @@ import os.path from cStringIO import StringIO -from OpenSSL.SSL import SSLv3_METHOD +from OpenSSL.SSL import OP_NO_SSLv3 from email import Encoders from email.MIMEText import MIMEText @@ -40,6 +40,17 @@ from twisted.mail.smtp import ESMTPSenderFactory, quoteaddr +class ContextFactory(ClientContextFactory): + + """Context factory that disables SSLv3 (POODLE attack).""" + + def getContext(self): + """Get the parent context but disable SSLv3.""" + ctx = ClientContextFactory.getContext(self) + ctx.set_options(OP_NO_SSLv3) + return ctx + + class Message(object): """Create new e-mail messages. @@ -170,8 +181,7 @@ def sendmail(mailconf, message): if use_tls: port = mailconf.get("port", 587) - contextFactory = ClientContextFactory() - contextFactory.method = SSLv3_METHOD + contextFactory = ContextFactory() else: port = mailconf.get("port", 25) contextFactory = None diff --git a/cyclone/tests/test_mail.py b/cyclone/tests/test_mail.py new file mode 100644 index 0000000000..6c96ace4d5 --- /dev/null +++ b/cyclone/tests/test_mail.py @@ -0,0 +1,69 @@ +# +# Copyright 2014 David Novakovic +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from twisted.trial import unittest +from cyclone.mail import ContextFactory, ClientContextFactory, Message +from cyclone.mail import sendmail +from mock import Mock, patch +import types + + +class ContextFactoryTest(unittest.TestCase): + def test_no_sslv3(self): + """We must disable ssl v3.""" + ClientContextFactory.getContext = Mock() + cf = ContextFactory() + ctx = cf.getContext() + ctx.set_options.assert_called_with(33554432) + + +class MessageTest(unittest.TestCase): + def setUp(self): + self.message = Message( + "foo@example.com", + ["bar@example.com"], + "hi thar", + "This is a message." + ) + + def test_init(self): + self.assertTrue(self.message.message) + str(self.message) + + def test_init_single_addr(self): + message = Message( + "foo@example.com", + "bar@example.com", + "hi thar", + "This is a message." + ) + self.assertTrue(isinstance(message.to_addrs, types.ListType)) + + def test_attach(self): + open("foo.txt", "w").write("sometext") + self.message.attach("foo.txt") + self.assertTrue(self.message.msg) + + def test_render(self): + self.message.add_header("X-MailTag", "foobar") + sio = self.message.render() + self.assertTrue("foo@example.com" in sio.getvalue()) + + @patch("cyclone.mail.reactor.connectTCP") + def test_sendmail(self, conn): + sendmail( + {"host": "localhost", "tls": True}, + self.message + ) + self.assertTrue(conn.call_count) From cc8e0361ebcf8bfb8bc1f79ef79d7dc52f797084 Mon Sep 17 00:00:00 2001 From: David Novakovic Date: Thu, 7 Jan 2016 10:12:51 +1000 Subject: [PATCH 2/3] Remove 2.6 for this branch. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f31f156e78..1a97f8f6d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "2.6" - "2.7" - "pypy" From 5f16b9e7013834178978490e90ad0efd216965f4 Mon Sep 17 00:00:00 2001 From: David Novakovic Date: Thu, 7 Jan 2016 10:15:36 +1000 Subject: [PATCH 3/3] Need openssl installed for tests. --- cyclone/tests/test_requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cyclone/tests/test_requirements.txt b/cyclone/tests/test_requirements.txt index 787c3f06e3..803b1afaf2 100644 --- a/cyclone/tests/test_requirements.txt +++ b/cyclone/tests/test_requirements.txt @@ -1,2 +1,3 @@ mock -twisted>=12.0 \ No newline at end of file +twisted>=12.0 +pyopenssl