aboutsummaryrefslogtreecommitdiffstats
path: root/rules/misc.lua
diff options
context:
space:
mode:
Diffstat (limited to 'rules/misc.lua')
-rw-r--r--rules/misc.lua199
1 files changed, 121 insertions, 78 deletions
diff --git a/rules/misc.lua b/rules/misc.lua
index b27a1bc53..17e3b8ac7 100644
--- a/rules/misc.lua
+++ b/rules/misc.lua
@@ -47,7 +47,7 @@ rspamd_config.R_PARTS_DIFFER = {
score = (nd - 0.5)
end
task:insert_result('R_PARTS_DIFFER', score,
- string.format('%.1f%%', tostring(100.0 * nd)))
+ string.format('%.1f%%', tostring(100.0 * nd)))
end
end
end
@@ -75,15 +75,15 @@ local date_id = rspamd_config:register_symbol({
return
end
- local dt = task:get_date({format = 'connect', gmt = true})
+ local dt = task:get_date({ format = 'connect', gmt = true })
local date_diff = dt - dm
if date_diff > 86400 then
-- Older than a day
- task:insert_result('DATE_IN_PAST', 1.0, tostring(math.floor(date_diff/3600)))
+ task:insert_result('DATE_IN_PAST', 1.0, tostring(math.floor(date_diff / 3600)))
elseif -date_diff > 7200 then
-- More than 2 hours in the future
- task:insert_result('DATE_IN_FUTURE', 1.0, tostring(math.floor(-date_diff/3600)))
+ task:insert_result('DATE_IN_FUTURE', 1.0, tostring(math.floor(-date_diff / 3600)))
end
end
})
@@ -124,15 +124,15 @@ rspamd_config:register_symbol({
parent = date_id,
})
-local obscured_id = rspamd_config:register_symbol{
+local obscured_id = rspamd_config:register_symbol {
callback = function(task)
- local susp_urls = task:get_urls_filtered({ 'obscured', 'zw_spaces'})
+ local susp_urls = task:get_urls_filtered({ 'obscured', 'zw_spaces' })
if susp_urls and susp_urls[1] then
local obs_flag = url_flags_tab.obscured
local zw_flag = url_flags_tab.zw_spaces
- for _,u in ipairs(susp_urls) do
+ for _, u in ipairs(susp_urls) do
local fl = u:get_flags_num()
if bit.band(fl, obs_flag) ~= 0 then
task:insert_result('R_SUSPICIOUS_URL', 1.0, u:get_host())
@@ -152,7 +152,7 @@ local obscured_id = rspamd_config:register_symbol{
group = 'url'
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
name = 'ZERO_WIDTH_SPACE_URL',
score = 7.0,
@@ -162,9 +162,8 @@ rspamd_config:register_symbol{
parent = obscured_id,
}
-
rspamd_config.ENVFROM_PRVS = {
- callback = function (task)
+ callback = function(task)
--[[
Detect PRVS/BATV addresses to avoid FORGED_SENDER
https://en.wikipedia.org/wiki/Bounce_Address_Tag_Validation
@@ -183,7 +182,9 @@ rspamd_config.ENVFROM_PRVS = {
local re_text = '^(?:(prvs|msprvs1)=([^=]+)=|btv1==[^=]+==)(.+@(.+))$'
local re = rspamd_regexp.create_cached(re_text)
local c = re:search(envfrom[1].addr:lower(), false, true)
- if not c then return false end
+ if not c then
+ return false
+ end
local ef = c[1][4]
-- See if it matches the From header
local from = task:get_from(2)
@@ -207,23 +208,25 @@ rspamd_config.ENVFROM_PRVS = {
}
rspamd_config.ENVFROM_VERP = {
- callback = function (task)
+ callback = function(task)
if not (task:has_from(1) and task:has_recipients(1)) then
return false
end
local envfrom = task:get_from(1)
local envrcpts = task:get_recipients(1)
-- VERP only works for single recipient messages
- if #envrcpts > 1 then return false end
+ if #envrcpts > 1 then
+ return false
+ end
-- Get recipient and compute VERP address
local rcpt = envrcpts[1].addr:lower()
- local verp = rcpt:gsub('@','=')
+ local verp = rcpt:gsub('@', '=')
-- Get the user portion of the envfrom
local ef_user = envfrom[1].user:lower()
-- See if the VERP representation of the recipient appears in it
if ef_user:find(verp, 1, true)
- and not ef_user:find('+caf_=' .. verp, 1, true) -- Google Forwarding
- and not ef_user:find('^srs[01]=') -- SRS
+ and not ef_user:find('+caf_=' .. verp, 1, true) -- Google Forwarding
+ and not ef_user:find('^srs[01]=') -- SRS
then
return true
end
@@ -235,12 +238,14 @@ rspamd_config.ENVFROM_VERP = {
type = 'mime',
}
-local check_rcvd = rspamd_config:register_symbol{
+local check_rcvd = rspamd_config:register_symbol {
name = 'CHECK_RCVD',
group = 'headers',
- callback = function (task)
+ callback = function(task)
local rcvds = task:get_received_headers()
- if not rcvds or #rcvds == 0 then return false end
+ if not rcvds or #rcvds == 0 then
+ return false
+ end
local all_tls = fun.all(function(rc)
return rc.flags and rc.flags['ssl']
@@ -275,7 +280,7 @@ local check_rcvd = rspamd_config:register_symbol{
type = 'callback,mime',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_rcvd,
name = 'RCVD_TLS_ALL',
@@ -284,7 +289,7 @@ rspamd_config:register_symbol{
group = 'headers'
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_rcvd,
name = 'RCVD_TLS_LAST',
@@ -293,7 +298,7 @@ rspamd_config:register_symbol{
group = 'headers'
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_rcvd,
name = 'RCVD_NO_TLS_LAST',
@@ -302,7 +307,7 @@ rspamd_config:register_symbol{
group = 'headers'
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_rcvd,
name = 'RCVD_VIA_SMTP_AUTH',
@@ -313,7 +318,7 @@ rspamd_config:register_symbol{
}
rspamd_config.RCVD_HELO_USER = {
- callback = function (task)
+ callback = function(task)
-- Check HELO argument from MTA
local helo = task:get_helo()
if (helo and helo:lower():find('^user$')) then
@@ -321,11 +326,17 @@ rspamd_config.RCVD_HELO_USER = {
end
-- Check Received headers
local rcvds = task:get_header_full('Received')
- if not rcvds then return false end
+ if not rcvds then
+ return false
+ end
for _, rcvd in ipairs(rcvds) do
local r = rcvd['decoded']:lower()
- if (r:find("^%s*from%suser%s")) then return true end
- if (r:find("helo[%s=]user[%s%)]")) then return true end
+ if (r:find("^%s*from%suser%s")) then
+ return true
+ end
+ if (r:find("helo[%s=]user[%s%)]")) then
+ return true
+ end
end
end,
description = 'HELO User spam pattern',
@@ -335,11 +346,13 @@ rspamd_config.RCVD_HELO_USER = {
}
rspamd_config.URI_COUNT_ODD = {
- callback = function (task)
+ callback = function(task)
local ct = task:get_header('Content-Type')
if (ct and ct:lower():find('^multipart/alternative')) then
- local urls = task:get_urls_filtered(nil, {'subject', 'html_displayed', 'special'}) or {}
- local nurls = fun.foldl(function(acc, val) return acc + val:get_count() end, 0, urls)
+ local urls = task:get_urls_filtered(nil, { 'subject', 'html_displayed', 'special' }) or {}
+ local nurls = fun.foldl(function(acc, val)
+ return acc + val:get_count()
+ end, 0, urls)
if nurls % 2 == 1 then
return true, 1.0, tostring(nurls)
@@ -352,7 +365,7 @@ rspamd_config.URI_COUNT_ODD = {
}
rspamd_config.HAS_ATTACHMENT = {
- callback = function (task)
+ callback = function(task)
local parts = task:get_parts()
if parts and #parts > 1 then
for _, p in ipairs(parts) do
@@ -376,7 +389,7 @@ local function freemail_reply_neq_from(task)
local ff = task:get_symbol('FREEMAIL_FROM')
local frt_opts = frt[1]['options']
local ff_opts = ff[1]['options']
- return ( frt_opts and ff_opts and frt_opts[1] ~= ff_opts[1] )
+ return (frt_opts and ff_opts and frt_opts[1] ~= ff_opts[1])
end
rspamd_config:register_symbol({
@@ -404,7 +417,8 @@ rspamd_config.OMOGRAPH_URL = {
local h1 = u:get_host()
local h2 = u:get_phished()
- if h2 then -- Due to changes of the phished flag in 2.8
+ if h2 then
+ -- Due to changes of the phished flag in 2.8
h2 = h2:get_host()
end
if h1 and h2 then
@@ -448,20 +462,20 @@ rspamd_config.URL_IN_SUBJECT = {
local urls = task:get_urls()
if urls then
- for _,u in ipairs(urls) do
+ for _, u in ipairs(urls) do
local flags = u:get_flags()
if flags.subject then
if flags.schemaless then
- return true,0.1,u:get_host()
+ return true, 0.1, u:get_host()
end
local subject = task:get_subject()
if subject then
if tostring(u) == subject then
- return true,1.0,u:get_host()
+ return true, 1.0, u:get_host()
end
end
- return true,0.25,u:get_host()
+ return true, 0.25, u:get_host()
end
end
end
@@ -474,18 +488,20 @@ rspamd_config.URL_IN_SUBJECT = {
description = 'Subject contains URL'
}
-local aliases_id = rspamd_config:register_symbol{
+local aliases_id = rspamd_config:register_symbol {
type = 'prefilter',
name = 'EMAIL_PLUS_ALIASES',
callback = function(task)
local function check_from(type)
if task:has_from(type) then
local addr = task:get_from(type)[1]
- local na,tags = lua_util.remove_email_aliases(addr)
+ local na, tags = lua_util.remove_email_aliases(addr)
if na then
task:set_from(type, addr, 'alias')
task:insert_result('TAGGED_FROM', 1.0, fun.totable(
- fun.filter(function(t) return t and #t > 0 end, tags)))
+ fun.filter(function(t)
+ return t and #t > 0
+ end, tags)))
end
end
end
@@ -500,11 +516,15 @@ local aliases_id = rspamd_config:register_symbol{
local addrs = task:get_recipients(type)
for _, addr in ipairs(addrs) do
- local na,tags = lua_util.remove_email_aliases(addr)
+ local na, tags = lua_util.remove_email_aliases(addr)
if na then
modified = true
- fun.each(function(t) table.insert(all_tags, t) end,
- fun.filter(function(t) return t and #t > 0 end, tags))
+ fun.each(function(t)
+ table.insert(all_tags, t)
+ end,
+ fun.filter(function(t)
+ return t and #t > 0
+ end, tags))
end
end
@@ -523,7 +543,7 @@ local aliases_id = rspamd_config:register_symbol{
group = 'headers',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = aliases_id,
name = 'TAGGED_RCPT',
@@ -531,7 +551,7 @@ rspamd_config:register_symbol{
group = 'headers',
score = 0.0,
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = aliases_id,
name = 'TAGGED_FROM',
@@ -540,18 +560,26 @@ rspamd_config:register_symbol{
score = 0.0,
}
-local check_from_display_name = rspamd_config:register_symbol{
+local check_from_display_name = rspamd_config:register_symbol {
type = 'callback,mime',
name = 'FROM_DISPLAY_CALLBACK',
- callback = function (task)
+ callback = function(task)
local from = task:get_from(2)
- if not (from and from[1] and from[1].name) then return false end
+ if not (from and from[1] and from[1].name) then
+ return false
+ end
-- See if we can parse an email address from the name
local parsed = rspamd_parsers.parse_mail_address(from[1].name, task:get_mempool())
- if not parsed then return false end
- if not (parsed[1] and parsed[1]['addr']) then return false end
+ if not parsed then
+ return false
+ end
+ if not (parsed[1] and parsed[1]['addr']) then
+ return false
+ end
-- Make sure we did not mistake e.g. <something>@<name> for an email address
- if not parsed[1]['domain'] or not parsed[1]['domain']:find('%.') then return false end
+ if not parsed[1]['domain'] or not parsed[1]['domain']:find('%.') then
+ return false
+ end
-- See if the parsed domains differ
if not rspamd_util.strequal_caseless(from[1]['domain'], parsed[1]['domain']) then
-- See if the destination domain is the same as the spoof
@@ -580,7 +608,7 @@ local check_from_display_name = rspamd_config:register_symbol{
group = 'headers',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_from_display_name,
name = 'SPOOF_DISPLAY_NAME',
@@ -589,7 +617,7 @@ rspamd_config:register_symbol{
score = 8.0,
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_from_display_name,
name = 'FROM_NEQ_DISPLAY_NAME',
@@ -599,15 +627,19 @@ rspamd_config:register_symbol{
}
rspamd_config.SPOOF_REPLYTO = {
- callback = function (task)
+ callback = function(task)
-- First check for a Reply-To header
local rt = task:get_header_full('Reply-To')
- if not rt or not rt[1] then return false end
+ if not rt or not rt[1] then
+ return false
+ end
-- Get From and To headers
rt = rt[1]['value']
local from = task:get_from(2)
local to = task:get_recipients(2)
- if not (from and from[1] and from[1].addr) then return false end
+ if not (from and from[1] and from[1].addr) then
+ return false
+ end
if (to and to[1] and to[1].addr) then
-- Handle common case for Web Contact forms of From = To
if rspamd_util.strequal_caseless(from[1].addr, to[1].addr) then
@@ -616,9 +648,13 @@ rspamd_config.SPOOF_REPLYTO = {
end
-- SMTP recipients must contain From domain
to = task:get_recipients(1)
- if not to then return false end
+ if not to then
+ return false
+ end
-- Try mitigate some possible FPs on mailing list posts
- if #to == 1 and rspamd_util.strequal_caseless(to[1].addr, from[1].addr) then return false end
+ if #to == 1 and rspamd_util.strequal_caseless(to[1].addr, from[1].addr) then
+ return false
+ end
local found_fromdom = false
for _, t in ipairs(to) do
if rspamd_util.strequal_caseless(t.domain, from[1].domain) then
@@ -626,10 +662,14 @@ rspamd_config.SPOOF_REPLYTO = {
break
end
end
- if not found_fromdom then return false end
+ if not found_fromdom then
+ return false
+ end
-- Parse Reply-To header
local parsed = ((rspamd_parsers.parse_mail_address(rt, task:get_mempool()) or E)[1] or E).domain
- if not parsed then return false end
+ if not parsed then
+ return false
+ end
-- Reply-To domain must be different to From domain
if not rspamd_util.strequal_caseless(parsed, from[1].domain) then
return true, from[1].domain, parsed
@@ -652,14 +692,18 @@ rspamd_config.INFO_TO_INFO_LU = {
return false
end
local to = task:get_recipients('smtp')
- if not to then return false end
+ if not to then
+ return false
+ end
local found = false
- for _,r in ipairs(to) do
+ for _, r in ipairs(to) do
if rspamd_util.strequal_caseless(r['user'], 'info') then
found = true
end
end
- if found then return true end
+ if found then
+ return true
+ end
return false
end,
description = 'info@ From/To address with List-Unsubscribe headers',
@@ -674,12 +718,12 @@ rspamd_config.R_BAD_CTE_7BIT = {
callback = function(task)
local tp = task:get_text_parts() or {}
- for _,p in ipairs(tp) do
+ for _, p in ipairs(tp) do
local cte = p:get_mimepart():get_cte() or ''
if cte ~= '8bit' and p:has_8bit_raw() then
- local _,_,attrs = p:get_mimepart():get_type_full()
+ local _, _, attrs = p:get_mimepart():get_type_full()
local mul = 1.0
- local params = {cte}
+ local params = { cte }
if attrs then
if attrs.charset and attrs.charset:lower() == "utf-8" then
-- Penalise rule as people don't know that utf8 is surprisingly
@@ -689,7 +733,7 @@ rspamd_config.R_BAD_CTE_7BIT = {
end
end
- return true,mul,params
+ return true, mul, params
end
end
@@ -701,8 +745,7 @@ rspamd_config.R_BAD_CTE_7BIT = {
type = 'mime',
}
-
-local check_encrypted_name = rspamd_config:register_symbol{
+local check_encrypted_name = rspamd_config:register_symbol {
name = 'BOGUS_ENCRYPTED_AND_TEXT',
callback = function(task)
local parts = task:get_parts() or {}
@@ -714,14 +757,14 @@ local check_encrypted_name = rspamd_config:register_symbol{
local children = part:get_children() or {}
local text_kids = {}
- for _,cld in ipairs(children) do
+ for _, cld in ipairs(children) do
if cld:is_multipart() then
check_part(cld)
elseif cld:is_text() then
seen_text = true
text_kids[#text_kids + 1] = cld
else
- local type,subtype,_ = cld:get_type_full()
+ local type, subtype, _ = cld:get_type_full()
if type:lower() == 'application' then
if string.find(subtype:lower(), 'pkcs7%-mime') then
@@ -743,8 +786,8 @@ local check_encrypted_name = rspamd_config:register_symbol{
end
if seen_text and seen_encrypted then
-- Ensure that our seen text is not really part of pgp #3205
- for _,tp in ipairs(text_kids) do
- local t,_ = tp:get_type()
+ for _, tp in ipairs(text_kids) do
+ local t, _ = tp:get_type()
seen_text = false -- reset temporary
if t and t == 'text' then
seen_text = true
@@ -756,7 +799,7 @@ local check_encrypted_name = rspamd_config:register_symbol{
end
end
- for _,part in ipairs(parts) do
+ for _, part in ipairs(parts) do
check_part(part)
end
@@ -771,7 +814,7 @@ local check_encrypted_name = rspamd_config:register_symbol{
group = 'mime_types',
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_encrypted_name,
name = 'ENCRYPTED_PGP',
@@ -781,7 +824,7 @@ rspamd_config:register_symbol{
one_shot = true
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_encrypted_name,
name = 'ENCRYPTED_SMIME',
@@ -791,7 +834,7 @@ rspamd_config:register_symbol{
one_shot = true
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_encrypted_name,
name = 'SIGNED_PGP',
@@ -801,7 +844,7 @@ rspamd_config:register_symbol{
one_shot = true
}
-rspamd_config:register_symbol{
+rspamd_config:register_symbol {
type = 'virtual',
parent = check_encrypted_name,
name = 'SIGNED_SMIME',