diff options
author | Vsevolod Stakhov <vsevolod@rspamd.com> | 2023-08-07 11:41:28 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@rspamd.com> | 2023-08-07 11:41:28 +0100 |
commit | 662145d0554de5e769b92dab2d41173a98adcee5 (patch) | |
tree | ec28311a0bce6181f248ba7b50304293ad764e44 /rules | |
parent | bbd88232db43d18f5e0de5a6502848d4074621c5 (diff) | |
download | rspamd-662145d0554de5e769b92dab2d41173a98adcee5.tar.gz rspamd-662145d0554de5e769b92dab2d41173a98adcee5.zip |
[Minor] Reformat all Lua code, no functional changes
Diffstat (limited to 'rules')
-rw-r--r-- | rules/bitcoin.lua | 25 | ||||
-rw-r--r-- | rules/bounce.lua | 9 | ||||
-rw-r--r-- | rules/content.lua | 32 | ||||
-rw-r--r-- | rules/controller/fuzzy.lua | 6 | ||||
-rw-r--r-- | rules/controller/init.lua | 6 | ||||
-rw-r--r-- | rules/controller/maps.lua | 28 | ||||
-rw-r--r-- | rules/controller/neural.lua | 4 | ||||
-rw-r--r-- | rules/controller/selectors.lua | 4 | ||||
-rw-r--r-- | rules/forwarding.lua | 34 | ||||
-rw-r--r-- | rules/headers_checks.lua | 248 | ||||
-rw-r--r-- | rules/html.lua | 101 | ||||
-rw-r--r-- | rules/mid.lua | 18 | ||||
-rw-r--r-- | rules/misc.lua | 199 | ||||
-rw-r--r-- | rules/regexp/compromised_hosts.lua | 50 | ||||
-rw-r--r-- | rules/regexp/headers.lua | 91 | ||||
-rw-r--r-- | rules/regexp/misc.lua | 10 | ||||
-rw-r--r-- | rules/regexp/upstream_spam_filters.lua | 4 | ||||
-rw-r--r-- | rules/rspamd.lua | 10 | ||||
-rw-r--r-- | rules/subject_checks.lua | 8 |
19 files changed, 520 insertions, 367 deletions
diff --git a/rules/bitcoin.lua b/rules/bitcoin.lua index 1c902e93e..6a70721f8 100644 --- a/rules/bitcoin.lua +++ b/rules/bitcoin.lua @@ -26,7 +26,7 @@ local off = 0 local base58_dec = fun.tomap(fun.map( function(c) off = off + 1 - return c,(off - 1) + return c, (off - 1) end, "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")) @@ -34,11 +34,13 @@ local function is_traditional_btc_address(word) local hash = require "rspamd_cryptobox_hash" local bytes = {} - for i=1,25 do bytes[i] = 0 end + for i = 1, 25 do + bytes[i] = 0 + end -- Base58 decode loop fun.each(function(ch) local acc = base58_dec[ch] or 0 - for i=25,1,-1 do + for i = 25, 1, -1 do acc = acc + (58 * bytes[i]); bytes[i] = acc % 256 acc = math.floor(acc / 256); @@ -46,14 +48,14 @@ local function is_traditional_btc_address(word) end, word) -- Now create a validation tag local sha256 = hash.create_specific('sha256') - for i=1,21 do + for i = 1, 21 do sha256:update(string.char(bytes[i])) end sha256 = hash.create_specific('sha256', sha256:bin()):bin() -- Compare tags local valid = true - for i=1,4 do + for i = 1, 4 do if string.sub(sha256, i, i) ~= string.char(bytes[21 + i]) then valid = false end @@ -65,13 +67,13 @@ end -- Beach32 checksum combiner local function polymod(...) local chk = 1; - local gen = {0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}; - for _,t in ipairs({...}) do - for _,v in ipairs(t) do + local gen = { 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3 }; + for _, t in ipairs({ ... }) do + for _, v in ipairs(t) do local top = bit.rshift(chk, 25) chk = bit.bxor(bit.lshift(bit.band(chk, 0x1ffffff), 5), v) - for i=1,5 do + for i = 1, 5 do if bit.band(bit.rshift(top, i - 1), 0x1) ~= 0 then chk = bit.bxor(chk, gen[i]) end @@ -100,7 +102,6 @@ local function verify_beach32_cksum(hrp, elts) return polymod(hrpExpand(hrp), elts) == 1 end - local function gen_bleach32_table(input) local d = {} local i = 1 @@ -167,7 +168,9 @@ local function is_segwit_bech32_address(task, word) -- For semicolon table.insert(polymod_tbl, 0) - fun.each(function(byte) table.insert(polymod_tbl, byte) end, decoded) + fun.each(function(byte) + table.insert(polymod_tbl, byte) + end, decoded) lua_util.debugm(N, task, 'final polymod table: %s', polymod_tbl) return rspamd_util.btc_polymod(polymod_tbl) diff --git a/rules/bounce.lua b/rules/bounce.lua index 08e1faef4..fb74b9797 100644 --- a/rules/bounce.lua +++ b/rules/bounce.lua @@ -57,12 +57,11 @@ rspamd_config.BOUNCE = { return false end - local parts = task:get_parts() local top_type, top_subtype, params = parts[1]:get_type_full() -- RFC 3464, RFC 8098 if top_type == 'multipart' and top_subtype == 'report' and params and - (params['report-type'] == 'delivery-status' or params['report-type'] == 'disposition-notification') then + (params['report-type'] == 'delivery-status' or params['report-type'] == 'disposition-notification') then -- Assume that inner parts are OK, don't check them to save time return true, 1.0, 'DSN' end @@ -75,9 +74,9 @@ rspamd_config.BOUNCE = { -- Check common bounce senders if (from_user == 'postmaster' or from_user == 'mailer-daemon') then bounce_sender = from_user - -- MDaemon >= 14.5 sends multipart/report (RFC 3464) DSN covered above, - -- but older versions send non-standard bounces with localized subjects and they - -- are still around + -- MDaemon >= 14.5 sends multipart/report (RFC 3464) DSN covered above, + -- but older versions send non-standard bounces with localized subjects and they + -- are still around elseif from_user == 'mdaemon' and task:has_header('X-MDDSN-Message') then return true, 1.0, 'MDaemon' end diff --git a/rules/content.lua b/rules/content.lua index 0936f5898..667b7ec74 100644 --- a/rules/content.lua +++ b/rules/content.lua @@ -34,7 +34,9 @@ local function process_pdf_specific(task, part, specific) end if suspicious_factor > 0.5 then - if suspicious_factor > 1.0 then suspicious_factor = 1.0 end + if suspicious_factor > 1.0 then + suspicious_factor = 1.0 + end task:insert_result('PDF_SUSPICIOUS', suspicious_factor, part:get_filename() or 'unknown') end @@ -59,7 +61,7 @@ local tags_processors = { local function process_specific_cb(task) local parts = task:get_parts() or {} - for _,p in ipairs(parts) do + for _, p in ipairs(parts) do if p:is_specific() then local data = p:get_specific() @@ -72,45 +74,45 @@ local function process_specific_cb(task) end end -local id = rspamd_config:register_symbol{ +local id = rspamd_config:register_symbol { type = 'callback', name = 'SPECIFIC_CONTENT_CHECK', callback = process_specific_cb } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_ENCRYPTED', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_JAVASCRIPT', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_SUSPICIOUS', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_LONG_TRAILER', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_MANY_OBJECTS', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', name = 'PDF_TIMEOUT', parent = id, - groups = {"content", "pdf"}, + groups = { "content", "pdf" }, } diff --git a/rules/controller/fuzzy.lua b/rules/controller/fuzzy.lua index 7e4c96fe1..193e6fd4c 100644 --- a/rules/controller/fuzzy.lua +++ b/rules/controller/fuzzy.lua @@ -19,16 +19,16 @@ local function handle_gen_fuzzy(task, conn, req_params) local ret, hashes task:process_message() if req_params.rule then - ret,hashes = pcall(rspamd_plugins.fuzzy_check.hex_hashes, task, req_params.rule) + ret, hashes = pcall(rspamd_plugins.fuzzy_check.hex_hashes, task, req_params.rule) elseif req_params.flag then - ret,hashes = pcall(rspamd_plugins.fuzzy_check.hex_hashes, task, tonumber(req_params.flag)) + ret, hashes = pcall(rspamd_plugins.fuzzy_check.hex_hashes, task, tonumber(req_params.flag)) else conn:send_error(404, 'missing rule or flag') return end if ret then - conn:send_ucl({success = true, hashes = hashes}) + conn:send_ucl({ success = true, hashes = hashes }) else conn:send_error(500, 'cannot generate hashes') end diff --git a/rules/controller/init.lua b/rules/controller/init.lua index 9d60200f8..17fbbfc43 100644 --- a/rules/controller/init.lua +++ b/rules/controller/init.lua @@ -39,7 +39,7 @@ if rspamd_util.file_exists(local_conf .. '/controller.lua') then end end -for plug,paths in pairs(controller_plugin_paths) do +for plug, paths in pairs(controller_plugin_paths) do if not rspamd_plugins[plug] then rspamd_plugins[plug] = {} end @@ -49,7 +49,7 @@ for plug,paths in pairs(controller_plugin_paths) do local webui = rspamd_plugins[plug].webui - for path,attrs in pairs(paths) do + for path, attrs in pairs(paths) do if type(attrs) == 'table' then if type(attrs.handler) ~= 'function' then rspamd_logger.infox(rspamd_config, 'controller plugin %s; webui path %s has invalid handler: %s; ignore it', @@ -61,7 +61,7 @@ for plug,paths in pairs(controller_plugin_paths) do end else rspamd_logger.infox(rspamd_config, 'controller plugin %s; webui path %s has invalid type: %s; ignore it', - plug, path, type(attrs)) + plug, path, type(attrs)) end end end diff --git a/rules/controller/maps.lua b/rules/controller/maps.lua index a45e3d306..718e292f6 100644 --- a/rules/controller/maps.lua +++ b/rules/controller/maps.lua @@ -26,7 +26,7 @@ local function maybe_fill_maps_cache() maps_cache = {} maps_aliases = {} local maps = rspamd_config:get_maps() - for _,m in ipairs(maps) do + for _, m in ipairs(maps) do -- We get the first url here and that's it local url = m:get_uri() if url ~= 'static' then @@ -81,12 +81,12 @@ local function handle_query_map(_, conn, req_params) end local results = {} - for _,key in ipairs(keys_to_check) do - for uri,m in pairs(maps_cache) do + for _, key in ipairs(keys_to_check) do + for uri, m in pairs(maps_cache) do check_specific_map(key, uri, m, results, req_params.report_misses) end end - conn:send_ucl{ + conn:send_ucl { success = (#results > 0), results = results } @@ -106,7 +106,7 @@ local function handle_query_specific_map(_, conn, req_params) if req_params.maps then local map_names = lua_util.str_split(req_params.maps, ',') maps_to_check = {} - for _,mn in ipairs(map_names) do + for _, mn in ipairs(map_names) do if maps_cache[mn] then maps_to_check[mn] = maps_cache[mn] else @@ -122,13 +122,13 @@ local function handle_query_specific_map(_, conn, req_params) end local results = {} - for _,key in ipairs(keys_to_check) do - for uri,m in pairs(maps_to_check) do + for _, key in ipairs(keys_to_check) do + for uri, m in pairs(maps_to_check) do check_specific_map(key, uri, m, results, req_params.report_misses) end end - conn:send_ucl{ + conn:send_ucl { success = (#results > 0), results = results } @@ -136,13 +136,13 @@ end local function handle_list_maps(_, conn, _) maybe_fill_maps_cache() - conn:send_ucl{ + conn:send_ucl { maps = lua_util.keys(maps_cache), aliases = maps_aliases } end -local query_json_schema = ts.shape{ +local query_json_schema = ts.shape { maps = ts.array_of(ts.string):is_optional(), report_misses = ts.boolean:is_optional(), values = ts.array_of(ts.string), @@ -170,7 +170,7 @@ local function handle_query_json(task, conn) local results = {} if obj.maps then - for _,mn in ipairs(obj.maps) do + for _, mn in ipairs(obj.maps) do if maps_cache[mn] then maps_to_check[mn] = maps_cache[mn] else @@ -188,12 +188,12 @@ local function handle_query_json(task, conn) maps_to_check = maps_cache end - for _,key in ipairs(obj.values) do - for uri,m in pairs(maps_to_check) do + for _, key in ipairs(obj.values) do + for uri, m in pairs(maps_to_check) do check_specific_map(key, uri, m, results, report_misses) end end - conn:send_ucl{ + conn:send_ucl { success = (#results > 0), results = results } diff --git a/rules/controller/neural.lua b/rules/controller/neural.lua index db95cb7e9..aef104247 100644 --- a/rules/controller/neural.lua +++ b/rules/controller/neural.lua @@ -22,7 +22,7 @@ local E = {} -- Controller neural plugin -local learn_request_schema = ts.shape{ +local learn_request_schema = ts.shape { ham_vec = ts.array_of(ts.array_of(ts.number)), rule = ts.string:is_optional(), spam_vec = ts.array_of(ts.array_of(ts.number)), @@ -48,7 +48,7 @@ local function handle_learn(task, conn) local set = neural_common.get_rule_settings(task, rule) local version = ((set.ann or E).version or 0) + 1 - neural_common.spawn_train{ + neural_common.spawn_train { ev_base = task:get_ev_base(), ann_key = neural_common.new_ann_key(rule, set, version), set = set, diff --git a/rules/controller/selectors.lua b/rules/controller/selectors.lua index 8f3dc0931..7fc2894d7 100644 --- a/rules/controller/selectors.lua +++ b/rules/controller/selectors.lua @@ -30,7 +30,7 @@ local function handle_check_selector(_, conn, req_params) if req_params.selector and req_params.selector ~= '' then local selector = lua_selectors.create_selector_closure(rspamd_config, req_params.selector, '', true) - conn:send_ucl({success = selector and true}) + conn:send_ucl({ success = selector and true }) else conn:send_error(404, 'missing selector') end @@ -45,7 +45,7 @@ local function handle_check_message(task, conn, req_params) else task:process_message() local elts = selector(task) - conn:send_ucl({success = true, data = elts}) + conn:send_ucl({ success = true, data = elts }) end else conn:send_error(404, 'missing selector') diff --git a/rules/forwarding.lua b/rules/forwarding.lua index 7d79a0c31..a008c587d 100644 --- a/rules/forwarding.lua +++ b/rules/forwarding.lua @@ -19,22 +19,24 @@ limitations under the License. local rspamd_util = require "rspamd_util" rspamd_config.FWD_GOOGLE = { - 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{'smtp', 'orig'} + local envfrom = task:get_from { 'smtp', 'orig' } local envrcpts = task:get_recipients(1) -- Forwarding will only be to a single recipient - 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() -- Check for a match if ef_user:find('+caf_=' .. verp, 1, true) then - local _,_,user = ef_user:find('^(.+)+caf_=') + local _, _, user = ef_user:find('^(.+)+caf_=') if user then user = user .. '@' .. envfrom[1].domain return true, user @@ -48,7 +50,7 @@ rspamd_config.FWD_GOOGLE = { } rspamd_config.FWD_YANDEX = { - callback = function (task) + callback = function(task) if not (task:has_from(1) and task:has_recipients(1)) then return false end @@ -64,7 +66,7 @@ rspamd_config.FWD_YANDEX = { } rspamd_config.FWD_MAILRU = { - callback = function (task) + callback = function(task) if not (task:has_from(1) and task:has_recipients(1)) then return false end @@ -80,14 +82,16 @@ rspamd_config.FWD_MAILRU = { } rspamd_config.FWD_SRS = { - 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) -- Forwarding is only to a single recipient - if #envrcpts > 1 then return false end + if #envrcpts > 1 then + return false + end -- Get recipient and compute rewritten SRS address local srs = '=' .. envrcpts[1].domain:lower() .. '=' .. envrcpts[1].user:lower() @@ -104,10 +108,10 @@ rspamd_config.FWD_SRS = { } rspamd_config.FORWARDED = { - callback = function (task) + callback = function(task) local function normalize_addr(addr) addr = string.match(addr, '^<?([^>]*)>?$') or addr - local cap, _,domain = string.match(addr, '^([^%+][^%+]*)(%+[^@]*)@(.*)$') + local cap, _, domain = string.match(addr, '^([^%+][^%+]*)(%+[^@]*)@(.*)$') if cap then addr = string.format('%s@%s', cap, domain) end @@ -115,10 +119,14 @@ rspamd_config.FORWARDED = { return addr end - if not task:has_recipients(1) or not task:has_recipients(2) then return false end + if not task:has_recipients(1) or not task:has_recipients(2) then + return false + end local envrcpts = task:get_recipients(1) -- Forwarding will only be for single recipient messages - if #envrcpts > 1 then return false end + if #envrcpts > 1 then + return false + end -- Get any other headers we might need local has_list_unsub = task:has_header('List-Unsubscribe') local to = task:get_recipients(2) diff --git a/rules/headers_checks.lua b/rules/headers_checks.lua index f3d93efe7..f28b0bc7a 100644 --- a/rules/headers_checks.lua +++ b/rules/headers_checks.lua @@ -23,7 +23,7 @@ local tonumber = tonumber local fun = require "fun" local E = {} -local rcvd_cb_id = rspamd_config:register_symbol{ +local rcvd_cb_id = rspamd_config:register_symbol { name = 'CHECK_RECEIVED', type = 'callback', score = 0.0, @@ -40,12 +40,12 @@ local rcvd_cb_id = rspamd_config:register_symbol{ local def = 'ZERO' local received = task:get_received_headers() local nreceived = fun.reduce(function(acc, rcvd) - return acc + 1 - end, 0, fun.filter(function(h) - return not h['flags']['artificial'] - end, received)) + return acc + 1 + end, 0, fun.filter(function(h) + return not h['flags']['artificial'] + end, received)) - for k,v in pairs(cnts) do + for k, v in pairs(cnts) do if nreceived >= tonumber(k) then def = v end @@ -55,7 +55,7 @@ local rcvd_cb_id = rspamd_config:register_symbol{ end } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_ZERO', score = 0.0, parent = rcvd_cb_id, @@ -63,7 +63,7 @@ rspamd_config:register_symbol{ description = 'Message has no Received headers', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_ONE', score = 0.0, parent = rcvd_cb_id, @@ -71,7 +71,7 @@ rspamd_config:register_symbol{ description = 'Message has one Received header', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_TWO', score = 0.0, parent = rcvd_cb_id, @@ -79,7 +79,7 @@ rspamd_config:register_symbol{ description = 'Message has two Received headers', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_THREE', score = 0.0, parent = rcvd_cb_id, @@ -87,7 +87,7 @@ rspamd_config:register_symbol{ description = 'Message has 3-5 Received headers', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_FIVE', score = 0.0, parent = rcvd_cb_id, @@ -95,7 +95,7 @@ rspamd_config:register_symbol{ description = 'Message has 5-7 Received headers', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_SEVEN', score = 0.0, parent = rcvd_cb_id, @@ -103,7 +103,7 @@ rspamd_config:register_symbol{ description = 'Message has 7-11 Received headers', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCVD_COUNT_TWELVE', score = 0.0, parent = rcvd_cb_id, @@ -118,8 +118,8 @@ local prio_cb_id = rspamd_config:register_symbol { description = 'X-Priority check callback rule', score = 0.0, group = 'headers', - callback = function (task) - local cnts = { + callback = function(task) + local cnts = { [1] = 'ONE', [2] = 'TWO', [3] = 'THREE', @@ -127,11 +127,13 @@ local prio_cb_id = rspamd_config:register_symbol { } local def = 'ZERO' local xprio = task:get_header('X-Priority'); - if not xprio then return false end - local _,_,x = xprio:find('^%s?(%d+)'); + if not xprio then + return false + end + local _, _, x = xprio:find('^%s?(%d+)'); if (x) then x = tonumber(x) - for k,v in pairs(cnts) do + for k, v in pairs(cnts) do if x >= tonumber(k) then def = v end @@ -140,7 +142,7 @@ local prio_cb_id = rspamd_config:register_symbol { end end } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_X_PRIO_ZERO', score = 0.0, parent = prio_cb_id, @@ -148,7 +150,7 @@ rspamd_config:register_symbol{ description = 'Message has X-Priority header set to 0', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_X_PRIO_ONE', score = 0.0, parent = prio_cb_id, @@ -156,7 +158,7 @@ rspamd_config:register_symbol{ description = 'Message has X-Priority header set to 1', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_X_PRIO_TWO', score = 0.0, parent = prio_cb_id, @@ -164,7 +166,7 @@ rspamd_config:register_symbol{ description = 'Message has X-Priority header set to 2', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_X_PRIO_THREE', score = 0.0, parent = prio_cb_id, @@ -172,7 +174,7 @@ rspamd_config:register_symbol{ description = 'Message has X-Priority header set to 3 or 4', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_X_PRIO_FIVE', score = 0.0, parent = prio_cb_id, @@ -214,7 +216,7 @@ local check_replyto_id = rspamd_config:register_symbol({ end -- See if Reply-To matches From in some way - local from = task:get_from{'mime', 'orig'} + local from = task:get_from { 'mime', 'orig' } local from_h = get_raw_header(task, 'From') if not (from and from[1]) then return false @@ -257,7 +259,7 @@ local check_replyto_id = rspamd_config:register_symbol({ end }) -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_UNPARSEABLE', score = 1.0, parent = check_replyto_id, @@ -265,7 +267,7 @@ rspamd_config:register_symbol{ description = 'Reply-To header could not be parsed', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'HAS_REPLYTO', score = 0.0, parent = check_replyto_id, @@ -273,7 +275,7 @@ rspamd_config:register_symbol{ description = 'Has Reply-To header', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_EQ_FROM', score = 0.0, parent = check_replyto_id, @@ -281,7 +283,7 @@ rspamd_config:register_symbol{ description = 'Reply-To header is identical to From header', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_ADDR_EQ_FROM', score = 0.0, parent = check_replyto_id, @@ -289,7 +291,7 @@ rspamd_config:register_symbol{ description = 'Reply-To header is identical to SMTP From', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_DOM_EQ_FROM_DOM', score = 0.0, parent = check_replyto_id, @@ -297,7 +299,7 @@ rspamd_config:register_symbol{ description = 'Reply-To domain matches the From domain', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_DOM_NEQ_FROM_DOM', score = 0.0, parent = check_replyto_id, @@ -305,7 +307,7 @@ rspamd_config:register_symbol{ description = 'Reply-To domain does not match the From domain', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_DN_EQ_FROM_DN', score = 0.0, parent = check_replyto_id, @@ -313,7 +315,7 @@ rspamd_config:register_symbol{ description = 'Reply-To display name matches From', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_EMAIL_HAS_TITLE', score = 2.0, parent = check_replyto_id, @@ -321,7 +323,7 @@ rspamd_config:register_symbol{ description = 'Reply-To header has title', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'REPLYTO_EQ_TO_ADDR', score = 5.0, parent = check_replyto_id, @@ -332,7 +334,7 @@ rspamd_config:register_symbol{ rspamd_config:register_dependency('CHECK_REPLYTO', 'CHECK_FROM') -local check_mime_id = rspamd_config:register_symbol{ +local check_mime_id = rspamd_config:register_symbol { name = 'CHECK_MIME', type = 'callback', group = 'headers', @@ -383,7 +385,7 @@ local check_mime_id = rspamd_config:register_symbol{ end } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'MISSING_MIME_VERSION', score = 2.0, parent = check_mime_id, @@ -391,7 +393,7 @@ rspamd_config:register_symbol{ description = 'MIME-Version header is missing in MIME message', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'MIME_MA_MISSING_TEXT', score = 2.0, parent = check_mime_id, @@ -399,7 +401,7 @@ rspamd_config:register_symbol{ description = 'MIME multipart/alternative missing text/plain part', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'MIME_MA_MISSING_HTML', score = 1.0, parent = check_mime_id, @@ -411,12 +413,16 @@ rspamd_config:register_symbol{ -- Used to be called IS_LIST rspamd_config.PREVIOUSLY_DELIVERED = { callback = function(task) - if not task:has_recipients(2) then return false end + if not task:has_recipients(2) then + return false + end local to = task:get_recipients(2) 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 _,_,addr = rcvd['decoded']:lower():find("%sfor%s<(.-)>") + local _, _, addr = rcvd['decoded']:lower():find("%sfor%s<(.-)>") if addr then for _, toa in ipairs(to) do if toa and toa.addr:lower() == addr then @@ -442,8 +448,10 @@ rspamd_config.BROKEN_HEADERS = { rspamd_config.BROKEN_CONTENT_TYPE = { callback = function(task) - return fun.any(function(p) return p:is_broken() end, - task:get_parts()) + return fun.any(function(p) + return p:is_broken() + end, + task:get_parts()) end, score = 1.5, group = 'headers', @@ -451,18 +459,20 @@ rspamd_config.BROKEN_CONTENT_TYPE = { } rspamd_config.HEADER_RCONFIRM_MISMATCH = { - callback = function (task) + callback = function(task) local header_from = nil local cread = task:get_header('X-Confirm-Reading-To') if task:has_from('mime') then - header_from = task:get_from('mime')[1] + header_from = task:get_from('mime')[1] end local header_cread = nil if cread then local headers_cread = util.parse_mail_address(cread, task:get_mempool()) - if headers_cread then header_cread = headers_cread[1] end + if headers_cread then + header_cread = headers_cread[1] + end end if header_from and header_cread then @@ -480,9 +490,11 @@ rspamd_config.HEADER_RCONFIRM_MISMATCH = { } rspamd_config.HEADER_FORGED_MDN = { - callback = function (task) + callback = function(task) local mdn = task:get_header('Disposition-Notification-To') - if not mdn then return false end + if not mdn then + return false + end local header_rp = nil if task:has_from('smtp') then @@ -492,9 +504,15 @@ rspamd_config.HEADER_FORGED_MDN = { -- Parse mail addr local headers_mdn = util.parse_mail_address(mdn, task:get_mempool()) - if headers_mdn and not header_rp then return true end - if header_rp and not headers_mdn then return false end - if not headers_mdn and not header_rp then return false end + if headers_mdn and not header_rp then + return true + end + if header_rp and not headers_mdn then + return false + end + if not headers_mdn and not header_rp then + return false + end local found_match = false for _, h in ipairs(headers_mdn) do @@ -535,7 +553,7 @@ rspamd_config.MULTIPLE_UNIQUE_HEADERS = { local max_mult = 0.0 local res_tbl = {} - for hdr,mult in pairs(headers_unique) do + for hdr, mult in pairs(headers_unique) do local hc = task:get_header_count(hdr) if hc > 1 then @@ -548,7 +566,7 @@ rspamd_config.MULTIPLE_UNIQUE_HEADERS = { end if res > 0 then - return true,max_mult,table.concat(res_tbl, ',') + return true, max_mult, table.concat(res_tbl, ',') end return false @@ -577,7 +595,9 @@ rspamd_config.MULTIPLE_FROM = { callback = function(task) local from = task:get_from('mime') if from and from[2] then - return true, 1.0, fun.totable(fun.map(function(a) return a.raw end, from)) + return true, 1.0, fun.totable(fun.map(function(a) + return a.raw + end, from)) end return false end, @@ -587,7 +607,7 @@ rspamd_config.MULTIPLE_FROM = { } rspamd_config.MV_CASE = { - callback = function (task) + callback = function(task) return task:has_header('Mime-Version', true) end, description = 'Mime-Version .vs. MIME-Version', @@ -595,7 +615,7 @@ rspamd_config.MV_CASE = { group = 'headers' } -local check_from_id = rspamd_config:register_symbol{ +local check_from_id = rspamd_config:register_symbol { name = 'CHECK_FROM', type = 'callback', score = 0.0, @@ -610,10 +630,10 @@ local check_from_id = rspamd_config:register_symbol{ if not (from[1]["flags"]["valid"]) then task:insert_result('FROM_INVALID', 1.0) end - if (from[1].name == nil or from[1].name == '' ) then + if (from[1].name == nil or from[1].name == '') then task:insert_result('FROM_NO_DN', 1.0) elseif (from[1].name and - util.strequal_caseless(from[1].name, from[1].addr)) then + util.strequal_caseless(from[1].name, from[1].addr)) then task:insert_result('FROM_DN_EQ_ADDR', 1.0) elseif (from[1].name and from[1].name ~= '') then task:insert_result('FROM_HAS_DN', 1.0) @@ -622,11 +642,11 @@ local check_from_id = rspamd_config:register_symbol{ local match, match_end match, match_end = n:find('^mrs?[%.%s]') if match then - task:insert_result('FROM_NAME_HAS_TITLE', 1.0, n:sub(match, match_end-1)) + task:insert_result('FROM_NAME_HAS_TITLE', 1.0, n:sub(match, match_end - 1)) end match, match_end = n:find('^dr[%.%s]') if match then - task:insert_result('FROM_NAME_HAS_TITLE', 1.0, n:sub(match, match_end-1)) + task:insert_result('FROM_NAME_HAS_TITLE', 1.0, n:sub(match, match_end - 1)) end -- Check for excess spaces if n:find('%s%s') then @@ -644,19 +664,21 @@ local check_from_id = rspamd_config:register_symbol{ end local to = task:get_recipients(2) - if not (to and to[1] and #to == 1 and from and from[1]) then return false end + if not (to and to[1] and #to == 1 and from and from[1]) then + return false + end -- Check if FROM == TO if (util.strequal_caseless(to[1].addr, from[1].addr)) then task:insert_result('TO_EQ_FROM', 1.0) elseif (to[1].domain and from[1].domain and - util.strequal_caseless(to[1].domain, from[1].domain)) + util.strequal_caseless(to[1].domain, from[1].domain)) then task:insert_result('TO_DOM_EQ_FROM_DOM', 1.0) end end } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'ENVFROM_INVALID', score = 2.0, group = 'headers', @@ -664,7 +686,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'Envelope from does not have a valid format', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_INVALID', score = 2.0, group = 'headers', @@ -672,7 +694,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header does not have a valid format', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_NO_DN', score = 0.0, group = 'headers', @@ -680,7 +702,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header does not have a display name', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_DN_EQ_ADDR', score = 1.0, group = 'headers', @@ -688,7 +710,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header display name is the same as the address', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_HAS_DN', score = 0.0, group = 'headers', @@ -696,7 +718,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header has a display name', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_NAME_EXCESS_SPACE', score = 1.0, group = 'headers', @@ -704,7 +726,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header display name contains excess whitespace', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_NAME_HAS_TITLE', score = 1.0, group = 'headers', @@ -712,7 +734,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From header display name has a title (Mr/Mrs/Dr)', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_EQ_ENVFROM', score = 0.0, group = 'headers', @@ -720,7 +742,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From address is the same as the envelope', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'FROM_NEQ_ENVFROM', score = 0.0, group = 'headers', @@ -728,7 +750,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'From address is different to the envelope', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_EQ_FROM', score = 0.0, group = 'headers', @@ -736,7 +758,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'To address matches the From address', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DOM_EQ_FROM_DOM', score = 0.0, group = 'headers', @@ -745,7 +767,7 @@ rspamd_config:register_symbol{ description = 'To domain is the same as the From domain', } -local check_to_cc_id = rspamd_config:register_symbol{ +local check_to_cc_id = rspamd_config:register_symbol { name = 'CHECK_TO_CC', type = 'callback', score = 0.0, @@ -764,10 +786,12 @@ local check_to_cc_id = rspamd_config:register_symbol{ [50] = 'GT_50' } local def = 'ZERO' - if (not to) then return false end + if (not to) then + return false + end -- Add symbol for recipient count local nrcpt = #to - for k,v in pairs(cnts) do + for k, v in pairs(cnts) do if nrcpt >= tonumber(k) then def = v end @@ -820,7 +844,7 @@ local check_to_cc_id = rspamd_config:register_symbol{ end } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_ZERO', score = 0.0, parent = check_to_cc_id, @@ -828,7 +852,7 @@ rspamd_config:register_symbol{ description = 'No recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_ONE', score = 0.0, parent = check_to_cc_id, @@ -836,7 +860,7 @@ rspamd_config:register_symbol{ description = 'One recipient', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_TWO', score = 0.0, parent = check_to_cc_id, @@ -844,7 +868,7 @@ rspamd_config:register_symbol{ description = 'Two recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_THREE', score = 0.0, parent = check_to_cc_id, @@ -852,7 +876,7 @@ rspamd_config:register_symbol{ description = '3-5 recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_FIVE', score = 0.0, parent = check_to_cc_id, @@ -860,7 +884,7 @@ rspamd_config:register_symbol{ description = '5-7 recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_SEVEN', score = 0.0, parent = check_to_cc_id, @@ -868,7 +892,7 @@ rspamd_config:register_symbol{ description = '7-11 recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_TWELVE', score = 0.0, parent = check_to_cc_id, @@ -876,7 +900,7 @@ rspamd_config:register_symbol{ description = '12-50 recipients', group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'RCPT_COUNT_GT_50', score = 0.0, parent = check_to_cc_id, @@ -885,7 +909,7 @@ rspamd_config:register_symbol{ group = 'headers', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_RECIPIENTS', score = 2.0, group = 'headers', @@ -893,7 +917,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'To header display name is "Recipients"', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_NONE', score = 0.0, group = 'headers', @@ -901,7 +925,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'None of the recipients have display names', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_ALL', score = 0.0, group = 'headers', @@ -909,7 +933,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'All the recipients have display names', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_SOME', score = 0.0, group = 'headers', @@ -917,7 +941,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'Some of the recipients have display names', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_EQ_ADDR_ALL', score = 0.0, group = 'headers', @@ -925,7 +949,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'All of the recipients have display names that are the same as their address', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_DN_EQ_ADDR_SOME', score = 0.0, group = 'headers', @@ -933,7 +957,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'Some of the recipients have display names that are the same as their address', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_MATCH_ENVRCPT_ALL', score = 0.0, group = 'headers', @@ -941,7 +965,7 @@ rspamd_config:register_symbol{ type = 'virtual', description = 'All of the recipients match the envelope', } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { name = 'TO_MATCH_ENVRCPT_SOME', score = 0.0, group = 'headers', @@ -954,8 +978,10 @@ rspamd_config:register_symbol{ rspamd_config.CTYPE_MISSING_DISPOSITION = { callback = function(task) local parts = task:get_parts() - if (not parts) or (parts and #parts < 1) then return false end - for _,p in ipairs(parts) do + if (not parts) or (parts and #parts < 1) then + return false + end + for _, p in ipairs(parts) do local ct = p:get_header('Content-Type') if (ct and ct:lower():match('^application/octet%-stream') ~= nil) then local cd = p:get_header('Content-Disposition') @@ -969,7 +995,7 @@ rspamd_config.CTYPE_MISSING_DISPOSITION = { local parent = p:get_parent() if parent then - local t,st = parent:get_type() + local t, st = parent:get_type() if t == 'multipart' and st == 'encrypted' then -- Special case @@ -991,15 +1017,21 @@ rspamd_config.CTYPE_MISSING_DISPOSITION = { rspamd_config.CTYPE_MIXED_BOGUS = { callback = function(task) local ct = task:get_header('Content-Type') - if (not ct) then return false end + if (not ct) then + return false + end local parts = task:get_parts() - if (not parts) then return false end - if (not ct:lower():match('^multipart/mixed')) then return false end + if (not parts) then + return false + end + if (not ct:lower():match('^multipart/mixed')) then + return false + end local found = false -- Check each part and look for a part that isn't multipart/* or text/plain or text/html local ntext_parts = 0 - for _,p in ipairs(parts) do - local mtype,_ = p:get_type() + for _, p in ipairs(parts) do + local mtype, _ = p:get_type() if mtype then if mtype == 'text' and not p:is_attachment() then ntext_parts = ntext_parts + 1 @@ -1013,7 +1045,9 @@ rspamd_config.CTYPE_MIXED_BOGUS = { end end end - if (not found) then return true end + if (not found) then + return true + end return false end, description = 'multipart/mixed without non-textual part', @@ -1023,7 +1057,9 @@ rspamd_config.CTYPE_MIXED_BOGUS = { local function check_for_base64_text(part) local ct = part:get_header('Content-Type') - if (not ct) then return false end + if (not ct) then + return false + end ct = ct:lower() if (ct:match('^text')) then -- Check encoding @@ -1042,7 +1078,9 @@ rspamd_config.MIME_BASE64_TEXT = { return true else local parts = task:get_parts() - if (not parts) then return false end + if (not parts) then + return false + end -- Check each part and look for base64 encoded text parts for _, part in ipairs(parts) do if (check_for_base64_text(part)) then @@ -1060,7 +1098,9 @@ rspamd_config.MIME_BASE64_TEXT = { rspamd_config.MIME_BASE64_TEXT_BOGUS = { callback = function(task) local parts = task:get_text_parts() - if (not parts) then return false end + if (not parts) then + return false + end -- Check each part and look for base64 encoded text parts -- where the part does not have any 8bit characters within it for _, part in ipairs(parts) do @@ -1113,7 +1153,7 @@ rspamd_config.INVALID_RCPT_8BIT = { } rspamd_config.XM_CASE = { - callback = function (task) + callback = function(task) return task:has_header('X-mailer', true) end, description = 'X-mailer .vs. X-Mailer', diff --git a/rules/html.lua b/rules/html.lua index df42f2f0e..7c352c2e1 100644 --- a/rules/html.lua +++ b/rules/html.lua @@ -42,16 +42,15 @@ end local function check_html_image(task, min, max) local tp = task:get_text_parts() - for _,p in ipairs(tp) do + for _, p in ipairs(tp) do if p:is_html() then local hc = p:get_html() local len = p:get_length() - if hc and len >= min and len < max then local images = hc:get_images() if images then - for _,i in ipairs(images) do + for _, i in ipairs(images) do local tag = i['tag'] if tag then if has_anchor_parent(tag) then @@ -99,16 +98,22 @@ rspamd_config.R_EMPTY_IMAGE = { callback = function(task) local tp = task:get_text_parts() -- get text parts in a message - for _,p in ipairs(tp) do -- iterate over text parts array using `ipairs` - if p:is_html() then -- if the current part is html part + for _, p in ipairs(tp) do + -- iterate over text parts array using `ipairs` + if p:is_html() then + -- if the current part is html part local hc = p:get_html() -- we get HTML context local len = p:get_length() -- and part's length - if hc and len < 50 then -- if we have a part that has less than 50 bytes of text + if hc and len < 50 then + -- if we have a part that has less than 50 bytes of text local images = hc:get_images() -- then we check for HTML images - if images then -- if there are images - for _,i in ipairs(images) do -- then iterate over images in the part - if i['height'] + i['width'] >= 400 then -- if we have a large image + if images then + -- if there are images + for _, i in ipairs(images) do + -- then iterate over images in the part + if i['height'] + i['width'] >= 400 then + -- if we have a large image local tag = i['tag'] if tag then if not has_anchor_parent(tag) then @@ -174,7 +179,7 @@ rspamd_config.R_SUSPICIOUS_IMAGES = { description = 'Message contains many suspicious messages' } -local vis_check_id = rspamd_config:register_symbol{ +local vis_check_id = rspamd_config:register_symbol { name = 'HTML_VISIBLE_CHECKS', type = 'callback', group = 'html', @@ -190,12 +195,14 @@ local vis_check_id = rspamd_config:register_symbol{ local normal_len = 0 local transp_len = 0 - for _,p in ipairs(tp) do -- iterate over text parts array using `ipairs` + for _, p in ipairs(tp) do + -- iterate over text parts array using `ipairs` normal_len = normal_len + p:get_length() - if p:is_html() and p:get_html() then -- if the current part is html part + if p:is_html() and p:get_html() then + -- if the current part is html part local hc = p:get_html() -- we get HTML context - hc:foreach_tag({'font', 'span', 'div', 'p', 'td'}, function(tag, clen, is_leaf) + hc:foreach_tag({ 'font', 'span', 'div', 'p', 'td' }, function(tag, clen, is_leaf) local bl = tag:get_style() if bl then if not bl.visible and clen > 0 and is_leaf then @@ -214,8 +221,12 @@ local vis_check_id = rspamd_config:register_symbol{ local tr = transp_len / (normal_len + transp_len) if tr > transp_rate then transp_rate = tr - if not bl.color then bl.color = {0, 0, 0} end - if not bl.bgcolor then bl.bgcolor = {0, 0, 0} end + if not bl.color then + bl.color = { 0, 0, 0 } + end + if not bl.bgcolor then + bl.bgcolor = { 0, 0, 0 } + end arg = string.format('%s color #%x%x%x bgcolor #%x%x%x', tag:get_type(), bl.color[1], bl.color[2], bl.color[3], @@ -288,7 +299,7 @@ local vis_check_id = rspamd_config:register_symbol{ end, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', parent = vis_check_id, name = 'R_WHITE_ON_WHITE', @@ -298,7 +309,7 @@ rspamd_config:register_symbol{ one_shot = true, } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', parent = vis_check_id, name = 'ZERO_FONT', @@ -308,7 +319,7 @@ rspamd_config:register_symbol{ group = 'html' } -rspamd_config:register_symbol{ +rspamd_config:register_symbol { type = 'virtual', parent = vis_check_id, name = 'MANY_INVISIBLE_PARTS', @@ -324,10 +335,12 @@ rspamd_config.EXT_CSS = { local re = regexp_lib.create_cached('/^.*\\.css(?:[?#].*)?$/i') local tp = task:get_text_parts() -- get text parts in a message local ret = false - for _,p in ipairs(tp) do -- iterate over text parts array using `ipairs` - if p:is_html() and p:get_html() then -- if the current part is html part + for _, p in ipairs(tp) do + -- iterate over text parts array using `ipairs` + if p:is_html() and p:get_html() then + -- if the current part is html part local hc = p:get_html() -- we get HTML context - hc:foreach_tag({'link'}, function(tag) + hc:foreach_tag({ 'link' }, function(tag) local bl = tag:get_extra() if bl then local s = tostring(bl) @@ -357,26 +370,36 @@ rspamd_config.HTTP_TO_HTTPS = { local found_opts local tp = task:get_text_parts() or {} - for _,p in ipairs(tp) do + for _, p in ipairs(tp) do if p:is_html() then local hc = p:get_html() - if (not hc) then return false end + if (not hc) then + return false + end local found = false - hc:foreach_tag('a', function (tag, _) + hc:foreach_tag('a', function(tag, _) -- Skip this loop if we already have a match - if (found) then return true end + if (found) then + return true + end local c = tag:get_content() if (c) then - if (not https_re:match(c)) then return false end + if (not https_re:match(c)) then + return false + end local u = tag:get_extra() - if (not u) then return false end + if (not u) then + return false + end local url_proto = u:get_protocol() - if url_proto ~= 'http' then return false end + if url_proto ~= 'http' then + return false + end -- Capture matches for http in href to https in visible part only found = true found_opts = u:get_host() @@ -387,7 +410,7 @@ rspamd_config.HTTP_TO_HTTPS = { end) if (found) then - return true,1.0,found_opts + return true, 1.0, found_opts end return false @@ -403,14 +426,20 @@ rspamd_config.HTTP_TO_HTTPS = { rspamd_config.HTTP_TO_IP = { callback = function(task) local tp = task:get_text_parts() - if (not tp) then return false end - for _,p in ipairs(tp) do + if (not tp) then + return false + end + for _, p in ipairs(tp) do if p:is_html() then local hc = p:get_html() - if (not hc) then return false end + if (not hc) then + return false + end local found = false - hc:foreach_tag('a', function (tag, length) - if (found) then return true end + hc:foreach_tag('a', function(tag, length) + if (found) then + return true + end local u = tag:get_extra() if (u) then u = tostring(u):lower() @@ -420,7 +449,9 @@ rspamd_config.HTTP_TO_IP = { end return false end) - if found then return true end + if found then + return true + end return false end end diff --git a/rules/mid.lua b/rules/mid.lua index 14c701cf6..1bac26c61 100644 --- a/rules/mid.lua +++ b/rules/mid.lua @@ -17,7 +17,9 @@ limitations under the License. local rspamd_util = require "rspamd_util" local function mid_check_func(task) local mid = task:get_header('Message-ID') - if not mid then return false end + if not mid then + return false + end -- Check for 'bare' IP addresses in RHS if mid:find("@%d+%.%d+%.%d+%.%d+>$") then task:insert_result('MID_BARE_IP', 1.0) @@ -39,7 +41,7 @@ local function mid_check_func(task) local fd if (from and from[1] and from[1].domain and from[1].domain ~= '') then fd = from[1].domain:lower() - local _,_,md = mid:find("@([^>]+)>?$") + local _, _, md = mid:find("@([^>]+)>?$") -- See if all or part of the From address -- can be found in the Message-ID -- extract tld @@ -49,7 +51,7 @@ local function mid_check_func(task) fdtld = rspamd_util.get_tld(fd) mdtld = rspamd_util.get_tld(md) end - if (mid:lower():find(from[1].addr:lower(),1,true)) then + if (mid:lower():find(from[1].addr:lower(), 1, true)) then task:insert_result('MID_CONTAINS_FROM', 1.0) elseif (md and fd == md:lower()) then task:insert_result('MID_RHS_MATCH_FROM', 1.0) @@ -61,12 +63,12 @@ local function mid_check_func(task) local to = task:get_recipients(2) if (to and to[1] and to[1].domain and to[1].domain ~= '') then local td = to[1].domain:lower() - local _,_,md = mid:find("@([^>]+)>?$") + local _, _, md = mid:find("@([^>]+)>?$") -- Skip if from domain == to domain if ((fd and fd ~= td) or not fd) then -- See if all or part of the To address -- can be found in the Message-ID - if (mid:lower():find(to[1].addr:lower(),1,true)) then + if (mid:lower():find(to[1].addr:lower(), 1, true)) then task:insert_result('MID_CONTAINS_TO', 1.0) elseif (md and td == md:lower()) then task:insert_result('MID_RHS_MATCH_TO', 1.0) @@ -115,9 +117,11 @@ rspamd_config:register_symbol { callback = function(task) local mid = task:get_header('Message-ID') - if not mid then return end + if not mid then + return + end local mime_from = task:get_from('mime') - local _,_,mid_realm = mid:find("@([a-z]+)>?$") + local _, _, mid_realm = mid:find("@([a-z]+)>?$") if mid_realm and mime_from and mime_from[1] and mime_from[1].user then if (mid_realm == mime_from[1].user) then return true 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', diff --git a/rules/regexp/compromised_hosts.lua b/rules/regexp/compromised_hosts.lua index cfd560bc2..e120b181b 100644 --- a/rules/regexp/compromised_hosts.lua +++ b/rules/regexp/compromised_hosts.lua @@ -44,10 +44,12 @@ reconf['HAS_X_SOURCE'] = { -- X-Authenticated-Sender: accord.host-care.com: sales@cortaflex.si rspamd_config.HAS_X_AS = { - callback = function (task) + callback = function(task) local xas = task:get_header('X-Authenticated-Sender') - if not xas then return false end - local _,_,auth = xas:find('[^:]+:%s(.+)$') + if not xas then + return false + end + local _, _, auth = xas:find('[^:]+:%s(.+)$') if auth then -- TODO: see if we can parse an e-mail address from auth -- and see if it matches the from address or not @@ -63,10 +65,12 @@ rspamd_config.HAS_X_AS = { -- X-Get-Message-Sender-Via: accord.host-care.com: authenticated_id: sales@cortaflex.si rspamd_config.HAS_X_GMSV = { - callback = function (task) + callback = function(task) local xgmsv = task:get_header('X-Get-Message-Sender-Via') - if not xgmsv then return false end - local _,_,auth = xgmsv:find('authenticated_id: (.+)$') + if not xgmsv then + return false + end + local _, _, auth = xgmsv:find('authenticated_id: (.+)$') if auth then -- TODO: see if we can parse an e-mail address from auth -- and see if it matches the from address or not. @@ -146,21 +150,21 @@ reconf['HIDDEN_SOURCE_OBJ'] = { group = "compromised_hosts" } -local hidden_uri_re = rspamd_regexp.create_cached('/(?!\\/\\.well[-_]known\\/)(?:^\\.[A-Za-z0-9]|\\/'.. +local hidden_uri_re = rspamd_regexp.create_cached('/(?!\\/\\.well[-_]known\\/)(?:^\\.[A-Za-z0-9]|\\/' .. '\\.[A-Za-z0-9]|\\/\\.\\.\\/)/i') rspamd_config.URI_HIDDEN_PATH = { - callback = function (task) + callback = function(task) local urls = task:get_urls(false) if (urls) then - for _, url in ipairs(urls) do - if (not (url:is_subject() and url:is_html_displayed())) then - local path = url:get_path() - if (hidden_uri_re:match(path)) then - -- TODO: need url:is_schemeless() to improve this - return true, 1.0, url:get_text() - end - end + for _, url in ipairs(urls) do + if (not (url:is_subject() and url:is_html_displayed())) then + local path = url:get_path() + if (hidden_uri_re:match(path)) then + -- TODO: need url:is_schemeless() to improve this + return true, 1.0, url:get_text() + end end + end end end, description = 'Message contains URI with a hidden path', @@ -176,19 +180,23 @@ reconf['MID_RHS_WWW'] = { } rspamd_config.FROM_SERVICE_ACCT = { - callback = function (task) + callback = function(task) local re = rspamd_regexp.create_cached('/^(?:www-data|anonymous|ftp|apache|nobody|guest|nginx|web|www)@/i'); -- From local from = task:get_from(2) if (from and from[1]) then - if (re:match(from[1].addr)) then return true end + if (re:match(from[1].addr)) then + return true + end end -- Sender local sender = task:get_header('Sender') if sender then local s = util.parse_mail_address(sender, task:get_mempool()) if (s and s[1]) then - if (re:match(s[1].addr)) then return true end + if (re:match(s[1].addr)) then + return true + end end end -- Reply-To @@ -196,7 +204,9 @@ rspamd_config.FROM_SERVICE_ACCT = { if replyto then local rt = util.parse_mail_address(replyto, task:get_mempool()) if (rt and rt[1]) then - if (re:match(rt[1].addr)) then return true end + if (re:match(rt[1].addr)) then + return true + end end end end, diff --git a/rules/regexp/headers.lua b/rules/regexp/headers.lua index 42c08ca3f..b634dd909 100644 --- a/rules/regexp/headers.lua +++ b/rules/regexp/headers.lua @@ -380,8 +380,8 @@ reconf['SUSPICIOUS_BOUNDARY3'] = { group = 'mua' } -- Forged OE/MSO boundary -local suspicious_boundary_01C4 = 'Content-Type=/^\\s*multipart.+boundary="----=_NextPart_000_[A-Z\\d]{4}_01C4[\\dA-F]{4}\\.[A-Z\\d]{8}"[\\r\\n]*$/siX' -local suspicious_boundary_01C4_date = 'Date=/^\\s*\\w\\w\\w,\\s+\\d+\\s+\\w\\w\\w 20(0[56789]|1\\d)/' +local suspicious_boundary_01C4 = 'Content-Type=/^\\s*multipart.+boundary="----=_NextPart_000_[A-Z\\d]{4}_01C4[\\dA-F]{4}\\.[A-Z\\d]{8}"[\\r\\n]*$/siX' +local suspicious_boundary_01C4_date = 'Date=/^\\s*\\w\\w\\w,\\s+\\d+\\s+\\w\\w\\w 20(0[56789]|1\\d)/' reconf['SUSPICIOUS_BOUNDARY4'] = { re = string.format('(%s) & (%s)', suspicious_boundary_01C4, suspicious_boundary_01C4_date), score = 4.0, @@ -439,24 +439,27 @@ reconf['FORGED_MUA_OPERA_MSGID'] = { -- Detect forged Mozilla Mail/Thunderbird/Seamonkey/Postbox headers -- Mozilla based X-Mailer -local user_agent_mozilla5 = 'User-Agent=/^\\s*Mozilla\\/5\\.0/H' -local user_agent_thunderbird = 'User-Agent=/^\\s*(Thunderbird|Mozilla Thunderbird|Mozilla\\/.*Gecko\\/.*(Thunderbird|Betterbird|Icedove)\\/)/H' -local user_agent_seamonkey = 'User-Agent=/^\\s*Mozilla\\/5\\.0\\s.+\\sSeaMonkey\\/\\d+\\.\\d+/H' -local user_agent_postbox = [[User-Agent=/^\s*Mozilla\/5\.0\s\([^)]+\)\sGecko\/\d+\sPostboxApp\/\d+(?:\.\d+){2,3}$/H]] -local user_agent_mozilla = string.format('(%s) & !(%s) & !(%s) & !(%s)', user_agent_mozilla5, user_agent_thunderbird, user_agent_seamonkey, user_agent_postbox) +local user_agent_mozilla5 = 'User-Agent=/^\\s*Mozilla\\/5\\.0/H' +local user_agent_thunderbird = 'User-Agent=/^\\s*(Thunderbird|Mozilla Thunderbird|Mozilla\\/.*Gecko\\/.*(Thunderbird|Betterbird|Icedove)\\/)/H' +local user_agent_seamonkey = 'User-Agent=/^\\s*Mozilla\\/5\\.0\\s.+\\sSeaMonkey\\/\\d+\\.\\d+/H' +local user_agent_postbox = [[User-Agent=/^\s*Mozilla\/5\.0\s\([^)]+\)\sGecko\/\d+\sPostboxApp\/\d+(?:\.\d+){2,3}$/H]] +local user_agent_mozilla = string.format('(%s) & !(%s) & !(%s) & !(%s)', user_agent_mozilla5, user_agent_thunderbird, + user_agent_seamonkey, user_agent_postbox) -- Mozilla based common Message-ID template -local mozilla_msgid_common = 'Message-ID=/^\\s*<[\\dA-F]{8}\\.\\d{1,7}\\@([^>\\.]+\\.)+[^>\\.]+>$/H' -local mozilla_msgid_common_sec = 'Message-ID=/^\\s*<[\\da-f]{8}-([\\da-f]{4}-){3}[\\da-f]{12}\\@([^>\\.]+\\.)+[^>\\.]+>$/H' -local mozilla_msgid = 'Message-ID=/^\\s*<(3[3-9A-F]|[4-9A-F][\\dA-F])[\\dA-F]{6}\\.(\\d0){1,4}\\d\\@([^>\\.]+\\.)+[^>\\.]+>$/H' +local mozilla_msgid_common = 'Message-ID=/^\\s*<[\\dA-F]{8}\\.\\d{1,7}\\@([^>\\.]+\\.)+[^>\\.]+>$/H' +local mozilla_msgid_common_sec = 'Message-ID=/^\\s*<[\\da-f]{8}-([\\da-f]{4}-){3}[\\da-f]{12}\\@([^>\\.]+\\.)+[^>\\.]+>$/H' +local mozilla_msgid = 'Message-ID=/^\\s*<(3[3-9A-F]|[4-9A-F][\\dA-F])[\\dA-F]{6}\\.(\\d0){1,4}\\d\\@([^>\\.]+\\.)+[^>\\.]+>$/H' -- Summary rule for forged Mozilla Mail Message-ID header reconf['FORGED_MUA_MOZILLA_MAIL_MSGID'] = { - re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_mozilla, mozilla_msgid_common, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_mozilla, mozilla_msgid_common, mozilla_msgid, + unusable_msgid), score = 4.0, description = 'Message pretends to be send from Mozilla Mail but has forged Message-ID', group = 'mua' } reconf['FORGED_MUA_MOZILLA_MAIL_MSGID_UNKNOWN'] = { - re = string.format('(%s) & !(%s) & !(%s) & !(%s)', user_agent_mozilla, mozilla_msgid_common, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & !(%s) & !(%s) & !(%s)', user_agent_mozilla, mozilla_msgid_common, mozilla_msgid, + unusable_msgid), score = 2.5, description = 'Message pretends to be send from Mozilla Mail but has forged Message-ID', group = 'mua' @@ -464,39 +467,45 @@ reconf['FORGED_MUA_MOZILLA_MAIL_MSGID_UNKNOWN'] = { -- Summary rule for forged Thunderbird Message-ID header reconf['FORGED_MUA_THUNDERBIRD_MSGID'] = { - re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_thunderbird, mozilla_msgid_common, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_thunderbird, mozilla_msgid_common, mozilla_msgid, + unusable_msgid), score = 4.0, description = 'Forged mail pretending to be from Mozilla Thunderbird but has forged Message-ID', group = 'mua' } reconf['FORGED_MUA_THUNDERBIRD_MSGID_UNKNOWN'] = { - re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_thunderbird, mozilla_msgid_common, mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_thunderbird, mozilla_msgid_common, + mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), score = 2.5, description = 'Forged mail pretending to be from Mozilla Thunderbird but has forged Message-ID', group = 'mua' } -- Summary rule for forged Seamonkey Message-ID header reconf['FORGED_MUA_SEAMONKEY_MSGID'] = { - re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_seamonkey, mozilla_msgid_common, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_seamonkey, mozilla_msgid_common, mozilla_msgid, + unusable_msgid), score = 4.0, description = 'Forged mail pretending to be from Mozilla Seamonkey but has forged Message-ID', group = 'mua' } reconf['FORGED_MUA_SEAMONKEY_MSGID_UNKNOWN'] = { - re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_seamonkey, mozilla_msgid_common, mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_seamonkey, mozilla_msgid_common, + mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), score = 2.5, description = 'Forged mail pretending to be from Mozilla Seamonkey but has forged Message-ID', group = 'mua' } -- Summary rule for forged Postbox Message-ID header reconf['FORGED_MUA_POSTBOX_MSGID'] = { - re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_postbox, mozilla_msgid_common, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & (%s) & !(%s) & !(%s)', user_agent_postbox, mozilla_msgid_common, mozilla_msgid, + unusable_msgid), score = 4.0, description = 'Forged mail pretending to be from Postbox but has forged Message-ID', group = 'mua' } reconf['FORGED_MUA_POSTBOX_MSGID_UNKNOWN'] = { - re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_postbox, mozilla_msgid_common, mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), + re = string.format('(%s) & !((%s) | (%s)) & !(%s) & !(%s)', user_agent_postbox, mozilla_msgid_common, + mozilla_msgid_common_sec, mozilla_msgid, unusable_msgid), score = 2.5, description = 'Forged mail pretending to be from Postbox but has forged Message-ID', group = 'mua' @@ -647,8 +656,10 @@ reconf['MISSING_MIMEOLE'] = { -- Empty delimiters between header names and header values local function gen_check_header_delimiter_empty(header_name) return function(task) - for _,rh in ipairs(task:get_header_full(header_name) or {}) do - if rh['empty_separator'] then return true end + for _, rh in ipairs(task:get_header_full(header_name) or {}) do + if rh['empty_separator'] then + return true + end end return false end @@ -707,10 +718,10 @@ reconf['RCVD_ILLEGAL_CHARS'] = { group = 'headers' } -local MAIL_RU_Return_Path = 'Return-path=/^\\s*<.+\\@mail\\.ru>$/iX' -local MAIL_RU_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@mail\\.ru>$/iX' -local MAIL_RU_From = 'From=/\\@mail\\.ru>?$/iX' -local MAIL_RU_Received = 'Received=/from mail\\.ru \\(/mH' +local MAIL_RU_Return_Path = 'Return-path=/^\\s*<.+\\@mail\\.ru>$/iX' +local MAIL_RU_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@mail\\.ru>$/iX' +local MAIL_RU_From = 'From=/\\@mail\\.ru>?$/iX' +local MAIL_RU_Received = 'Received=/from mail\\.ru \\(/mH' reconf['FAKE_RECEIVED_mail_ru'] = { re = string.format('(%s) & !(((%s) | (%s)) & (%s))', @@ -720,26 +731,26 @@ reconf['FAKE_RECEIVED_mail_ru'] = { group = 'headers' } -local GMAIL_COM_Return_Path = 'Return-path=/^\\s*<.+\\@gmail\\.com>$/iX' -local GMAIL_COM_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@gmail\\.com>$/iX' -local GMAIL_COM_From = 'From=/\\@gmail\\.com>?$/iX' +local GMAIL_COM_Return_Path = 'Return-path=/^\\s*<.+\\@gmail\\.com>$/iX' +local GMAIL_COM_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@gmail\\.com>$/iX' +local GMAIL_COM_From = 'From=/\\@gmail\\.com>?$/iX' -local UKR_NET_Return_Path = 'Return-path=/^\\s*<.+\\@ukr\\.net>$/iX' -local UKR_NET_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@ukr\\.net>$/iX' -local UKR_NET_From = 'From=/\\@ukr\\.net>?$/iX' +local UKR_NET_Return_Path = 'Return-path=/^\\s*<.+\\@ukr\\.net>$/iX' +local UKR_NET_X_Envelope_From = 'X-Envelope-From=/^\\s*<.+\\@ukr\\.net>$/iX' +local UKR_NET_From = 'From=/\\@ukr\\.net>?$/iX' -local RECEIVED_smtp_yandex_ru_1 = 'Received=/from \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] \\((port=\\d+ )?helo=smtp\\.yandex\\.ru\\)/iX' -local RECEIVED_smtp_yandex_ru_2 = 'Received=/from \\[UNAVAILABLE\\] \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]:\\d+ helo=smtp\\.yandex\\.ru\\)/iX' -local RECEIVED_smtp_yandex_ru_3 = 'Received=/from \\S+ \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]:\\d+ helo=smtp\\.yandex\\.ru\\)/iX' -local RECEIVED_smtp_yandex_ru_4 = 'Received=/from \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] \\(account \\S+ HELO smtp\\.yandex\\.ru\\)/iX' -local RECEIVED_smtp_yandex_ru_5 = 'Received=/from smtp\\.yandex\\.ru \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]\\)/iX' -local RECEIVED_smtp_yandex_ru_6 = 'Received=/from smtp\\.yandex\\.ru \\(\\S+ \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]\\)/iX' -local RECEIVED_smtp_yandex_ru_7 = 'Received=/from \\S+ \\(HELO smtp\\.yandex\\.ru\\) \\(\\S+\\@\\d+\\.\\d+\\.\\d+\\.\\d+\\)/iX' -local RECEIVED_smtp_yandex_ru_8 = 'Received=/from \\S+ \\(HELO smtp\\.yandex\\.ru\\) \\(\\d+\\.\\d+\\.\\d+\\.\\d+\\)/iX' -local RECEIVED_smtp_yandex_ru_9 = 'Received=/from \\S+ \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] helo=smtp\\.yandex\\.ru\\)/iX' +local RECEIVED_smtp_yandex_ru_1 = 'Received=/from \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] \\((port=\\d+ )?helo=smtp\\.yandex\\.ru\\)/iX' +local RECEIVED_smtp_yandex_ru_2 = 'Received=/from \\[UNAVAILABLE\\] \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]:\\d+ helo=smtp\\.yandex\\.ru\\)/iX' +local RECEIVED_smtp_yandex_ru_3 = 'Received=/from \\S+ \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]:\\d+ helo=smtp\\.yandex\\.ru\\)/iX' +local RECEIVED_smtp_yandex_ru_4 = 'Received=/from \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] \\(account \\S+ HELO smtp\\.yandex\\.ru\\)/iX' +local RECEIVED_smtp_yandex_ru_5 = 'Received=/from smtp\\.yandex\\.ru \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]\\)/iX' +local RECEIVED_smtp_yandex_ru_6 = 'Received=/from smtp\\.yandex\\.ru \\(\\S+ \\[\\d+\\.\\d+\\.\\d+\\.\\d+\\]\\)/iX' +local RECEIVED_smtp_yandex_ru_7 = 'Received=/from \\S+ \\(HELO smtp\\.yandex\\.ru\\) \\(\\S+\\@\\d+\\.\\d+\\.\\d+\\.\\d+\\)/iX' +local RECEIVED_smtp_yandex_ru_8 = 'Received=/from \\S+ \\(HELO smtp\\.yandex\\.ru\\) \\(\\d+\\.\\d+\\.\\d+\\.\\d+\\)/iX' +local RECEIVED_smtp_yandex_ru_9 = 'Received=/from \\S+ \\(\\[\\d+\\.\\d+\\.\\d+\\.\\d+\\] helo=smtp\\.yandex\\.ru\\)/iX' reconf['FAKE_RECEIVED_smtp_yandex_ru'] = { - re = string.format('(((%s) & ((%s) | (%s))) | ((%s) & ((%s) | (%s))) '.. + re = string.format('(((%s) & ((%s) | (%s))) | ((%s) & ((%s) | (%s))) ' .. ' | ((%s) & ((%s) | (%s)))) & (%s) | (%s) | (%s) | (%s) | (%s) | (%s) | (%s) | (%s) | (%s)', MAIL_RU_From, MAIL_RU_Return_Path, MAIL_RU_X_Envelope_From, GMAIL_COM_From, GMAIL_COM_Return_Path, GMAIL_COM_X_Envelope_From, UKR_NET_From, UKR_NET_Return_Path, diff --git a/rules/regexp/misc.lua b/rules/regexp/misc.lua index 4caa5da42..d723f2901 100644 --- a/rules/regexp/misc.lua +++ b/rules/regexp/misc.lua @@ -55,10 +55,10 @@ reconf['INTRODUCTION'] = { local onion_uri_v2 = '/[a-z0-9]{16}\\.onion?/{url}i' local onion_uri_v3 = '/[a-z0-9]{56}\\.onion?/{url}i' reconf['HAS_ONION_URI'] = { - re = string.format('(%s | %s)', onion_uri_v2, onion_uri_v3), - description = 'Contains .onion hidden service URI', - score = 0.0, - group = 'url' + re = string.format('(%s | %s)', onion_uri_v2, onion_uri_v3), + description = 'Contains .onion hidden service URI', + score = 0.0, + group = 'url' } local my_victim = [[/(?:victim|prey)/{words}]] @@ -82,7 +82,7 @@ reconf['LEAKED_PASSWORD_SCAM_RE'] = { check_data_images = function(task) local tp = task:get_text_parts() or {} - for _,p in ipairs(tp) do + for _, p in ipairs(tp) do if p:is_html() then local hc = p:get_html() diff --git a/rules/regexp/upstream_spam_filters.lua b/rules/regexp/upstream_spam_filters.lua index b88e85709..b92f473b5 100644 --- a/rules/regexp/upstream_spam_filters.lua +++ b/rules/regexp/upstream_spam_filters.lua @@ -52,8 +52,8 @@ reconf['SPAM_FLAG'] = { reconf['UNITEDINTERNET_SPAM'] = { re = string.format('%s || %s', - 'X-UI-Filterresults=/^junk:/H', - 'X-UI-Out-Filterresults=/^junk:/H'), + 'X-UI-Filterresults=/^junk:/H', + 'X-UI-Out-Filterresults=/^junk:/H'), score = 5.0, description = "United Internet says this message is spam", group = 'upstream_spam_filters' diff --git a/rules/rspamd.lua b/rules/rspamd.lua index 5d93dec00..39017f169 100644 --- a/rules/rspamd.lua +++ b/rules/rspamd.lua @@ -16,7 +16,7 @@ limitations under the License. -- This is main lua config file for rspamd -require "global_functions" () +require "global_functions"() config['regexp'] = {} rspamd_maps = {} -- Global maps @@ -53,11 +53,13 @@ if rspamd_util.file_exists(local_conf .. '/local.d/rspamd.lua') then dofile(local_conf .. '/local.d/rspamd.lua') end -local rmaps = rspamd_config:get_all_opt("lua_maps") +local rmaps = rspamd_config:get_all_opt("lua_maps") if rmaps and type(rmaps) == 'table' then local rspamd_logger = require "rspamd_logger" - for k,v in pairs(rmaps) do - local status,map_or_err = pcall(function () return rspamd_config:add_map(v) end) + for k, v in pairs(rmaps) do + local status, map_or_err = pcall(function() + return rspamd_config:add_map(v) + end) if not status then rspamd_logger.errx(rspamd_config, "cannot add map %s: %s", k, map_or_err) diff --git a/rules/subject_checks.lua b/rules/subject_checks.lua index a21dc86cc..f781e1d6d 100644 --- a/rules/subject_checks.lua +++ b/rules/subject_checks.lua @@ -22,8 +22,8 @@ local subject_re = rspamd_regexp.create('/^(?:(?:Re|Fwd|Fw|Aw|Antwort|Sv):\\s*)+ local function test_subject(task, check_function, rate) local function normalize_linear(a, x) - local f = a * x - return true, (( f < 1 ) and f or 1), tostring(x) + local f = a * x + return true, ((f < 1) and f or 1), tostring(x) end local sbj = task:get_header('Subject') @@ -48,7 +48,7 @@ rspamd_config.SUBJ_ALL_CAPS = { local caps_test = function(sbj) return util.is_uppercase(sbj) end - return test_subject(task, caps_test, 1.0/40.0) + return test_subject(task, caps_test, 1.0 / 40.0) end, score = 3.0, group = 'subject', @@ -61,7 +61,7 @@ rspamd_config.LONG_SUBJ = { local length_test = function(_, len) return len > 200 end - return test_subject(task, length_test, 1.0/400.0) + return test_subject(task, length_test, 1.0 / 400.0) end, score = 3.0, group = 'subject', |