Antispam reputation and composite rules
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
if eval "!t.SPAM_TRAP && !t.TRUSTED_REPLY" {
|
||||
let "bayes_result" "bayes_classify('spamdb/bayes-classify', body_and_subject)";
|
||||
let "bayes_result" "bayes_classify('spamdb/token-lookup', body_and_subject)";
|
||||
if eval "!is_empty(bayes_result)" {
|
||||
if eval "bayes_result > 0.7" {
|
||||
let "t.BAYES_SPAM" "1";
|
||||
|
||||
75
resources/config/sieve/composites.sieve
Normal file
75
resources/config/sieve/composites.sieve
Normal file
@@ -0,0 +1,75 @@
|
||||
if eval "t.FORGED_RECIPIENTS && t.MAILLIST" {
|
||||
let "t.FORGED_RECIPIENTS_MAILLIST" "1";
|
||||
}
|
||||
|
||||
if eval "t.FORGED_SENDER && t.MAILLIST" {
|
||||
let "t.FORGED_SENDER_MAILLIST" "1";
|
||||
}
|
||||
|
||||
if eval "t.DMARC_POLICY_ALLOW && (t.R_SPF_SOFTFAIL || t.R_SPF_FAIL || t.R_DKIM_REJECT)" {
|
||||
let "t.DMARC_POLICY_ALLOW_WITH_FAILURES" "1";
|
||||
}
|
||||
|
||||
if eval "t.R_DKIM_NA && t.R_SPF_NA && t.DMARC_NA && t.ARC_NA" {
|
||||
let "t.AUTH_NA" "1";
|
||||
}
|
||||
|
||||
if eval "!(t.R_DKIM_NA && t.R_SPF_NA && t.DMARC_NA && t.ARC_NA) && (t.R_DKIM_NA || t.R_DKIM_TEMPFAIL || t.R_DKIM_PERMFAIL) && (t.R_SPF_NA || t.R_SPF_DNSFAIL) && t.DMARC_NA && (t.ARC_NA || t.ARC_DNSFAIL)" {
|
||||
let "t.AUTH_NA_OR_FAIL" "1";
|
||||
}
|
||||
|
||||
if eval "(t.AUTH_NA || t.AUTH_NA_OR_FAIL) && (t.BOUNCE || t.SUBJ_BOUNCE_WORDS)" {
|
||||
let "t.BOUNCE_NO_AUTH" "1";
|
||||
}
|
||||
|
||||
if eval "(t.HAS_X_POS || t.HAS_PHPMAILER_SIG) && t.HAS_WP_URI && (t.PHISHING || t.CRACKED_SURBL || t.PH_SURBL_MULTI || t.DBL_PHISH || t.DBL_ABUSE_PHISH || t.URIBL_BLACK || t.PHISHED_OPENPHISH || t.PHISHED_PHISHTANK)" {
|
||||
let "t.HACKED_WP_PHISHING" "1";
|
||||
}
|
||||
|
||||
if eval "(t.HAS_XOIP || t.RCVD_FROM_SMTP_AUTH) && t.DCC_BULK" {
|
||||
let "t.COMPROMISED_ACCT_BULK" "1";
|
||||
}
|
||||
|
||||
if eval "t.DCC_BULK && (t.MISSING_TO || t.R_UNDISC_RCPT)" {
|
||||
let "t.UNDISC_RCPTS_BULK" "1";
|
||||
}
|
||||
|
||||
if eval "t.RECEIVED_SPAMHAUS_PBL && !t.RCVD_VIA_SMTP_AUTH" {
|
||||
let "t.RCVD_UNAUTH_PBL" "1";
|
||||
}
|
||||
|
||||
if eval "(t.R_DKIM_ALLOW || t.ARC_ALLOW) && t.RCVD_IN_DNSWL_MED" {
|
||||
let "t.RCVD_DKIM_ARC_DNSWL_MED" "1";
|
||||
}
|
||||
|
||||
if eval "(t.R_DKIM_ALLOW || t.ARC_ALLOW) && t.RCVD_IN_DNSWL_HI" {
|
||||
let "t.RCVD_DKIM_ARC_DNSWL_HI" "1";
|
||||
}
|
||||
|
||||
if eval "(t.HAS_X_POS || t.HAS_PHPMAILER_SIG || t.HAS_X_PHP_SCRIPT) && (t.SUBJECT_ENDS_QUESTION || t.SUBJECT_ENDS_EXCLAIM || t.MANY_INVISIBLE_PARTS)" {
|
||||
let "t.AUTOGEN_PHP_SPAMMY" "1";
|
||||
}
|
||||
|
||||
if eval "(t.PHISHING || t.DBL_PHISH || t.PHISHED_OPENPHISH || t.PHISHED_PHISHTANK) && (t.SUBJECT_ENDS_QUESTION || t.SUBJECT_ENDS_EXCLAIM)" {
|
||||
let "t.PHISH_EMOTION" "1";
|
||||
}
|
||||
|
||||
if eval "t.HAS_GUC_PROXY_URI || t.URIBL_RED || t.DBL_ABUSE_REDIR || t.HAS_ONION_URI" {
|
||||
let "t.HAS_ANON_DOMAIN" "1";
|
||||
}
|
||||
|
||||
if eval "(t.R_SPF_FAIL || t.R_SPF_SOFTFAIL) && (t.RCVD_COUNT_ZERO || t.RCVD_NO_TLS_LAST)" {
|
||||
let "t.VIOLATED_DIRECT_SPF" "1";
|
||||
}
|
||||
|
||||
if eval "(t.FREEMAIL_FROM || t.FREEMAIL_ENVFROM || t.FREEMAIL_REPLYTO) && (t.TO_DN_RECIPIENTS || t.R_UNDISC_RCPT) && (t.FROM_NAME_HAS_TITLE || t.FREEMAIL_REPLYTO_NEQ_FROM_DOM)" {
|
||||
let "t.FREEMAIL_AFF" "1";
|
||||
}
|
||||
|
||||
if eval "t.HFILTER_URL_ONLY && t.REDIRECTOR_URL" {
|
||||
let "t.REDIRECTOR_URL_ONLY" "1";
|
||||
}
|
||||
|
||||
if eval "t.FAKE_REPLY && t.RCVD_VIA_SMTP_AUTH && (!t.RECEIVED_SPAMHAUS_PBL || t.RECEIVED_SPAMHAUS_XBL || t.RECEIVED_SPAMHAUS_SBL)" {
|
||||
let "t.THREAD_HIJACKING_FROM_INJECTOR" "1";
|
||||
}
|
||||
@@ -103,6 +103,9 @@ if eval "header.X-PHP-Script.exists" {
|
||||
let "t.HIDDEN_SOURCE_OBJ" "1";
|
||||
}
|
||||
}
|
||||
if eval "contains_ignore_case(header.X-Mailer, 'PHPMailer')" {
|
||||
let "t.HAS_PHPMAILER_SIG" "1";
|
||||
}
|
||||
if eval "header.X-Source:X-Source-Args:X-Source-Dir.exists" {
|
||||
let "t.HAS_X_SOURCE" "1";
|
||||
if eval "contains(header.X-Source-Args, '../')" {
|
||||
|
||||
@@ -6,17 +6,3 @@ if eval "env.iprev.result != ''" {
|
||||
let "t.RDNS_NONE" "1";
|
||||
}
|
||||
}
|
||||
|
||||
# Lookup ASN
|
||||
let "asn_lookup" "";
|
||||
if eval "len(env.remote_ip.reverse) <= 15" {
|
||||
let "asn_lookup" "env.remote_ip.reverse + '.origin.asn.cymru.com'";
|
||||
} else {
|
||||
let "asn_lookup" "env.remote_ip.reverse + '.origin.asn6.cymru.com'";
|
||||
}
|
||||
let "asn_lookup" "split(dns_query(asn_lookup, 'txt'), '|')";
|
||||
let "asn" "asn_lookup[0]";
|
||||
let "country" "asn_lookup[2]";
|
||||
|
||||
#eval "print('ASN: ' + asn + ' (' + country + ')')";
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@ if eval "!is_empty(message_id)" {
|
||||
eval "lookup('spamdb/id-insert', message_id)";
|
||||
|
||||
if eval "lookup('spam/options', 'AUTOLEARN_REPLIES')" {
|
||||
eval "bayes_train('spamdb/bayes-train', thread_name(header.subject) + ' ' + body.to_text, false)";
|
||||
eval "bayes_train('spamdb/token-insert', thread_name(header.subject) + ' ' + body.to_text, false)";
|
||||
}
|
||||
}
|
||||
|
||||
73
resources/config/sieve/reputation.sieve
Normal file
73
resources/config/sieve/reputation.sieve
Normal file
@@ -0,0 +1,73 @@
|
||||
# Obtain sender address and domain
|
||||
let "rep_from" "envelope.from";
|
||||
let "rep_from_domain" "envfrom_domain_sld";
|
||||
if eval "is_empty(rep_from)" {
|
||||
let "rep_from" "from_addr";
|
||||
let "rep_from_domain" "from_domain_sld";
|
||||
}
|
||||
if eval "env.dmarc.result != 'pass'" {
|
||||
# Do not penalize forged domains
|
||||
let "rep_from" "'_' + rep_from";
|
||||
let "rep_from_domain" "'_' + rep_from_domain";
|
||||
}
|
||||
|
||||
# Lookup ASN
|
||||
let "asn" "";
|
||||
if eval "len(env.remote_ip.reverse) <= 15" {
|
||||
let "asn" "env.remote_ip.reverse + '.origin.asn.cymru.com'";
|
||||
} else {
|
||||
let "asn" "env.remote_ip.reverse + '.origin.asn6.cymru.com'";
|
||||
}
|
||||
let "asn" "split(dns_query(asn, 'txt'), '|')[0]";
|
||||
|
||||
# Generate reputation tokens
|
||||
let "token_ids" "";
|
||||
if eval "asn > 0" {
|
||||
let "token_ids" "['i:' + env.remote_ip, 'f:' + rep_from, 'd:' + rep_from_domain, 'a:' + asn ]";
|
||||
} else {
|
||||
let "token_ids" "['i:' + env.remote_ip, 'f:' + rep_from, 'd:' + rep_from_domain ]";
|
||||
}
|
||||
|
||||
# Lookup reputation
|
||||
let "i" "len(token_ids)";
|
||||
let "reputation" "0.0";
|
||||
|
||||
while "i > 0" {
|
||||
let "i" "i - 1";
|
||||
let "token_id" "token_ids[i]";
|
||||
|
||||
# Lookup reputation
|
||||
let "token_rep" "lookup_map('spamdb/reputation-lookup', token_id)";
|
||||
|
||||
# Update reputation
|
||||
eval "lookup_map('spamdb/reputation-insert', [token_id, score])";
|
||||
|
||||
if eval "is_empty(token_rep)" {
|
||||
continue;
|
||||
}
|
||||
|
||||
# Assign weight
|
||||
let "weight" "";
|
||||
if eval "starts_with(token_id, 'f:')" {
|
||||
# Sender address has 50% weight
|
||||
let "weight" "0.5";
|
||||
} elsif eval "starts_with(token_id, 'd:')" {
|
||||
# Sender domain has 20% weight
|
||||
let "weight" "0.2";
|
||||
} elsif eval "starts_with(token_id, 'i:')" {
|
||||
# IP has 20% weight
|
||||
let "weight" "0.2";
|
||||
} elsif eval "starts_with(token_id, 'a:')" {
|
||||
# ASN has 10% weight
|
||||
let "weight" "0.1";
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let "reputation" "reputation + (token_rep[0] / token_rep[1] * weight)";
|
||||
}
|
||||
|
||||
# Adjust score using a 0.5 factor
|
||||
if eval "reputation > 0" {
|
||||
let "score" "score + (reputation - score) * 0.5";
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Check if the message was sent to a spam trap address
|
||||
if eval "lookup('spam/trap-address', envelope.to)" {
|
||||
eval "bayes_train('spamdb/bayes-train', body_and_subject, true)";
|
||||
eval "bayes_train('spamdb/token-insert', body_and_subject, true)";
|
||||
let "t.SPAM_TRAP" "1";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user