Example #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
Example #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)

        # 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
Example #3
0
    def setUp(self):
        testfile = "/tmp/attachconfig.db"
        if os.path.exists(testfile):
            os.remove(testfile)
        # important: 4 slashes for absolute paths!
        testdb = "sqlite:///%s" % testfile

        sql = """create table attachmentrules(
        id integer not null primary key,
        scope varchar(255) not null,
        checktype varchar(20) not null,
        action varchar(255) not null,
        regex varchar(255) not null,
        description varchar(255) not null,
        prio integer not null
        )
        """

        self.session = fuglu.extensions.sql.get_session(testdb)
        self.session.flush()
        self.session.execute(sql)
        self.tempdir = tempfile.mkdtemp('attachtestdb', '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', 'dbconnectstring', testdb)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set(
            'FiletypePlugin', 'query',
            'SELECT action,regex,description FROM attachmentrules WHERE scope=:scope AND checktype=:checktype ORDER BY prio'
        )
        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('main', 'nobouncefile', '')
        config.set('FiletypePlugin', 'checkarchivenames', 'False')
        config.set('FiletypePlugin', 'checkarchivecontent', 'False')
        config.set('FiletypePlugin', 'archivecontentmaxsize', 500000)
        config.set('FiletypePlugin', 'archiveextractlevel', -1)
        config.set('FiletypePlugin', 'enabledarchivetypes', '')
        self.candidate = FiletypePlugin(config)
    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 setUp(self):
        testfile = "/tmp/attachconfig.db"
        if os.path.exists(testfile):
            os.remove(testfile)
        # important: 4 slashes for absolute paths!
        testdb = "sqlite:///%s" % testfile

        sql = """create table attachmentrules(
        id integer not null primary key,
        scope varchar(255) not null,
        checktype varchar(20) not null,
        action varchar(255) not null,
        regex varchar(255) not null,
        description varchar(255) not null,
        prio integer not null
        )
        """

        self.session = fuglu.extensions.sql.get_session(testdb)
        self.session.flush()
        self.session.execute(sql)
        self.tempdir = tempfile.mkdtemp('attachtestdb', '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', 'dbconnectstring', testdb)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'query',
                   'SELECT action,regex,description FROM attachmentrules WHERE scope=:scope AND checktype=:checktype ORDER BY prio')
        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('FiletypePlugin', 'checkarchivenames', 'False')
        config.set('FiletypePlugin', 'checkarchivecontent', 'False')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '500000')
        config.set('FiletypePlugin', 'enabledarchivetypes', '')
        self.candidate = FiletypePlugin(config)
Example #6
0
class DatabaseConfigTestCase(unittest.TestCase):

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

    def setUp(self):
        testfile = "/tmp/attachconfig.db"
        if os.path.exists(testfile):
            os.remove(testfile)
        # important: 4 slashes for absolute paths!
        testdb = "sqlite:///%s" % testfile

        sql = """create table attachmentrules(
        id integer not null primary key,
        scope varchar(255) not null,
        checktype varchar(20) not null,
        action varchar(255) not null,
        regex varchar(255) not null,
        description varchar(255) not null,
        prio integer not null
        )
        """

        self.session = fuglu.extensions.sql.get_session(testdb)
        self.session.flush()
        self.session.execute(sql)
        self.tempdir = tempfile.mkdtemp('attachtestdb', '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', 'dbconnectstring', testdb)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set('FiletypePlugin', 'query',
                   'SELECT action,regex,description FROM attachmentrules WHERE scope=:scope AND checktype=:checktype ORDER BY prio')
        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('FiletypePlugin', 'checkarchivenames', 'False')
        config.set('FiletypePlugin', 'checkarchivecontent', 'False')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '500000')
        self.candidate = FiletypePlugin(config)

    def test_dbrules(self):
        """Test if db rules correctly override defaults"""

        testdata = u"""
        INSERT INTO attachmentrules(scope,checktype,action,regex,description,prio) VALUES
        ('*****@*****.**','contenttype','allow','application/x-executable','this user likes exe',1)
        """
        self.session.execute(testdata)
        # 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)
        resstr = actioncode_to_string(result)
        self.assertEqual(resstr, "DUNNO")

        # another recipient should still get the block
        suspect = Suspect(
            '*****@*****.**', '*****@*****.**', tmpfile.name)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        resstr = actioncode_to_string(result)
        self.assertEqual(resstr, "DELETE")
        tmpfile.close()
Example #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', '5000000')

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

    def tearDown(self):
        os.remove('%s/default-filenames.conf' % self.tempdir)
        os.remove('%s/default-filetypes.conf' % self.tempdir)
        os.remove(self.template)
        os.rmdir(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)

    @nottest
    def test_utf8msg(self):
        """Test utf8 msgs are parsed ok - can cause bugs on some magic implementations (eg. centos)
        disabled - need new sample"""

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

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        tmpfile.close()
        os.remove(tmpfile.name)
        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")

                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")

                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)
Example #8
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)
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)
Example #10
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.add_section('main')
        config.set('main', 'disablebounces', '1')
        self.candidate = FiletypePlugin(config)

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

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

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        os.remove(tempfilename)
        self.failIf(result != DELETE)

    @nottest
    def test_utf8msg(self):
        """Test utf8 msgs are parsed ok - can cause bugs on some magic implementations (eg. centos)
        disabled - need new sample"""

        tempfilename = tempfile.mktemp(suffix='virus',
                                       prefix='fuglu-unittest',
                                       dir='/tmp')
        shutil.copy(TESTDATADIR + '/utf8message.eml', tempfilename)
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tempfilename)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        os.remove(tempfilename)
        self.assertEquals(result, DUNNO)

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

                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")

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

                # 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.failIf(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.failIf(result != DUNNO, 'large file should be skipped')

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

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

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

                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"
                )

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

                result = self.candidate.examine(suspect)
                if type(result) is tuple:
                    result, message = result
                self.failIf(
                    result != DELETE,
                    'archive containing blocked filename was not blocked')
            finally:
                os.remove(tempfilename)
                os.remove(conffile)
Example #11
0
class DatabaseConfigTestCase(unittest.TestCase):
    """Testcases for the Attachment Checker Plugin"""
    def setUp(self):
        testfile = "/tmp/attachconfig.db"
        if os.path.exists(testfile):
            os.remove(testfile)
        # important: 4 slashes for absolute paths!
        testdb = "sqlite:///%s" % testfile

        sql = """create table attachmentrules(
        id integer not null primary key,
        scope varchar(255) not null,
        checktype varchar(20) not null,
        action varchar(255) not null,
        regex varchar(255) not null,
        description varchar(255) not null,
        prio integer not null
        )
        """

        self.session = fuglu.extensions.sql.get_session(testdb)
        self.session.flush()
        self.session.execute(sql)
        self.tempdir = tempfile.mkdtemp('attachtestdb', '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', 'dbconnectstring', testdb)
        config.set('FiletypePlugin', 'blockaction', 'DELETE')
        config.set('FiletypePlugin', 'sendbounce', 'True')
        config.set(
            'FiletypePlugin', 'query',
            'SELECT action,regex,description FROM attachmentrules WHERE scope=:scope AND checktype=:checktype ORDER BY prio'
        )
        config.add_section('main')
        config.set('main', 'disablebounces', '1')
        config.set('FiletypePlugin', 'checkarchivenames', 'False')
        config.set('FiletypePlugin', 'checkarchivecontent', 'False')
        config.set('FiletypePlugin', 'archivecontentmaxsize', '500000')
        self.candidate = FiletypePlugin(config)

    def test_dbrules(self):
        """Test if db rules correctly override defaults"""

        testdata = u"""
        INSERT INTO attachmentrules(scope,checktype,action,regex,description,prio) VALUES
        ('*****@*****.**','contenttype','allow','application/x-executable','this user likes exe',1)
        """
        self.session.execute(testdata)
        # copy file rules
        tempfilename = tempfile.mktemp(suffix='virus',
                                       prefix='fuglu-unittest',
                                       dir='/tmp')
        shutil.copy(TESTDATADIR + '/binaryattachment.eml', tempfilename)
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tempfilename)

        result = self.candidate.examine(suspect)
        resstr = actioncode_to_string(result)
        self.assertEquals(resstr, "DUNNO")

        # another recipient should still get the block
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**', tempfilename)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        resstr = actioncode_to_string(result)
        self.assertEquals(resstr, "DELETE")
        os.remove(tempfilename)
Example #12
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."
        )
Example #13
0
class TestOnlyAttachmentInline(unittest.TestCase):
    """
    From bug report. Message text parts (unnamed.txt) detected as dosexec
    and the blocked.
    """
    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

    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_dosexec(self):
        suspect = Suspect('*****@*****.**',
                          '*****@*****.**',
                          TESTDATADIR + '/6mbrarattachment.eml')

        attachmentmanager = suspect.att_mgr
        objectlist = attachmentmanager.get_objectlist()
        filenames = [obj.filename for obj in objectlist]

        self.assertTrue("unnamed.txt" in filenames,
                        "unnamed.txt not in list %s" % ",".join(filenames))
        for obj in objectlist:
            if obj.filename == "unnamed.txt":
                contenttype = obj.contenttype
                # this is a cached property -> patch the cached value for this test
                obj._property_cache['contenttype'] = 'application/x-dosexec'

                # explicitly set to the blocking content
                contenttype = obj.contenttype
                self.assertEqual(
                    'application/x-dosexec', contenttype,
                    "-> check patching of dict for smart_cached_property")
                print("content type: %s" % contenttype)
                print("filename auto generated: %s" % obj.filename_generated)

        result = self.candidate.examine(suspect)
        if type(result) is tuple:
            result, message = result
        self.assertEqual(result, DUNNO, "no attachment should be blocked!")
Example #14
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)