enhanced decode-spam-headers.py and added exception handling.
This commit is contained in:
parent
c954e554e6
commit
a4230c5ce8
|
@ -993,46 +993,72 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
self.headers = self.collect(text)
|
self.headers = self.collect(text)
|
||||||
|
|
||||||
self.results['Received - Mail Servers Flow'] = self.testReceived()
|
tests = (
|
||||||
self.results['Extracted IP addresses'] = self.testExtractIP()
|
('Received - Mail Servers Flow', self.testReceived),
|
||||||
self.results['Extracted Domains'] = self.testResolveIntoIP()
|
('Extracted IP addresses', self.testExtractIP),
|
||||||
self.results['Bad Keywords In Headers'] = self.testBadKeywords()
|
('Extracted Domains', self.testResolveIntoIP),
|
||||||
self.results['From Address Analysis'] = self.testFrom()
|
('Bad Keywords In Headers', self.testBadKeywords),
|
||||||
self.results['Subject and Thread Topic Difference'] = self.testSubjecThreadTopic()
|
('From Address Analysis', self.testFrom),
|
||||||
self.results['Authentication-Results'] = self.testAuthenticationResults()
|
('Subject and Thread Topic Difference', self.testSubjecThreadTopic),
|
||||||
self.results['ARC-Authentication-Results'] = self.testARCAuthenticationResults()
|
('Authentication-Results', self.testAuthenticationResults),
|
||||||
self.results['Received-SPF'] = self.testReceivedSPF()
|
('ARC-Authentication-Results', self.testARCAuthenticationResults),
|
||||||
self.results['Mail Client Version'] = self.testXMailer()
|
('Received-SPF', self.testReceivedSPF),
|
||||||
self.results['X-Originating-IP'] = self.testXOriginatingIP()
|
('Mail Client Version', self.testXMailer),
|
||||||
self.results['User-Agent Version'] = self.testUserAgent()
|
('X-Originating-IP', self.testXOriginatingIP),
|
||||||
self.results['X-Forefront-Antispam-Report'] = self.testForefrontAntiSpamReport()
|
('User-Agent Version', self.testUserAgent),
|
||||||
self.results['X-Microsoft-Antispam-Mailbox-Delivery'] = self.testAntispamMailboxDelivery()
|
('X-Forefront-Antispam-Report', self.testForefrontAntiSpamReport),
|
||||||
self.results['X-Microsoft-Antispam Bulk Mail'] = self.testMicrosoftAntiSpam()
|
('X-Microsoft-Antispam-Mailbox-Delivery', self.testAntispamMailboxDelivery),
|
||||||
self.results['X-Exchange-Antispam-Report-CFA-Test'] = self.testAntispamReportCFA()
|
('X-Microsoft-Antispam Bulk Mail', self.testMicrosoftAntiSpam),
|
||||||
self.results['Domain Impersonation'] = self.testDomainImpersonation()
|
('X-Exchange-Antispam-Report-CFA-Test', self.testAntispamReportCFA),
|
||||||
self.results['SpamAssassin Spam Status'] = self.testSpamAssassinSpamStatus()
|
('Domain Impersonation', self.testDomainImpersonation),
|
||||||
self.results['SpamAssassin Spam Level'] = self.testSpamAssassinSpamLevel()
|
('SpamAssassin Spam Status', self.testSpamAssassinSpamStatus),
|
||||||
self.results['SpamAssassin Spam Flag'] = self.testSpamAssassinSpamFlag()
|
('SpamAssassin Spam Level', self.testSpamAssassinSpamLevel),
|
||||||
self.results['SpamAssassin Spam Report'] = self.testSpamAssassinSpamReport()
|
('SpamAssassin Spam Flag', self.testSpamAssassinSpamFlag),
|
||||||
self.results['OVH\'s X-VR-SPAMCAUSE'] = self.testSpamCause()
|
('SpamAssassin Spam Report', self.testSpamAssassinSpamReport),
|
||||||
self.results['OVH\'s X-Ovh-Spam-Reason'] = self.testOvhSpamReason()
|
('OVH\'s X-VR-SPAMCAUSE', self.testSpamCause),
|
||||||
self.results['OVH\'s X-Ovh-Spam-Score'] = self.testOvhSpamScore()
|
('OVH\'s X-Ovh-Spam-Reason', self.testOvhSpamReason),
|
||||||
self.results['X-Virus-Scan'] = self.testXVirusScan()
|
('OVH\'s X-Ovh-Spam-Score', self.testOvhSpamScore),
|
||||||
self.results['X-Spam-Checker-Version'] = self.testXSpamCheckerVersion()
|
('X-Virus-Scan', self.testXVirusScan),
|
||||||
self.results['X-IronPort-AV'] = self.testXIronPortAV()
|
('X-Spam-Checker-Version', self.testXSpamCheckerVersion),
|
||||||
self.results['X-Mimecast-Spam-Score'] = self.testXMimecastSpamScore()
|
('X-IronPort-AV', self.testXIronPortAV),
|
||||||
self.results['Spam Diagnostics Metadata'] = self.testSpamDiagnosticMetadata()
|
('X-Mimecast-Spam-Score', self.testXMimecastSpamScore),
|
||||||
self.results['MS Defender ATP Message Properties'] = self.testATPMessageProperties()
|
('Spam Diagnostics Metadata', self.testSpamDiagnosticMetadata),
|
||||||
self.results['Message Feedback Loop'] = self.testMSFBL()
|
('MS Defender ATP Message Properties', self.testATPMessageProperties),
|
||||||
self.results['End-to-End Latency - Message Delivery Time'] = self.testTransportEndToEndLatency()
|
('Message Feedback Loop', self.testMSFBL),
|
||||||
self.results['X-MS-Oob-TLC-OOBClassifiers'] = self.testTLCOObClasifiers()
|
('End-to-End Latency - Message Delivery Time', self.testTransportEndToEndLatency),
|
||||||
|
('X-MS-Oob-TLC-OOBClassifiers', self.testTLCOObClasifiers),
|
||||||
|
|
||||||
|
('Other unrecognized Spam Related Headers', self.testSpamRelatedHeaders),
|
||||||
|
('Other interesting headers', self.testInterestingHeaders),
|
||||||
|
)
|
||||||
|
|
||||||
|
testsDecodeAll = (
|
||||||
|
('X-Microsoft-Antispam-Message-Info', self.testMicrosoftAntiSpamMessageInfo),
|
||||||
|
('Decoded Mail-encoded header values', self.testDecodeEncodedHeaders),
|
||||||
|
)
|
||||||
|
|
||||||
|
for testName, testFunc in tests:
|
||||||
|
try:
|
||||||
|
logger.dbg(f'Running "{testName}"...')
|
||||||
|
self.results[testName] = testFunc()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.err(f'Test: "{testName}" failed: {e} . Use --debug to show entire stack trace.')
|
||||||
|
|
||||||
|
if options['debug']:
|
||||||
|
raise
|
||||||
|
|
||||||
if self.decode_all:
|
if self.decode_all:
|
||||||
self.results['X-Microsoft-Antispam-Message-Info'] = self.testMicrosoftAntiSpamMessageInfo()
|
for testName, testFunc in tests:
|
||||||
self.results['Decoded Mail-encoded header values'] = self.testDecodeEncodedHeaders()
|
try:
|
||||||
|
logger.dbg(f'Running "{testName}"...')
|
||||||
|
self.results[testName] = testFunc()
|
||||||
|
|
||||||
self.results['Other unrecognized Spam Related Headers'] = self.testSpamRelatedHeaders()
|
except Exception as e:
|
||||||
self.results['Other interesting headers'] = self.testInterestingHeaders()
|
logger.err(f'Test: "{testName}" failed: {e} . Use --debug to show entire stack trace.')
|
||||||
|
|
||||||
|
if options['debug']:
|
||||||
|
raise
|
||||||
|
|
||||||
return {k: v for k, v in self.results.items() if v}
|
return {k: v for k, v in self.results.items() if v}
|
||||||
|
|
||||||
|
@ -1133,9 +1159,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
else:
|
else:
|
||||||
result += f'{self.logger.colored(value, "red")}\n'
|
result += f'{self.logger.colored(value, "red")}\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1171,9 +1194,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
result += f'\t- Subject: {self.logger.colored(v1, "green")}\n'
|
result += f'\t- Subject: {self.logger.colored(v1, "green")}\n'
|
||||||
result += f'\t- Thread-Topic: {self.logger.colored(v2, "magenta")}\n'
|
result += f'\t- Thread-Topic: {self.logger.colored(v2, "magenta")}\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : f'{header1}, {header2}',
|
'header' : f'{header1}, {header2}',
|
||||||
'value': f'\n{header1}:\n\t{value1}\n\n {header2}:\n\t{value2}',
|
'value': f'\n{header1}:\n\t{value1}\n\n {header2}:\n\t{value2}',
|
||||||
|
@ -1191,9 +1211,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
else:
|
else:
|
||||||
result += f' {value}\n'
|
result += f' {value}\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1290,9 +1307,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
pos += 1
|
pos += 1
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1305,9 +1319,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
result = f'- SpamAssassin version.'
|
result = f'- SpamAssassin version.'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1322,9 +1333,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
value = SMTPHeadersAnalysis.flattenLine(value).replace(' ', '').replace('\t', '')
|
value = SMTPHeadersAnalysis.flattenLine(value).replace(' ', '').replace('\t', '')
|
||||||
result += f'Score: {self.logger.colored(value.strip(), "red")}\n'
|
result += f'Score: {self.logger.colored(value.strip(), "red")}\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1345,9 +1353,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
result += tmp + '\n'
|
result += tmp + '\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1555,9 +1560,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
result += '\n'
|
result += '\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1643,9 +1645,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
result += f'\t- Mail\'s domain should resolve to: \t{self.logger.colored(senderDomain, "green")}\n'
|
result += f'\t- Mail\'s domain should resolve to: \t{self.logger.colored(senderDomain, "green")}\n'
|
||||||
result += f'\t- But instead first hop resolved to:\t{self.logger.colored(firstHopDomain1, "red")}\n'
|
result += f'\t- But instead first hop resolved to:\t{self.logger.colored(firstHopDomain1, "red")}\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -1660,11 +1659,13 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
if value.strip().lower() == 'yes':
|
if value.strip().lower() == 'yes':
|
||||||
result = self.logger.colored(f'- SpamAssassin marked this message as SPAM:\n', 'red')
|
result = self.logger.colored(f'- SpamAssassin marked this message as SPAM:\n', 'red')
|
||||||
result += f'\t- ' + self.logger.colored(value, 'red') + '\n'
|
result += f'\t- ' + self.logger.colored(value, 'red') + '\n'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
'analysis' : result
|
'analysis' : result
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def testSpamAssassinSpamLevel(self):
|
def testSpamAssassinSpamLevel(self):
|
||||||
|
@ -1676,15 +1677,14 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
_num = self.logger.colored(str(len(value.strip())), 'red')
|
_num = self.logger.colored(str(len(value.strip())), 'red')
|
||||||
result += f'\t- ' + self.logger.colored(value.strip(), 'red') + f' (number: {_num})\n'
|
result += f'\t- ' + self.logger.colored(value.strip(), 'red') + f' (number: {_num})\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
'analysis' : result
|
'analysis' : result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def testSpamAssassinSpamReport(self):
|
def testSpamAssassinSpamReport(self):
|
||||||
(num, header, value) = self.getHeader('X-Spam-Report')
|
(num, header, value) = self.getHeader('X-Spam-Report')
|
||||||
if num == -1: return []
|
if num == -1: return []
|
||||||
|
@ -1700,15 +1700,14 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
result += tmp + '\n'
|
result += tmp + '\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
'analysis' : result
|
'analysis' : result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def testATPMessageProperties(self):
|
def testATPMessageProperties(self):
|
||||||
(num, header, value) = self.getHeader('X-MS-Exchange-AtpMessageProperties')
|
(num, header, value) = self.getHeader('X-MS-Exchange-AtpMessageProperties')
|
||||||
if num == -1: return []
|
if num == -1: return []
|
||||||
|
@ -1720,9 +1719,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
if prop in SMTPHeadersAnalysis.ATP_Message_Properties.keys():
|
if prop in SMTPHeadersAnalysis.ATP_Message_Properties.keys():
|
||||||
result += f'- ' + self.logger.colored(SMTPHeadersAnalysis.ATP_Message_Properties[prop], 'magenta') + '\n'
|
result += f'- ' + self.logger.colored(SMTPHeadersAnalysis.ATP_Message_Properties[prop], 'magenta') + '\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -2063,9 +2059,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
if addscl:
|
if addscl:
|
||||||
result += tmpfoo
|
result += tmpfoo
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -2157,9 +2150,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
tmp += SMTPHeadersAnalysis.hexdump(base64.b64decode(value))
|
tmp += SMTPHeadersAnalysis.hexdump(base64.b64decode(value))
|
||||||
tmp += '\n\n\n'
|
tmp += '\n\n\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': '...',
|
'value': '...',
|
||||||
|
@ -2234,9 +2224,6 @@ Results will be unsound. Make sure you have pasted your headers with correct spa
|
||||||
|
|
||||||
result += tmp + '\n'
|
result += tmp + '\n'
|
||||||
|
|
||||||
if len(result) == 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'header' : header,
|
'header' : header,
|
||||||
'value': value,
|
'value': value,
|
||||||
|
@ -2617,5 +2604,14 @@ def main(argv):
|
||||||
else:
|
else:
|
||||||
print(output)
|
print(output)
|
||||||
|
|
||||||
|
if not options['nocolor']:
|
||||||
|
print('''
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
Experiencing a bad-looking output with unprintable characters?
|
||||||
|
Use -N flag to disable console colors, or switch your console for better UI experience.
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
|
|
Loading…
Reference in New Issue