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)
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." )
def get_suspect(self): success = self.sess.getincomingmail() if not success: self.logger.error('incoming smtp transfer did not finish') return None sess = self.sess fromaddr = "*****@*****.**" toaddr = ["*****@*****.**"] # use envelope from/to from ncsession if available if sess.from_address: fromaddr = sess.from_address if sess.recipients: toaddr = sess.recipients tempfilename = sess.tempfilename suspect = Suspect(fromaddr, toaddr, tempfilename, att_cachelimit=self._att_mgr_cachesize) if sess.tags: # update suspect tags with the ones receivec # during ncsession suspect.tags.update(sess.tags) return suspect
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)
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!")
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)
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)
def test_virus(self): """Test if eicar is detected as virus""" from fuglu.shared import Suspect import shutil self.mc.load_plugins() if len(self.mc.plugins) == 0: raise Exception("plugins not loaded") sesshandler = SessionHandler(None, self.mc.config, self.mc.prependers, self.mc.plugins, self.mc.appenders) tempfilename = tempfile.mktemp(suffix='virus', prefix='fuglu-unittest', dir='/tmp') shutil.copy(TESTDATADIR + '/eicar.eml', tempfilename) self.tempfiles.append(tempfilename) suspect = Suspect('*****@*****.**', '*****@*****.**', tempfilename) pluglist = sesshandler.run_prependers(suspect) self.assertFalse( len(pluglist) == 0, "Viruscheck will fail, pluginlist empty after run_prependers") sesshandler.run_plugins(suspect, pluglist) self.assertTrue(suspect.is_virus(), "Eicar message was not detected as virus")
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_generated_message(self): from fuglu.shared import Suspect suspect = Suspect(u'*****@*****.**', '*****@*****.**', '/dev/null') suspect.tags['nobounce'] = True v = Vacation() v.ignoresender = u"" v.awayuser = u'*****@*****.**' v.created = datetime.now() v.start = datetime.now() v.end = v.start + timedelta(days=2) v.subject = u'Döner mit schärf!' v.body = u"""Je m'envole pour l'Allemagne, où je suis né.""" # v.body="Away!" self.session.add(v) self.session.flush() self.session.expunge_all() self.refreshcache() candidatevacation = self.candidate.on_vacation(suspect) self.assertTrue(candidatevacation != None, "Vacation object not found in database") message = self.candidate.send_vacation_reply(suspect, candidatevacation)
def lint_blacklist(self): if not self.config.has_option( self.section, 'check_sql_blacklist') or not self.config.getboolean( self.section, 'check_sql_blacklist'): return True from fuglu.extensions.sql import ENABLED, get_session if not ENABLED: print("SQL Blacklist requested but SQLALCHEMY is not enabled") return False session = get_session( self.config.get(self.section, 'sql_blacklist_dbconnectstring')) suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') conf_sql = self.config.get(self.section, 'sql_blacklist_sql') sql, params = self._replace_sql_params(suspect, conf_sql) try: session.execute(sql, params) print("Blacklist SQL Query OK") return True except Exception as e: print(e) return False
def test_localpartblacklist(self): """test messages from mailer-daemon""" from fuglu.shared import Suspect import email v = Vacation() v.ignoresender = u"" v.awayuser = u'*****@*****.**' v.created = datetime.now() v.start = datetime.now() v.end = v.start + timedelta(days=2) v.subject = u'awaaay' v.body = u'cya' self.session.add(v) self.session.flush() self.session.expunge_all() self.refreshcache() botmsg = """From: [email protected] Subject: mailinglist membership reminder... """ suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') suspect.set_tag('nobounce', True) suspect.set_source(botmsg) candidatevacation = self.candidate.on_vacation(suspect) self.assertTrue(candidatevacation != None, "Vacation object not found in database") self.assertFalse( self.candidate.should_send_vacation_message(suspect), "Test Message should NOT generate vacation reply(automated)")
def test_modified_message(self): """Test if the modified message gets archived correctly""" from fuglu.shared import Suspect import shutil import tempfile tempfilename = tempfile.mktemp(suffix='archive', prefix='fuglu-unittest', dir='/tmp') shutil.copy(TESTDATADIR + '/helloworld.eml', tempfilename) self.tempfiles.append(tempfilename) self.config.set('ArchivePlugin', 'storeoriginal', '0') candidate = ArchivePlugin(self.config) suspect = Suspect('*****@*****.**', '*****@*****.**', tempfilename) origmessage = suspect.get_source() # modify the mesg msgrep = suspect.get_message_rep() msgrep['X-Changed-Something'] = 'Yes' suspect.setMessageRep(msgrep) filename = candidate.archive(suspect) self.assertTrue(filename != None and filename) self.tempfiles.append(filename) archivedmessage = open(filename, 'r').read() self.assertNotEqual(origmessage.strip(), archivedmessage.strip( )), 'Archived message should have stored modified message'
def test_extract_spamstatus(self): """Test if the spam status header gets extracted correctly""" candidate = SAPlugin(self.config) suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') headername = 'X-Spam-Status' headertests = [ # tuple header content, expected spamstatus, expected spam score ('YES', True, None), # _YESNOCAPS_ ('NO', False, None), # _YESNOCAPS_ (' Yes, score=13.37', True, 13.37), # _YESNO_, score=_SCORE_ (' No, score=-2.826', False, -2.826), # _YESNO_, score=_SCORE_ # with test names, bug #24 ("No, score=1.9 required=8.0 tests=BAYES_00,FROM_EQ_TO,TVD_SPACE_RATIO,TVD_SPACE_RATIO_MINFP autolearn=no autolearn_force=no version=3.4.0", False, 1.9), ] for headercontent, expectedspamstatus, expectedscore in headertests: msgrep = Message() msgrep[headername] = Header(headercontent).encode() spamstatus, score, report = candidate._extract_spamstatus( msgrep, headername, suspect) self.assertEqual( spamstatus, expectedspamstatus, "spamstatus should be %s from %s" % (expectedspamstatus, headercontent)) self.assertEqual( score, expectedscore, "spamscore should be %s from %s" % (expectedscore, headercontent))
def get_suspect(self): success = self.sess.getincomingmail() if not success: self.logger.error('incoming smtp transfer did not finish') return None sess = self.sess fromaddr = sess.from_address tempfilename = sess.tempfilename try: suspect = Suspect(fromaddr, sess.recipients, tempfilename, att_cachelimit=self._att_mgr_cachesize, smtp_options=sess.smtpoptions) except ValueError as e: if len(sess.recipients) > 0: toaddr = sess.recipients[0] else: toaddr = '' self.logger.error( 'failed to initialise suspect with from=<%s> to=<%s> : %s' % (fromaddr, toaddr, str(e))) raise return suspect
def test_ignore_sender(self): from fuglu.shared import Suspect v = Vacation() v.ignoresender = u"unittests.fuglu.org [email protected]" v.awayuser = u'*****@*****.**' v.created = datetime.now() v.start = datetime.now() v.end = v.start + timedelta(days=2) v.subject = u'gone for good' v.body = u'outta here' self.session.add(v) self.session.flush() self.session.expunge_all() self.refreshcache() suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') suspect.set_tag('nobounce', True) candidatevacation = self.candidate.on_vacation(suspect) self.assertTrue(candidatevacation != None, "Vacation object not found in database") # TODO had to disable due to sqlalchemy error # Instance <Vacation at 0x2938890> is not bound to a Session; attribute refresh operation cannot proceed #self.assertEqual(v.ignoresender,candidatevacation.ignoresender,"Vacation object did not get ignore list") self.assertTrue( self.candidate.ignore_sender(candidatevacation, suspect), "Test Message should generate vacation reply(ignored sender)") self.assertFalse( self.candidate.should_send_vacation_message(suspect), "Sender on ignorelist, still wants to send message?!")
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_result(self): """Test if EICAR virus is detected and message deleted""" suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') stream = """Date: Mon, 08 Sep 2008 17:33:54 +0200 To: [email protected] From: [email protected] Subject: test eicar attachment X-Mailer: swaks v20061116.0 jetmore.org/john/code/#swaks MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_MIME_BOUNDARY_000_12140" ------=_MIME_BOUNDARY_000_12140 Content-Type: text/plain Eicar test ------=_MIME_BOUNDARY_000_12140 Content-Type: application/octet-stream Content-Transfer-Encoding: BASE64 Content-Disposition: attachment UEsDBAoAAAAAAGQ7WyUjS4psRgAAAEYAAAAJAAAAZWljYXIuY29tWDVPIVAlQEFQWzRcUFpYNTQo UF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCoNClBLAQIU AAoAAAAAAGQ7WyUjS4psRgAAAEYAAAAJAAAAAAAAAAEAIAD/gQAAAABlaWNhci5jb21QSwUGAAAA AAEAAQA3AAAAbQAAAAAA ------=_MIME_BOUNDARY_000_12140--""" suspect.setMessageRep(email.message_from_string(stream)) result = self.candidate.examine(suspect) if type(result) is tuple: result, message = result strresult = actioncode_to_string(result) self.assertEqual(strresult, "DELETE")
def get_suspect(self): success = self.sess.getincomingmail() if not success: self.logger.error('incoming esmtp transfer did not finish') return None sess = self.sess fromaddr = sess.from_address tempfilename = sess.tempfilename try: suspect = Suspect(fromaddr, sess.recipients, tempfilename) except ValueError as e: if len(sess.recipients) > 0: toaddr = sess.recipients[0] else: toaddr = '' self.logger.error( 'failed to initialise suspect with from=<%s> to=<%s> : %s' % (fromaddr, toaddr, str(e))) raise if sess.xforward_helo is not None and sess.xforward_addr is not None and sess.xforward_rdns is not None: suspect.clientinfo = sess.xforward_helo, sess.xforward_addr, sess.xforward_rdns return suspect
def _make_dummy_suspect(self, senderdomain, clientip, helo='foo.example.com'): s = Suspect('sender@%s' % senderdomain, '*****@*****.**', '/dev/null') s.clientinfo = (helo, clientip, 'ptr.example.com') return s
def test_extract_spamstatus_fail(self): """Test correct return if extracting spamstatus fails because of missing header""" candidate = SAPlugin(self.config) suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') headername = 'X-Spam-Status' msgrep = Message() spamstatus, score, report = candidate._extract_spamstatus( msgrep, headername, suspect)
def test_script_normalexit(self): suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') # we expect to find one test script self.assertTrue(len(self.candidate.get_scripts()) == 1) action, message = self.candidate.examine(suspect) self.assertEqual(action, DUNNO) self.assertEqual(message, 'accepted')
def test_bounce(self): """Test bounce message, especially the encoding""" suspect = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') # include non-ascii charset unicode characters to make sure the encoding/decoding # works correctly displayname = u"((testing placeholder for displayname -> äää))" asciirep = u"((testing placeholder for asciirep -> üüü))" description = u"((testing placeholder for description -> ööö))" blockinfo = ("%s %s: %s" % (displayname, asciirep, description)).strip() blockedfiletemplate = os.path.join( *[CONFDIR, "templates", "blockedfile.tmpl.dist"]) bounce = Bounce(self.config) bounce.send_template_file(suspect.from_address, blockedfiletemplate, suspect, dict(blockinfo=blockinfo)) # might be needed to wait for a bit to make sure answer is available counter = 0 while self.smtp.suspect is None and counter < 20: counter = counter + 1 time.sleep(0.05) # sleep is needed to gotback = self.smtp.suspect self.assertFalse(gotback == None, "Did not get message from dummy smtp server") # get message received by dummy smtp server msg = gotback.get_message_rep() receivedMsg = msg.get_payload(decode='utf-8') # Build the message according to what Bounce is doing so it can be compared # to what was received from DummySMTPServer with open(blockedfiletemplate) as fp: templatecontent = fp.read() blockinfo = ("%s %s: %s" % (displayname, asciirep, description)).strip() message = apply_template(templatecontent, suspect, dict(blockinfo=blockinfo)) messageB = force_bString(message) # modify received message to add header parts from template messageToCompare = force_bString("To: " + msg['To'] + "\nSubject: " + msg['Subject'] + "\n\n") + force_bString(receivedMsg) # make sure comparison will not fail because of newlines # For example, Python 2.6 has only one "\n" at the end of the received message, whereas Python 2.7 and 3 have to messageToCompare = messageToCompare.replace(b"\r", b"\n").replace( b"\n\n", b"\n") messageB = messageB.replace(b"\r", b"\n").replace(b"\n\n", b"\n") self.assertEqual(messageB, messageToCompare)
def test_from_to_local_addr(self): """Make sure local senders / recipients are accepted""" s = Suspect('bob@localhost', 'root@localhost', '/dev/null') self.assertEqual("bob@localhost", s.from_address) self.assertEqual("bob", s.from_localpart) self.assertEqual("localhost", s.from_domain) self.assertEqual("root@localhost", s.to_address) self.assertEqual("root", s.to_localpart) self.assertEqual("localhost", s.to_domain)
def test_from_to_parsing(self): s = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') self.assertEqual("*****@*****.**", s.from_address) self.assertEqual("sender", s.from_localpart) self.assertEqual("example.com", s.from_domain) self.assertEqual("*****@*****.**", s.to_address) self.assertEqual("recipient", s.to_localpart) self.assertEqual("example.com", s.to_domain) for sender in (None, ''): s = Suspect(sender, '*****@*****.**', '/dev/null') self.assertEqual("", s.from_address) self.assertEqual("", s.from_localpart) self.assertEqual("", s.from_domain) self.assertEqual("*****@*****.**", s.to_address) self.assertEqual("recipient", s.to_localpart) self.assertEqual("example.com", s.to_domain)
def test_id(self): """Check the length and uniqueness of the generated id""" s = Suspect('*****@*****.**', '*****@*****.**', '/dev/null') known = [] for i in range(10000): suspect_id = s._generate_id() self.assertTrue(suspect_id not in known, 'duplicate id %s generated' % suspect_id) known.append(suspect_id) self.assertEqual(len(suspect_id), 32) for c in suspect_id: self.assertTrue(c in string.hexdigits)
def test_forward_modified_stripped(self): suspect = Suspect('*****@*****.**', '*****@*****.**', TESTDATADIR + '/helloworld.eml') action, message = self.saplugin.examine(suspect) self.assertEqual(DUNNO, action) try: self.assertIsNotNone(suspect.get_tag('SAPlugin.report')) except AttributeError: # Python 2.6 self.assertTrue(suspect.get_tag('SAPlugin.report') is not None)
def _lint_gtube(self): tmpfile = tempfile.mkstemp()[1] with open(tmpfile, 'w') as fh: fh.write(GTUBE) suspect = Suspect('*****@*****.**', '*****@*****.**', tmpfile) data = self.rspamd_json(suspect) os.remove(tmpfile) print(data) return (None not in data)
def test_sf_get_field(self): """Test SuspectFilter field extract""" suspect = Suspect('*****@*****.**', '*****@*****.**', TESTDATADIR + '/helloworld.eml') # additional field tests self.assertEqual( self.candidate.get_field(suspect, 'clienthelo')[0], 'helo1') self.assertEqual( self.candidate.get_field(suspect, 'clientip')[0], '10.0.0.1') self.assertEqual( self.candidate.get_field(suspect, 'clienthostname')[0], 'rdns1')
def test_vacation(self): """Test simple vacation use case""" from fuglu.shared import Suspect v = Vacation() v.ignoresender = "" v.awayuser = u'*****@*****.**' v.created = datetime.now() v.start = datetime.now() v.end = v.start + timedelta(days=2) v.subject = u'awaaay' v.body = u'cya' self.session.add(v) self.session.flush() self.session.expunge_all() self.refreshcache() suspect = Suspect(u'*****@*****.**', '*****@*****.**', '/dev/null') suspect.set_tag('nobounce', True) candidatevacation = self.candidate.on_vacation(suspect) self.assertTrue(candidatevacation != None, "Vacation object not found in database") self.assertTrue(self.candidate.should_send_vacation_message(suspect), "Test Message should generate vacation reply") self.candidate.log_bounce(suspect, candidatevacation) # TODO: had to disable due to sqlalchemy error # Instance <Vacation at 0x2938890> is not bound to a Session; attribute refresh operation cannot proceed #self.assertFalse(self.candidate.should_send_vacation_message(suspect),"2nd test Message should NOT generate vacation reply") suspect2 = Suspect(u'*****@*****.**', '*****@*****.**', '/dev/null') suspect2.set_tag('nobounce', True) candidatevacation = self.candidate.on_vacation(suspect2) self.assertFalse( candidatevacation != None, "There should be no vacation object for this recipient") self.assertFalse(self.candidate.should_send_vacation_message(suspect2), "test Message should NOT generate vacation reply")