コード例 #1
0
    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(
            CONFDIR + '/templates/blockedfile.tmpl.dist', self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '7000000')
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache
コード例 #2
0
    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(
            CONFDIR + '/templates/blockedfile.tmpl.dist', self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '5000000')
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache
コード例 #3
0
    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(CONFDIR + '/templates/blockedfile.tmpl.dist',
                    self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)

        # extend by the content we use for blocking in this test
        with open('%s/default-filetypes.conf' % self.tempdir, "a+") as f:
            f.write(
                "\ndeny	     application\/x-dosexec	    No DOS executables")

        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '7000000')
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('main', 'nobouncefile', '')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache
コード例 #4
0
class AttachmentMemoryTest(unittest.TestCase):
    """
    Test memory removal for attachment manager in realistic case
    using the attachment plugin
    """

    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(
            CONFDIR + '/templates/blockedfile.tmpl.dist', self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '7000000')
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache

    def tearDown(self):
        os.remove('%s/default-filenames.conf' % self.tempdir)
        os.remove('%s/default-filetypes.conf' % self.tempdir)
        os.remove(self.template)
        shutil.rmtree(self.tempdir)

    def test_archiveextractsize(self):
        """Test reference counts based on the archive
        test 'test archive extract max filesize'"""
        for testfile in ['6mbzipattachment.eml', '6mbrarattachment.eml']:
            try:
                tmpfile = tempfile.NamedTemporaryFile(
                    suffix='virus', prefix='fuglu-unittest', dir='/tmp')
                shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

                user = '******'
                conffile = self.tempdir + "/%s-archivefiletypes.conf" % user
                # the largefile in the test message is just a bunch of zeroes
                open(conffile, 'w').write(
                    "deny application\/octet\-stream no data allowed")
                self.rulescache._loadrules()
                suspect = Suspect(
                    '*****@*****.**', user, tmpfile.name)
                print("Refcounts to suspect/manager after creating it: %u/%u"
                      % (sys.getrefcount(suspect),
                         0 if suspect._att_mgr is None else sys.getrefcount(suspect._att_mgr)))

                # backup old limits from config file
                oldlimit = self.candidate.config.getint('FiletypePlugin', 'archivecontentmaxsize')
                oldlimit_aelevel = self.candidate.config.getint('FiletypePlugin', 'archiveextractlevel')

                # now set the limit to 4 mb, the file should be skipped now
                #
                # check log
                # reason of skipping should be the size is to large, file largefile/6mbfile is not extracted
                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', 4000000)

                print("Refcounts to suspect/manager before first examine: %u/%u"
                      % (sys.getrefcount(suspect),

                         0 if suspect._att_mgr is None else sys.getrefcount(suspect._att_mgr)))
                result = self.candidate.examine(suspect)

                print("Refcounts to suspect/manager after first examine: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))

                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped (not extracted)')

                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', 7000000)

                print("Refcounts to suspect/manager before second examine: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))

                result = self.candidate.examine(suspect)
                print("Refcounts to suspect/manager after second examine: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(
                    result, DELETE, 'extracted large file should be blocked')

                # now set the limit to 5 mb, the file should be skipped now
                # check log
                # reason of skipping should be the size is to large for check,
                # file largefile/6mbfile is already extracted
                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', 5000000)

                print("Refcounts to suspect/manager before third examine: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))

                result = self.candidate.examine(suspect)
                print("Refcounts to suspect/manager after third examine: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))

                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped')

                # now set the limit to 7 mb, the file should be skipped now
                self.candidate.config.set('FiletypePlugin', 'archivecontentmaxsize', 7000000)
                self.candidate.config.set('FiletypePlugin', 'archiveextractlevel', 0)

                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped')

                # reset config
                self.candidate.config.set('FiletypePlugin', 'archivecontentmaxsize', oldlimit)
                self.candidate.config.set('FiletypePlugin', 'archiveextractlevel', oldlimit_aelevel)

                print("Refcounts to suspect/manager before deleting it: %u/%u"
                      % (sys.getrefcount(suspect),sys.getrefcount(suspect._att_mgr)))
                self.assertEqual(2, sys.getrefcount(suspect), "only two references should remain, suspect and the one"
                                                              "from the 'getrefcount' call itself")
                self.assertEqual(2, sys.getrefcount(suspect._att_mgr), "only two references should remain, the one in "
                                                                       "suspect and the one from the 'getrefcount' "
                                                                       "call itself")
                del suspect

                if OBJGRAPH_EXTENSION_ENABLED:
                    print("\n----------------------")
                    print("After deleting suspect")
                    print("----------------------")
                    sus_objects = objgraph.by_type('Suspect')
                    ma_objects = objgraph.by_type('Mailattachment')
                    mam_objects = objgraph.by_type('Mailattachment_mgr')
                    print("Suspects in memory: %u" % len(sus_objects))
                    print("MailAttachments in memory: %u" % len(ma_objects))
                    print("MailAttachmentMgr in memory: %u" % len(mam_objects))

                    print("Refcounts:")
                    for sus in sus_objects:
                        print(" Suspect: %u" % sys.getrefcount(sus))
                    for mgr in mam_objects:
                        print(" Mgr: %u" % sys.getrefcount(mgr))
                    for ma in ma_objects:
                        print(" MailAttachment: %u" % sys.getrefcount(ma))

                    # if enabled print graphs
                    if print_graphs:
                        if len(sus_objects) > 0:
                            objgraph.show_backrefs(sus_objects, max_depth=5, refcounts=True)
                        elif len(ma_objects) > 0:
                            objgraph.show_backrefs(ma_objects, max_depth=5, refcounts=True)
                        elif len(mam_objects) > 0:
                            objgraph.show_backrefs(mam_objects, max_depth=5, refcounts=True)

                    self.assertEqual(0, len(sus_objects), "Deleting suspect should have removed Suspect objects")
                    self.assertEqual(0, len(mam_objects), "Deleting suspect should have removed Mailattachment_mgr objects")
                    self.assertEqual(0, len(ma_objects), "Deleting suspect should have removed Mailattachment objects")
            finally:
                tmpfile.close()
                os.remove(conffile)
コード例 #5
0
class AttachmentPluginTestCase(unittest.TestCase):

    """Testcases for the Attachment Checker Plugin"""

    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(
            CONFDIR + '/templates/blockedfile.tmpl.dist', self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '5000000')
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache

    def tearDown(self):
        os.remove('%s/default-filenames.conf' % self.tempdir)
        os.remove('%s/default-filetypes.conf' % self.tempdir)
        os.remove(self.template)
        shutil.rmtree(self.tempdir)

    def test_hiddenbinary(self):
        """Test if hidden binaries get detected correctly"""
        # copy file rules
        tmpfile = tempfile.NamedTemporaryFile(
            suffix='virus', prefix='fuglu-unittest', dir='/tmp')
        shutil.copy(TESTDATADIR + '/binaryattachment.eml', tmpfile.name)
        suspect = Suspect(
            '*****@*****.**', '*****@*****.**', tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        self.assertEqual(result, DELETE)

    def test_umlaut_in_zip(self):
        """Issue 69: Test if zip with files that contain umlauts are extracted ok"""
        tmpfile = tempfile.NamedTemporaryFile(
            suffix='badattach', prefix='fuglu-unittest', dir='/tmp')
        shutil.copy(TESTDATADIR + '/umlaut-in-attachment.eml', tmpfile.name)
        suspect = Suspect(
            '*****@*****.**', '*****@*****.**', tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        self.assertEqual(result, DUNNO)


    def test_archiveextractsize(self):
        """Test archive extract max filesize"""
        # copy file rules
        for testfile in ['6mbzipattachment.eml', '6mbrarattachment.eml']:
            try:
                tmpfile = tempfile.NamedTemporaryFile(
                    suffix='virus', prefix='fuglu-unittest', dir='/tmp')
                shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

                user = '******'
                conffile = self.tempdir + "/%s-archivefiletypes.conf" % user
                # the largefile in the test message is just a bunch of zeroes
                open(conffile, 'w').write(
                    "deny application\/octet\-stream no data allowed")
                self.rulescache._loadrules()
                suspect = Suspect(
                    '*****@*****.**', user, tmpfile.name)

                # test with high limit first
                oldlimit = self.candidate.config.get(
                    'FiletypePlugin', 'archivecontentmaxsize')
                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', '7000000')
                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(
                    result, DELETE, 'extracted large file should be blocked')

                # now set the limit to 5 mb, the file should be skipped now
                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', '5000000')
                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped')

                # reset config
                self.candidate.config.set(
                    'FiletypePlugin', 'archivecontentmaxsize', oldlimit)
            finally:
                tmpfile.close()
                os.remove(conffile)

    def test_archivename(self):
        """Test check archive names"""

        for testfile in ['6mbzipattachment.eml', '6mbrarattachment.eml']:
            try:
                # copy file rules
                tmpfile = tempfile.NamedTemporaryFile(
                    suffix='virus', prefix='fuglu-unittest', dir='/tmp')
                shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

                user = '******'
                conffile = self.tempdir + "/%s-archivenames.conf" % user
                open(conffile, 'w').write(
                    "deny largefile user does not like the largefile within a zip\ndeny 6mbfile user does not like the largefile within a zip")
                self.rulescache._loadrules()
                suspect = Suspect(
                    '*****@*****.**', user, tmpfile.name)

                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(
                    result, DELETE, 'archive containing blocked filename was not blocked')
            finally:
                tmpfile.close()
                os.remove(conffile)


    def test_hiddenpart(self):
        """Test for hidden part in message epilogue"""
        testfile='hiddenpart.eml'
        try:
            tmpfile = tempfile.NamedTemporaryFile(
                suffix='hidden', prefix='fuglu-unittest', dir='/tmp')
            shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

            user = '******'
            conffile = self.tempdir + "/%s-filetypes.conf" % user
            # the largefile in the test message is just a bunch of zeroes
            open(conffile, 'w').write(
                "deny application\/zip no zips allowed")
            self.rulescache._loadrules()
            suspect = Suspect(
                '*****@*****.**', user, tmpfile.name)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(
                result, DELETE, 'hidden message part was not detected')

        finally:
            tmpfile.close()
            os.remove(conffile)


    def test_archive_wrong_extension(self):
        """Test if archives don't fool us with forged file extensions"""
        testfile = 'wrongextension.eml'
        try:
            tmpfile = tempfile.NamedTemporaryFile(
                suffix='wrongext', prefix='fuglu-unittest', dir='/tmp')
            shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

            user = '******'
            conffile = self.tempdir + "/%s-archivenames.conf" % user
            # the largefile in the test message is just a bunch of zeroes
            open(conffile, 'w').write(
                "deny \.exe$ exe detected in zip with wrong extension")
            self.rulescache._loadrules()
            suspect = Suspect(
                '*****@*****.**', user, tmpfile.name)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(
                result, DELETE, 'exe in zip with .gz extension was not detected')

        finally:
            tmpfile.close()
            os.remove(conffile)
コード例 #6
0
class AttachmentPluginTestCaseMockBounce(unittest.TestCase):
    """
    Test setup with bouncing enabled. Don't forget to patch SMTP to prevent actual
    sending mail. Patch SMTP is done putting the mocking decorator:

    @patch("smtplib.SMTP") # when working with smtplib directly
    @patch("fuglu.bounce.FugluSMTPClient" # when using fuglu's smtp client from fuglu.bounce module

    in front of the test.
    """
    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(CONFDIR + '/templates/blockedfile.tmpl.dist',
                    self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '7000000')
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '0')
        config.set('main', 'nobouncefile', '')
        config.set('main', 'outgoingport', '10038')
        config.set('main', 'outgoinghelo', 'test.fuglu.org')
        config.set('main', 'bindaddress', '127.0.0.1')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache

    def tearDown(self):
        os.remove('%s/default-filenames.conf' % self.tempdir)
        os.remove('%s/default-filetypes.conf' % self.tempdir)
        os.remove(self.template)
        shutil.rmtree(self.tempdir)

    @patch("fuglu.bounce.FugluSMTPClient")
    def test_bounce_withenvsender(self, smtpmock):
        """Reference test bounce is called for setup, next test 'test_bounce_noenvsender' should behave differently."""

        testfile = '6mbzipattachment.eml'

        # copy file rules
        tmpfile = tempfile.NamedTemporaryFile(suffix='virus',
                                              prefix='fuglu-unittest',
                                              dir='/tmp')
        shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

        user = '******'
        conffile = self.tempdir + "/%s-archivenames.conf" % user
        open(conffile, 'w').write(
            "deny largefile user does not like the largefile within a zip\ndeny 6mbfile user does not like the largefile within a zip"
        )
        self.rulescache._loadrules()
        suspect = Suspect('*****@*****.**', user, tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        self.assertEqual(
            result, DELETE,
            'archive containing blocked filename was not blocked')
        tmpfile.close()
        os.remove(conffile)

        smtpmock_membercalls = [call[0] for call in smtpmock.mock_calls]
        self.assertTrue(smtpmock.called)
        self.assertTrue("().helo" in smtpmock_membercalls)
        self.assertTrue("().sendmail" in smtpmock_membercalls)
        self.assertTrue("().quit" in smtpmock_membercalls)

    @patch("fuglu.bounce.FugluSMTPClient")
    def test_bounce_noenvsender(self, smtpmock):
        """Don't try to send a bounce if original env sender is empty"""

        testfile = '6mbzipattachment.eml'

        # copy file rules
        tmpfile = tempfile.NamedTemporaryFile(suffix='virus',
                                              prefix='fuglu-unittest',
                                              dir='/tmp')
        shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

        user = '******'
        conffile = self.tempdir + "/%s-archivenames.conf" % user
        open(conffile, 'w').write(
            "deny largefile user does not like the largefile within a zip\ndeny 6mbfile user does not like the largefile within a zip"
        )
        self.rulescache._loadrules()
        suspect = Suspect('', user, tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        self.assertEqual(
            result, DELETE,
            'archive containing blocked filename was not blocked')
        tmpfile.close()
        os.remove(conffile)

        self.assertFalse(
            smtpmock.called,
            "No SMTP client should have been created because there's no mail to send."
        )
コード例 #7
0
class AttachmentPluginTestCase(unittest.TestCase):
    """Testcases for the Attachment Checker Plugin"""
    def setUp(self):
        self.tempdir = tempfile.mkdtemp('attachtest', 'fuglu')
        self.template = '%s/blockedfile.tmpl' % self.tempdir
        shutil.copy(CONFDIR + '/templates/blockedfile.tmpl.dist',
                    self.template)
        shutil.copy(CONFDIR + '/rules/default-filenames.conf.dist',
                    '%s/default-filenames.conf' % self.tempdir)
        shutil.copy(CONFDIR + '/rules/default-filetypes.conf.dist',
                    '%s/default-filetypes.conf' % self.tempdir)
        config = RawConfigParser()
        config.add_section('FiletypePlugin')
        config.set('FiletypePlugin', 'template_blockedfile', self.template)
        config.set('FiletypePlugin', 'rulesdir', self.tempdir)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'checkarchivenames', 'True')
        config.set('FiletypePlugin', 'checkarchivecontent', 'True')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '7000000')
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')

        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('main', 'nobouncefile', '')
        self.candidate = FiletypePlugin(config)
        self.rulescache = RulesCache(self.tempdir)
        self.candidate.rulescache = self.rulescache

    def tearDown(self):
        os.remove('%s/default-filenames.conf' % self.tempdir)
        os.remove('%s/default-filetypes.conf' % self.tempdir)
        os.remove(self.template)
        shutil.rmtree(self.tempdir)

    def test_hiddenbinary(self):
        """Test if hidden binaries get detected correctly"""
        # copy file rules
        tmpfile = tempfile.NamedTemporaryFile(suffix='virus',
                                              prefix='fuglu-unittest',
                                              dir='/tmp')
        shutil.copy(TESTDATADIR + '/binaryattachment.eml', tmpfile.name)
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        self.assertEqual(result, DELETE)

    def test_umlaut_in_zip(self):
        """Issue 69: Test if zip with files that contain umlauts are extracted ok"""
        tmpfile = tempfile.NamedTemporaryFile(suffix='badattach',
                                              prefix='fuglu-unittest',
                                              dir='/tmp')
        shutil.copy(TESTDATADIR + '/umlaut-in-attachment.eml', tmpfile.name)
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        self.assertEqual(result, DUNNO)

    def test_special_archive_name(self):
        """Check if gz file with exclamantion marks and points in archive name is extracted ok"""
        import logging
        import sys

        root = logging.getLogger()
        root.setLevel(logging.DEBUG)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        ch.setFormatter(formatter)
        root.addHandler(ch)

        tmpfile = tempfile.NamedTemporaryFile(suffix='badattach',
                                              prefix='fuglu-unittest',
                                              dir='/tmp')
        shutil.copy(TESTDATADIR + '/attachment_exclamation_marks_points.eml',
                    tmpfile.name)
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tmpfile.name)

        self.assertTrue('aaa.aa!aaaaaaaaa.aa!2345678910!1234567891.xml' in
                        suspect.att_mgr.get_fileslist(None))

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        self.assertEqual(result, DUNNO)

    def test_archiveextractsize(self):
        """Test archive extract max filesize"""
        # copy file rules
        for testfile in ['6mbzipattachment.eml', '6mbrarattachment.eml']:
            try:
                tmpfile = tempfile.NamedTemporaryFile(suffix='virus',
                                                      prefix='fuglu-unittest',
                                                      dir='/tmp')
                shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

                user = '******'
                conffile = self.tempdir + "/%s-archivefiletypes.conf" % user
                # the largefile in the test message is just a bunch of zeroes
                open(conffile, 'w').write(
                    "deny application\/octet\-stream no data allowed")
                self.rulescache._loadrules()
                suspect = Suspect('*****@*****.**', user,
                                  tmpfile.name)

                # backup old limits from config file
                oldlimit = self.candidate.config.getint(
                    'FiletypePlugin', 'archivecontentmaxsize')
                oldlimit_aelevel = self.candidate.config.getint(
                    'FiletypePlugin', 'archiveextractlevel')

                # now set the limit to 4 mb, the file should be skipped now
                #
                # check log
                # reason of skipping should be the size is to large, file largefile/6mbfile is not extracted
                self.candidate.config.set('FiletypePlugin',
                                          'archivecontentmaxsize', 4000000)
                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(
                    result, DUNNO,
                    'large file should be skipped (not extracted)')

                self.candidate.config.set('FiletypePlugin',
                                          'archivecontentmaxsize', 7000000)
                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DELETE,
                                 'extracted large file should be blocked')

                # now set the limit to 5 mb, the file should be skipped now
                # check log
                # reason of skipping should be the size is to large for check, file largefile/6mbfile is already extracted
                self.candidate.config.set('FiletypePlugin',
                                          'archivecontentmaxsize', 5000000)
                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped')

                # now set the limit to 7 mb, the file should be skipped now
                self.candidate.config.set('FiletypePlugin',
                                          'archivecontentmaxsize', 7000000)
                self.candidate.config.set('FiletypePlugin',
                                          'archiveextractlevel', 0)

                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(result, DUNNO, 'large file should be skipped')

                # reset config
                self.candidate.config.set('FiletypePlugin',
                                          'archivecontentmaxsize', oldlimit)
                self.candidate.config.set('FiletypePlugin',
                                          'archiveextractlevel',
                                          oldlimit_aelevel)
            finally:
                tmpfile.close()
                os.remove(conffile)

    def test_archivename(self):
        """Test check archive names"""

        for testfile in ['6mbzipattachment.eml', '6mbrarattachment.eml']:
            try:
                # copy file rules
                tmpfile = tempfile.NamedTemporaryFile(suffix='virus',
                                                      prefix='fuglu-unittest',
                                                      dir='/tmp')
                shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

                user = '******'
                conffile = self.tempdir + "/%s-archivenames.conf" % user
                open(conffile, 'w').write(
                    "deny largefile user does not like the largefile within a zip\ndeny 6mbfile user does not like the largefile within a zip"
                )
                self.rulescache._loadrules()
                suspect = Suspect('*****@*****.**', user,
                                  tmpfile.name)

                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.assertEqual(
                    result, DELETE,
                    'archive containing blocked filename was not blocked')
            finally:
                tmpfile.close()
                os.remove(conffile)

    def test_archivename_nestedarchive(self):
        """Test check archive names in nested archive"""

        #---
        # Note:
        #---
        # mail testedarchive.eml contains the attachment "nestedarchive.tar.gz"
        # which has the following nested structure:
        #---
        # Level : (extracted from archive  ) -> Files
        #---
        # 0     : nestedarchive.tar.gz
        # 1     : (extracting level1.tar.gz) -> level0.txt   level1.tar.gz
        # 2     : (extracting level1.tar.gz) -> level1.txt   level2.tar.gz
        # 3     : (extracting level2.tar.gz) -> level2.txt   level3.tar.gz
        # 4     : (extracting level3.tar.gz) -> level3.txt   level4.tar.gz
        # 5     : (extracting level4.tar.gz) -> level4.txt   level5.tar.gz
        # 6     : (extracting level5.tar.gz) -> level5.txt   level6.tar.gz
        # 7     : (extracting level6.tar.gz) -> level6.txt

        testfile = os.path.join(TESTDATADIR, "nestedarchive.eml")
        try:
            # copy file rules
            user = '******'
            conffile = self.tempdir + "/%s-archivenames.conf" % user
            open(conffile, 'w').write(
                "deny level6.txt user does not like the files in nested archives \ndeny 6mbfile user does not like the largefile within a zip"
            )
            self.rulescache._loadrules()

            suspect = Suspect('*****@*****.**', user, testfile)

            oldlimit_aelevel = self.candidate.config.getint(
                'FiletypePlugin', 'archiveextractlevel')

            #----
            self.candidate.config.set('FiletypePlugin', 'archiveextractlevel',
                                      6)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(
                result, DUNNO,
                'archive containing blocked filename should not be extracted')

            #----
            self.candidate.config.set('FiletypePlugin', 'archiveextractlevel',
                                      7)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(
                result, DELETE,
                'archive containing blocked filename was not blocked')

            self.candidate.config.set('FiletypePlugin', 'archiveextractlevel',
                                      oldlimit_aelevel)
        finally:
            os.remove(conffile)

    def test_hiddenpart(self):
        """Test for hidden part in message epilogue"""
        testfile = 'hiddenpart.eml'
        try:
            tmpfile = tempfile.NamedTemporaryFile(suffix='hidden',
                                                  prefix='fuglu-unittest',
                                                  dir='/tmp')
            shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

            user = '******'
            conffile = self.tempdir + "/%s-filetypes.conf" % user
            # the largefile in the test message is just a bunch of zeroes
            open(conffile, 'w').write("deny application\/zip no zips allowed")
            self.rulescache._loadrules()
            suspect = Suspect('*****@*****.**', user, tmpfile.name)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(result, DELETE,
                             'hidden message part was not detected')

        finally:
            tmpfile.close()
            os.remove(conffile)

    def test_archive_wrong_extension(self):
        """Test if archives don't fool us with forged file extensions"""
        testfile = 'wrongextension.eml'
        try:
            tmpfile = tempfile.NamedTemporaryFile(suffix='wrongext',
                                                  prefix='fuglu-unittest',
                                                  dir='/tmp')
            shutil.copy("%s/%s" % (TESTDATADIR, testfile), tmpfile.name)

            user = '******'
            conffile = self.tempdir + "/%s-archivenames.conf" % user
            # the largefile in the test message is just a bunch of zeroes
            open(conffile, 'w').write(
                "deny \.exe$ exe detected in zip with wrong extension")
            self.rulescache._loadrules()
            suspect = Suspect('*****@*****.**', user, tmpfile.name)

            result = self.candidate.examine(suspect)
            if type(result) is tuple:
                result, message = result
            self.assertEqual(result, DELETE,
                             'exe in zip with .gz extension was not detected')

        finally:
            tmpfile.close()
            os.remove(conffile)