From a3b5ad3d3be5f44996d247e7b1486fcc9f8ba7e3 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 6 Aug 2021 12:42:06 +0100 Subject: [PATCH] [Feature] Allow to save and show attachment name when inserting AV scan results --- lualib/lua_scanners/avast.lua | 13 ++++---- lualib/lua_scanners/clamav.lua | 24 +++++++++------ lualib/lua_scanners/common.lua | 46 +++++++++++++++++++--------- lualib/lua_scanners/fprot.lua | 12 +++++--- lualib/lua_scanners/icap.lua | 30 +++++++++++------- lualib/lua_scanners/kaspersky_av.lua | 16 ++++++---- lualib/lua_scanners/kaspersky_se.lua | 24 +++++++++------ lualib/lua_scanners/oletools.lua | 39 ++++++++++++++--------- lualib/lua_scanners/sophos.lua | 26 ++++++++++------ lualib/lua_scanners/vadesecure.lua | 5 +-- lualib/lua_scanners/virustotal.lua | 6 ++-- src/plugins/lua/antivirus.lua | 4 +-- src/plugins/lua/mime_types.lua | 3 +- 13 files changed, 155 insertions(+), 93 deletions(-) diff --git a/lualib/lua_scanners/avast.lua b/lualib/lua_scanners/avast.lua index 16c838dbc..da8e6d710 100644 --- a/lualib/lua_scanners/avast.lua +++ b/lualib/lua_scanners/avast.lua @@ -81,7 +81,7 @@ local function avast_config(opts) return nil end -local function avast_check(task, content, digest, rule) +local function avast_check(task, content, digest, rule, maybe_part) local function avast_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -148,7 +148,7 @@ local function avast_check(task, content, digest, rule) '%s [%s]: failed to scan, maximum retransmits exceed', rule['symbol'], rule['type']) common.yield_result(task, rule, 'failed to scan and retransmits exceed', - 0.0, 'fail') + 0.0, 'fail', maybe_part) return end @@ -251,7 +251,7 @@ local function avast_check(task, content, digest, rule) if vname then vname = vname:gsub('\\ ', ' '):gsub('\\\\', '\\') - common.yield_result(task, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) cached = vname end end @@ -263,12 +263,12 @@ local function avast_check(task, content, digest, rule) if ret then rspamd_logger.errx(task, '%s: error: %s', rule.log_prefix, ret[1][2]) common.yield_result(task, rule, 'error:' .. ret[1][2], - 0.0, 'fail') + 0.0, 'fail', maybe_part) end end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) else -- Unexpected reply rspamd_logger.errx(task, '%s: unexpected reply: %s', rule.log_prefix, mdata) @@ -286,7 +286,8 @@ local function avast_check(task, content, digest, rule) maybe_retransmit() end - if common.condition_check_and_continue(task, content, rule, digest, avast_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + avast_check_uncached, maybe_part) then return else avast_check_uncached() diff --git a/lualib/lua_scanners/clamav.lua b/lualib/lua_scanners/clamav.lua index 7c56f5798..bc090036c 100644 --- a/lualib/lua_scanners/clamav.lua +++ b/lualib/lua_scanners/clamav.lua @@ -79,7 +79,7 @@ local function clamav_config(opts) return nil end -local function clamav_check(task, content, digest, rule) +local function clamav_check(task, content, digest, rule, maybe_part) local function clamav_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -117,7 +117,9 @@ local function clamav_check(task, content, digest, rule) }) else rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits exceed', rule.log_prefix) - common.yield_result(task, rule, 'failed to scan and retransmits exceed', 0.0, 'fail') + common.yield_result(task, rule, + 'failed to scan and retransmits exceed', 0.0, 'fail', + maybe_part) end else @@ -138,25 +140,28 @@ local function clamav_check(task, content, digest, rule) local vname = string.match(data, 'stream: (.+) FOUND') if string.find(vname, '^Heuristics%.Encrypted') then rspamd_logger.errx(task, '%s: File is encrypted', rule.log_prefix) - common.yield_result(task, rule, 'File is encrypted: '.. vname, 0.0, 'encrypted') + common.yield_result(task, rule, 'File is encrypted: '.. vname, + 0.0, 'encrypted', maybe_part) cached = 'ENCRYPTED' elseif string.find(vname, '^Heuristics%.OLE2%.ContainsMacros') then rspamd_logger.errx(task, '%s: ClamAV Found an OLE2 Office Macro', rule.log_prefix) - common.yield_result(task, rule, vname, 0.0, 'macro') + common.yield_result(task, rule, vname, 0.0, 'macro', maybe_part) cached = 'MACRO' elseif string.find(vname, '^Heuristics%.Limits%.Exceeded') then rspamd_logger.errx(task, '%s: ClamAV Limits Exceeded', rule.log_prefix) - common.yield_result(task, rule, 'Limits Exceeded: '.. vname, 0.0, 'fail') + common.yield_result(task, rule, 'Limits Exceeded: '.. vname, 0.0, + 'fail', maybe_part) elseif vname then - common.yield_result(task, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) cached = vname else rspamd_logger.errx(task, '%s: unhandled response: %s', rule.log_prefix, data) - common.yield_result(task, rule, 'unhandled response:' .. vname, 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response:' .. vname, 0.0, + 'fail', maybe_part) end end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) end end end @@ -172,7 +177,8 @@ local function clamav_check(task, content, digest, rule) }) end - if common.condition_check_and_continue(task, content, rule, digest, clamav_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + clamav_check_uncached, maybe_part) then return else clamav_check_uncached() diff --git a/lualib/lua_scanners/common.lua b/lualib/lua_scanners/common.lua index 67952923d..bbad123f5 100644 --- a/lualib/lua_scanners/common.lua +++ b/lualib/lua_scanners/common.lua @@ -62,14 +62,21 @@ local function match_patterns(default_sym, found, patterns, dyn_weight) end end -local function yield_result(task, rule, vname, dyn_weight, is_fail) +local function yield_result(task, rule, vname, dyn_weight, is_fail, maybe_part) local all_whitelisted = true local patterns local symbol - local threat_table = {} + local threat_table local threat_info local flags + if type(vname) == 'string' then + threat_table = {vname} + elseif type(vname) == 'table' then + threat_table = vname + end + + -- This should be more generic if not is_fail then patterns = rule.patterns @@ -93,11 +100,6 @@ local function yield_result(task, rule, vname, dyn_weight, is_fail) dyn_weight = 1.0 end - if type(vname) == 'string' then - table.insert(threat_table, vname) - elseif type(vname) == 'table' then - threat_table = vname - end for _, tm in ipairs(threat_table) do local symname, symscore = match_patterns(symbol, tm, patterns, dyn_weight) @@ -107,7 +109,15 @@ local function yield_result(task, rule, vname, dyn_weight, is_fail) all_whitelisted = false rspamd_logger.infox(task, '%s: result - %s: "%s - score: %s"', rule.log_prefix, threat_info, tm, symscore) - task:insert_result(symname, symscore, tm) + + if maybe_part and rule.show_attachments and maybe_part:get_filename() then + local fname = maybe_part:get_filename() + task:insert_result(symname, symscore, string.format("%s|%s", + tm, fname)) + else + task:insert_result(symname, symscore, tm) + end + end end @@ -196,7 +206,7 @@ local function dynamic_scan(task, rule) end end -local function need_check(task, content, rule, digest, fn) +local function need_check(task, content, rule, digest, fn, maybe_part) local uncached = true local key = digest @@ -207,15 +217,18 @@ local function need_check(task, content, rule, digest, fn) data = lua_util.str_split(data, '\t') local threat_string = lua_util.str_split(data[1], '\v') local score = data[2] or rule.default_score + if threat_string[1] ~= 'OK' then if threat_string[1] == 'MACRO' then - yield_result(task, rule, 'File contains macros', 0.0, 'macro') + yield_result(task, rule, 'File contains macros', + 0.0, 'macro', maybe_part) elseif threat_string[1] == 'ENCRYPTED' then - yield_result(task, rule, 'File is encrypted', 0.0, 'encrypted') + yield_result(task, rule, 'File is encrypted', + 0.0, 'encrypted', maybe_part) else lua_util.debugm(rule.name, task, '%s: got cached threat result for %s: %s - score: %s', rule.log_prefix, key, threat_string[1], score) - yield_result(task, rule, threat_string, score) + yield_result(task, rule, threat_string, score, false, maybe_part) end else @@ -266,7 +279,7 @@ local function need_check(task, content, rule, digest, fn) end -local function save_cache(task, digest, rule, to_save, dyn_weight) +local function save_cache(task, digest, rule, to_save, dyn_weight, maybe_part) local key = digest if not dyn_weight then dyn_weight = 1.0 end @@ -285,7 +298,12 @@ local function save_cache(task, digest, rule, to_save, dyn_weight) to_save = table.concat(to_save, '\v') end - local value = table.concat({to_save, dyn_weight}, '\t') + local value_tbl = {to_save, dyn_weight} + if maybe_part and rule.show_attachments and maybe_part:get_filename() then + local fname = maybe_part:get_filename() + table.insert(value_tbl, fname) + end + local value = table.concat(value_tbl, '\t') if rule.redis_params and rule.prefix then key = rule.prefix .. key diff --git a/lualib/lua_scanners/fprot.lua b/lualib/lua_scanners/fprot.lua index 8baaeedde..4c416524c 100644 --- a/lualib/lua_scanners/fprot.lua +++ b/lualib/lua_scanners/fprot.lua @@ -78,7 +78,7 @@ local function fprot_config(opts) return nil end -local function fprot_check(task, content, digest, rule) +local function fprot_check(task, content, digest, rule, maybe_part) local function fprot_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -119,7 +119,8 @@ local function fprot_check(task, content, digest, rule) rspamd_logger.errx(task, '%s [%s]: failed to scan, maximum retransmits exceed', rule['symbol'], rule['type']) - common.yield_result(task, rule, 'failed to scan and retransmits exceed', 0.0, 'fail') + common.yield_result(task, rule, 'failed to scan and retransmits exceed', + 0.0, 'fail', maybe_part) end else upstream:ok() @@ -140,12 +141,12 @@ local function fprot_check(task, content, digest, rule) if not vname then rspamd_logger.errx(task, 'Unhandled response: %s', data) else - common.yield_result(task, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) cached = vname end end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) end end end @@ -161,7 +162,8 @@ local function fprot_check(task, content, digest, rule) }) end - if common.condition_check_and_continue(task, content, rule, digest, fprot_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + fprot_check_uncached, maybe_part) then return else fprot_check_uncached() diff --git a/lualib/lua_scanners/icap.lua b/lualib/lua_scanners/icap.lua index af9d070dc..86def8edd 100644 --- a/lualib/lua_scanners/icap.lua +++ b/lualib/lua_scanners/icap.lua @@ -98,7 +98,7 @@ local function icap_config(opts) return nil end -local function icap_check(task, content, digest, rule) +local function icap_check(task, content, digest, rule, maybe_part) local function icap_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -162,7 +162,8 @@ local function icap_check(task, content, digest, rule) else rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits '.. 'exceed - error: %s', rule.log_prefix, err_m or '') - common.yield_result(task, rule, 'failed - error: ' .. err_m or '', 0.0, 'fail') + common.yield_result(task, rule, 'failed - error: ' .. err_m or '', + 0.0, 'fail', maybe_part) end end @@ -248,7 +249,8 @@ local function icap_check(task, content, digest, rule) -- error returned lua_util.debugm(rule.name, task, '%s: icap error X-Infection-Found: %s', rule.log_prefix, icap_threat) - common.yield_result(task, rule, icap_threat, 0, 'fail') + common.yield_result(task, rule, icap_threat, 0, + 'fail', maybe_part) else lua_util.debugm(rule.name, task, '%s: icap X-Infection-Found: %s', rule.log_prefix, icap_threat) @@ -295,10 +297,10 @@ local function icap_check(task, content, digest, rule) end end if #threat_string > 0 then - common.yield_result(task, rule, threat_string, rule.default_score) - common.save_cache(task, digest, rule, threat_string, rule.default_score) + common.yield_result(task, rule, threat_string, rule.default_score, nil, maybe_part) + common.save_cache(task, digest, rule, threat_string, rule.default_score, maybe_part) else - common.save_cache(task, digest, rule, 'OK', 0) + common.save_cache(task, digest, rule, 'OK', 0, maybe_part) common.log_clean(task, rule) end end @@ -323,12 +325,15 @@ local function icap_check(task, content, digest, rule) ICAP/1.0 500 Server error ]]-- rspamd_logger.errx(task, '%s: ICAP ERROR: %s', rule.log_prefix, icap_headers.icap) - common.yield_result(task, rule, icap_headers.icap, 0.0, 'fail') + common.yield_result(task, rule, icap_headers.icap, 0.0, + 'fail', maybe_part) return false else rspamd_logger.errx(task, '%s: unhandled response |%s|', rule.log_prefix, string.gsub(result, "\r\n", ", ")) - common.yield_result(task, rule, 'unhandled icap response: ' .. icap_headers.icap or "-", 0.0, 'fail') + common.yield_result(task, rule, + 'unhandled icap response: ' .. icap_headers.icap or "-", + 0.0, 'fail', maybe_part) end end end @@ -371,12 +376,14 @@ local function icap_check(task, content, digest, rule) else rspamd_logger.errx(task, '%s: RESPMOD method not advertised: Methods: %s', rule.log_prefix, icap_headers['Methods']) - common.yield_result(task, rule, 'NO RESPMOD', 0.0, 'fail') + common.yield_result(task, rule, 'NO RESPMOD', 0.0, + 'fail', maybe_part) end else rspamd_logger.errx(task, '%s: OPTIONS query failed: %s', rule.log_prefix, icap_headers.icap or "-") - common.yield_result(task, rule, 'OPTIONS query failed', 0.0, 'fail') + common.yield_result(task, rule, 'OPTIONS query failed', 0.0, + 'fail', maybe_part) end end end @@ -402,7 +409,8 @@ local function icap_check(task, content, digest, rule) }) end - if common.condition_check_and_continue(task, content, rule, digest, icap_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + icap_check_uncached, maybe_part) then return else icap_check_uncached() diff --git a/lualib/lua_scanners/kaspersky_av.lua b/lualib/lua_scanners/kaspersky_av.lua index 4bdbdbbfd..c07a910cd 100644 --- a/lualib/lua_scanners/kaspersky_av.lua +++ b/lualib/lua_scanners/kaspersky_av.lua @@ -79,7 +79,7 @@ local function kaspersky_config(opts) return nil end -local function kaspersky_check(task, content, digest, rule) +local function kaspersky_check(task, content, digest, rule, maybe_part) local function kaspersky_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -138,7 +138,9 @@ local function kaspersky_check(task, content, digest, rule) rspamd_logger.errx(task, '%s [%s]: failed to scan, maximum retransmits exceed', rule['symbol'], rule['type']) - common.yield_result(task, rule, 'failed to scan and retransmits exceed', 0.0, 'fail') + common.yield_result(task, rule, + 'failed to scan and retransmits exceed', 0.0, 'fail', + maybe_part) end else @@ -154,15 +156,16 @@ local function kaspersky_check(task, content, digest, rule) else local vname = string.match(data, ': (.+) FOUND') if vname then - common.yield_result(task, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) cached = vname else rspamd_logger.errx(task, 'unhandled response: %s', data) - common.yield_result(task, rule, 'unhandled response', 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response', + 0.0, 'fail', maybe_part) end end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) end end end @@ -178,7 +181,8 @@ local function kaspersky_check(task, content, digest, rule) }) end - if common.condition_check_and_continue(task, content, rule, digest, kaspersky_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + kaspersky_check_uncached, maybe_part) then return else kaspersky_check_uncached() diff --git a/lualib/lua_scanners/kaspersky_se.lua b/lualib/lua_scanners/kaspersky_se.lua index cac604016..bf1fb9568 100644 --- a/lualib/lua_scanners/kaspersky_se.lua +++ b/lualib/lua_scanners/kaspersky_se.lua @@ -88,7 +88,7 @@ local function kaspersky_se_config(opts) return nil end -local function kaspersky_se_check(task, content, digest, rule) +local function kaspersky_se_check(task, content, digest, rule, maybe_part) local function kaspersky_se_check_uncached() local function make_url(addr) local url @@ -220,19 +220,21 @@ local function kaspersky_se_check(task, content, digest, rule) rule.log_prefix) end elseif data == 'CLEAN AND CONTAINS OFFICE MACRO' then - common.yield_result(task, rule, 'File contains macros', 0.0, 'macro') + common.yield_result(task, rule, 'File contains macros', + 0.0, 'macro', maybe_part) cached = 'MACRO' else rspamd_logger.errx(task, '%s: unhandled clean response: %s', rule.log_prefix, data) - common.yield_result(task, rule, 'unhandled response:' .. data, 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response:' .. data, + 0.0, 'fail', maybe_part) end elseif data == 'SERVER_ERROR' then rspamd_logger.errx(task, '%s: error: %s', rule.log_prefix, data) common.yield_result(task, rule, 'error:' .. data, - 0.0, 'fail') + 0.0, 'fail', maybe_part) elseif string.match(data, 'DETECT (.+)') then local vname = string.match(data, 'DETECT (.+)') - common.yield_result(task, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) cached = vname elseif string.match(data, 'NON_SCANNED %((.+)%)') then local why = string.match(data, 'NON_SCANNED %((.+)%)') @@ -240,18 +242,20 @@ local function kaspersky_se_check(task, content, digest, rule) if why == 'PASSWORD PROTECTED' then rspamd_logger.errx(task, '%s: File is encrypted', rule.log_prefix) common.yield_result(task, rule, 'File is encrypted: '.. why, - 0.0, 'encrypted') + 0.0, 'encrypted', maybe_part) cached = 'ENCRYPTED' else - common.yield_result(task, rule, 'unhandled response:' .. data, 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response:' .. data, + 0.0, 'fail', maybe_part) end else rspamd_logger.errx(task, '%s: unhandled response: %s', rule.log_prefix, data) - common.yield_result(task, rule, 'unhandled response:' .. data, 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response:' .. data, + 0.0, 'fail', maybe_part) end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) end end @@ -262,7 +266,7 @@ local function kaspersky_se_check(task, content, digest, rule) end if common.condition_check_and_continue(task, content, rule, digest, - kaspersky_se_check_uncached) then + kaspersky_se_check_uncached, maybe_part) then return else diff --git a/lualib/lua_scanners/oletools.lua b/lualib/lua_scanners/oletools.lua index 1121b3226..e76a25911 100644 --- a/lualib/lua_scanners/oletools.lua +++ b/lualib/lua_scanners/oletools.lua @@ -87,7 +87,7 @@ local function oletools_config(opts) return nil end -local function oletools_check(task, content, digest, rule) +local function oletools_check(task, content, digest, rule, maybe_part) local function oletools_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -125,7 +125,9 @@ local function oletools_check(task, content, digest, rule) else rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits '.. 'exceed - err: %s', rule.log_prefix, error) - common.yield_result(task, rule, 'failed to scan, maximum retransmits exceed - err: ' .. error, 0.0, 'fail') + common.yield_result(task, rule, + 'failed to scan, maximum retransmits exceed - err: ' .. error, + 0.0, 'fail', maybe_part) end end @@ -191,7 +193,7 @@ local function oletools_check(task, content, digest, rule) rspamd_logger.errx(task, '%s: ERROR found: %s', rule.log_prefix, v.error) if v.error == 'File too small' then - common.save_cache(task, digest, rule, 'OK') + common.save_cache(task, digest, rule, 'OK', 1.0, maybe_part) common.log_clean(task, rule, 'File too small to be scanned for macros') return else @@ -210,20 +212,26 @@ local function oletools_check(task, content, digest, rule) local oletools_rc_code = tonumber(v.return_code) if oletools_rc_code == 9 then rspamd_logger.warnx(task, '%s: File is encrypted.', rule.log_prefix) - common.yield_result(task, rule, 'failed - err: ' .. oletools_rc[oletools_rc_code], 0.0, 'encrypted') - common.save_cache(task, digest, rule, 'encrypted') + common.yield_result(task, rule, + 'failed - err: ' .. oletools_rc[oletools_rc_code], + 0.0, 'encrypted', maybe_part) + common.save_cache(task, digest, rule, 'encrypted', 1.0, maybe_part) return elseif oletools_rc_code == 5 then rspamd_logger.warnx(task, '%s: olefy could not open the file - error: %s', rule.log_prefix, result[2]['message']) - common.yield_result(task, rule, 'failed - err: ' .. oletools_rc[oletools_rc_code], 0.0, 'fail') + common.yield_result(task, rule, + 'failed - err: ' .. oletools_rc[oletools_rc_code], + 0.0, 'fail', maybe_part) return elseif oletools_rc_code > 6 then rspamd_logger.errx(task, '%s: MetaInfo section error code: %s', rule.log_prefix, oletools_rc[oletools_rc_code]) rspamd_logger.errx(task, '%s: MetaInfo section message: %s', rule.log_prefix, result[2]['message']) - common.yield_result(task, rule, 'failed - err: ' .. oletools_rc[oletools_rc_code], 0.0, 'fail') + common.yield_result(task, rule, + 'failed - err: ' .. oletools_rc[oletools_rc_code], + 0.0, 'fail', maybe_part) return elseif oletools_rc_code > 1 then rspamd_logger.errx(task, '%s: Error message: %s', @@ -296,8 +304,8 @@ local function oletools_check(task, content, digest, rule) -- use single string as virus name local threat = 'AutoExec + Suspicious (' .. table.concat(analysis_keyword_table, ',') .. ')' lua_util.debugm(rule.name, task, '%s: threat result: %s', rule.log_prefix, threat) - common.yield_result(task, rule, threat, rule.default_score) - common.save_cache(task, digest, rule, threat, rule.default_score) + common.yield_result(task, rule, threat, rule.default_score, nil, maybe_part) + common.save_cache(task, digest, rule, threat, rule.default_score, maybe_part) elseif rule.extended == true and #analysis_keyword_table > 0 then -- report any flags (types) and any most keywords as individual virus name @@ -317,15 +325,17 @@ local function oletools_check(task, content, digest, rule) lua_util.debugm(rule.name, task, '%s: extended threat result: %s', rule.log_prefix, table.concat(analysis_keyword_table, ',')) - common.yield_result(task, rule, analysis_keyword_table, rule.default_score) - common.save_cache(task, digest, rule, analysis_keyword_table, rule.default_score) + common.yield_result(task, rule, analysis_keyword_table, + rule.default_score, nil, maybe_part) + common.save_cache(task, digest, rule, analysis_keyword_table, + rule.default_score, maybe_part) elseif analysis_cat_table.macro_exist == '-' and #analysis_keyword_table == 0 then - common.save_cache(task, digest, rule, 'OK') + common.save_cache(task, digest, rule, 'OK', 1.0, maybe_part) common.log_clean(task, rule, 'No macro found') else - common.save_cache(task, digest, rule, 'OK') + common.save_cache(task, digest, rule, 'OK', 1.0, maybe_part) common.log_clean(task, rule, 'Scanned Macro is OK') end end @@ -344,7 +354,8 @@ local function oletools_check(task, content, digest, rule) end - if common.condition_check_and_continue(task, content, rule, digest, oletools_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + oletools_check_uncached, maybe_part) then return else oletools_check_uncached() diff --git a/lualib/lua_scanners/sophos.lua b/lualib/lua_scanners/sophos.lua index bfc1644ac..36ebe4177 100644 --- a/lualib/lua_scanners/sophos.lua +++ b/lualib/lua_scanners/sophos.lua @@ -78,7 +78,7 @@ local function sophos_config(opts) return nil end -local function sophos_check(task, content, digest, rule) +local function sophos_check(task, content, digest, rule, maybe_part) local function sophos_check_uncached () local upstream = rule.upstreams:get_upstream_round_robin() local addr = upstream:get_addr() @@ -115,7 +115,8 @@ local function sophos_check(task, content, digest, rule) }) else rspamd_logger.errx(task, '%s [%s]: failed to scan, maximum retransmits exceed', rule['symbol'], rule['type']) - common.yield_result(task, rule, 'failed to scan and retransmits exceed', 0.0, 'fail') + common.yield_result(task, rule, 'failed to scan and retransmits exceed', + 0.0, 'fail', maybe_part) end else upstream:ok() @@ -125,8 +126,8 @@ local function sophos_check(task, content, digest, rule) local vname = string.match(data, 'VIRUS (%S+) ') local cached if vname then - common.yield_result(task, rule, vname) - common.save_cache(task, digest, rule, vname) + common.yield_result(task, rule, vname, 1.0, nil, maybe_part) + common.save_cache(task, digest, rule, vname, 1.0, maybe_part) else if string.find(data, 'DONE OK') then if rule['log_clean'] then @@ -141,21 +142,25 @@ local function sophos_check(task, content, digest, rule) conn:add_read(sophos_callback) elseif string.find(data, 'FAIL 0212') then rspamd_logger.warnx(task, 'Message is encrypted (FAIL 0212): %s', data) - common.yield_result(task, rule, 'SAVDI: Message is encrypted (FAIL 0212)', 0.0, 'encrypted') + common.yield_result(task, rule, 'SAVDI: Message is encrypted (FAIL 0212)', + 0.0, 'encrypted', maybe_part) cached = 'ENCRYPTED' elseif string.find(data, 'REJ 4') then rspamd_logger.warnx(task, 'Message is oversized (REJ 4): %s', data) - common.yield_result(task, rule, 'SAVDI: Message oversized (REJ 4)', 0.0, 'fail') + common.yield_result(task, rule, 'SAVDI: Message oversized (REJ 4)', + 0.0, 'fail', maybe_part) -- excplicitly set REJ1 message when SAVDIreports a protocol error elseif string.find(data, 'REJ 1') then rspamd_logger.errx(task, 'SAVDI (Protocol error (REJ 1)): %s', data) - common.yield_result(task, rule, 'SAVDI: Protocol error (REJ 1)', 0.0, 'fail') + common.yield_result(task, rule, 'SAVDI: Protocol error (REJ 1)', + 0.0, 'fail', maybe_part) else rspamd_logger.errx(task, 'unhandled response: %s', data) - common.yield_result(task, rule, 'unhandled response: ' .. data, 0.0, 'fail') + common.yield_result(task, rule, 'unhandled response: ' .. data, + 0.0, 'fail', maybe_part) end if cached then - common.save_cache(task, digest, rule, cached) + common.save_cache(task, digest, rule, cached, 1.0, maybe_part) end end end @@ -171,7 +176,8 @@ local function sophos_check(task, content, digest, rule) }) end - if common.condition_check_and_continue(task, content, rule, digest, sophos_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + sophos_check_uncached, maybe_part) then return else sophos_check_uncached() diff --git a/lualib/lua_scanners/vadesecure.lua b/lualib/lua_scanners/vadesecure.lua index cbad42d4f..158eedb1a 100644 --- a/lualib/lua_scanners/vadesecure.lua +++ b/lualib/lua_scanners/vadesecure.lua @@ -152,7 +152,7 @@ local function vade_config(opts) return nil end -local function vade_check(task, content, digest, rule) +local function vade_check(task, content, digest, rule, maybe_part) local function vade_check_uncached() local function vade_url(addr) local url @@ -331,7 +331,8 @@ local function vade_check(task, content, digest, rule) http.request(request_data) end - if common.condition_check_and_continue(task, content, rule, digest, vade_check_uncached) then + if common.condition_check_and_continue(task, content, rule, digest, + vade_check_uncached, maybe_part) then return else vade_check_uncached() diff --git a/lualib/lua_scanners/virustotal.lua b/lualib/lua_scanners/virustotal.lua index 6bbdf94fd..8c5d71dbb 100644 --- a/lualib/lua_scanners/virustotal.lua +++ b/lualib/lua_scanners/virustotal.lua @@ -74,7 +74,7 @@ local function virustotal_config(opts) return default_conf end -local function virustotal_check(task, content, digest, rule) +local function virustotal_check(task, content, digest, rule, maybe_part) local function virustotal_check_uncached() local function make_url(hash) return string.format('%s/report?apikey=%s&resource=%s', @@ -172,7 +172,7 @@ local function virustotal_check(task, content, digest, rule) end local sopt = string.format("%s:%s/%s", hash, obj.positives, obj.total) - common.yield_result(task, rule, sopt, dyn_score) + common.yield_result(task, rule, sopt, dyn_score, nil, maybe_part) cached = sopt end end @@ -186,7 +186,7 @@ local function virustotal_check(task, content, digest, rule) end if cached then - common.save_cache(task, digest, rule, cached, dyn_score) + common.save_cache(task, digest, rule, cached, dyn_score, maybe_part) end end end diff --git a/src/plugins/lua/antivirus.lua b/src/plugins/lua/antivirus.lua index 18d41dbb6..56bd4b520 100644 --- a/src/plugins/lua/antivirus.lua +++ b/src/plugins/lua/antivirus.lua @@ -136,7 +136,7 @@ local function add_antivirus_rule(sym, opts) fun.each(function(p) local content = p:get_content() if content and #content > 0 then - cfg.check(task, content, p:get_digest(), rule) + cfg.check(task, content, p:get_digest(), rule, p) end end, common.check_parts_match(task, rule)) @@ -266,7 +266,7 @@ if opts and type(opts) == 'table' then }) end end - end + end if m['score'] then -- Register metric symbol local description = 'antivirus symbol' diff --git a/src/plugins/lua/mime_types.lua b/src/plugins/lua/mime_types.lua index 627c27f59..edfc6d002 100644 --- a/src/plugins/lua/mime_types.lua +++ b/src/plugins/lua/mime_types.lua @@ -222,7 +222,8 @@ local function check_mime_type(task) end -- Decode hex encoded characters - fname = string.gsub(fname, '%%(%x%x)', function (hex) return string.char(tonumber(hex,16)) end ) + fname = string.gsub(fname, '%%(%x%x)', + function (hex) return string.char(tonumber(hex,16)) end ) -- Replace potentially bad characters with '?' fname = fname:gsub('[^%s%g]', '?') -- 2.39.5