commit a2748a0: [Project] Settings: Add expression aliases

Vsevolod Stakhov vsevolod at highsecure.ru
Tue Jun 25 17:42:04 UTC 2019


Author: Vsevolod Stakhov
Date: 2019-06-25 18:37:09 +0100
URL: https://github.com/rspamd/rspamd/commit/a2748a06d9e4c50ac31e2c2669ef2cd8b882b662

[Project] Settings: Add expression aliases

---
 src/plugins/lua/settings.lua | 85 ++++++++++++++++++++++++++++++--------------
 1 file changed, 58 insertions(+), 27 deletions(-)

diff --git a/src/plugins/lua/settings.lua b/src/plugins/lua/settings.lua
index f269a4018..4b129db0f 100644
--- a/src/plugins/lua/settings.lua
+++ b/src/plugins/lua/settings.lua
@@ -261,7 +261,7 @@ local function check_settings(task)
         if not input then return false end
 
         if elt.check(input) then
-          matched[#matched] = atom
+          matched[#matched + 1] = atom
           return 1.0
         end
       else
@@ -302,8 +302,8 @@ local function check_settings(task)
       for _,s in ipairs(settings[pri]) do
         local matched = {}
 
-        lua_util.debugm(N, task, "check for settings element %s; %s",
-            s.name, s.rule.expression)
+        lua_util.debugm(N, task, "check for settings element %s",
+            s.name)
         local result = check_specific_setting(s.rule, matched)
         -- Can use xor here but more complicated for reading
         if result then
@@ -702,6 +702,41 @@ local function process_settings_table(tbl, allow_ids, mempool)
       }
     end
 
+    local aliases = {}
+    -- This function is used to convert compound condition with
+    -- generic type and specific part (e.g. `header`, `Content-Transfer-Encoding`)
+    -- to a set of usable check elements:
+    -- `generic:specific` - most common part
+    -- `generic:<order>` - e.g. `header:1` for the first header
+    -- `generic:safe` - replace unsafe stuff with safe + lowercase
+    -- also aliases entry is set to avoid implicit expression
+    local function process_compound_condition(cond, generic, specific)
+      local full_key = generic .. ':' .. specific
+      checks[full_key] = cond
+
+      -- Try numeric key
+      for i=1,1000 do
+        local num_key = generic .. ':' .. tostring(i)
+        if not checks[num_key]  then
+          checks[num_key] = cond
+          aliases[num_key] = true
+          break
+        end
+      end
+
+      local safe_key = generic .. ':' ..
+          specific:gsub('[:%-+&|><]', '_')
+                  :gsub('%(', '[')
+                  :gsub('%)', ']')
+                  :lower()
+
+      if not checks[safe_key] then
+        checks[safe_key] = cond
+        aliases[safe_key] = true
+      end
+
+      return safe_key
+  end
     -- Headers are tricky:
     -- We create an closure with extraction function depending on header name
     -- We also inserts it into `checks` table as an atom in form header:<hname>
@@ -714,30 +749,30 @@ local function process_settings_table(tbl, allow_ids, mempool)
           if type(v) == 'string' then
             local re = rspamd_regexp.create(v)
             if re then
-              checks[table_element .. ':'..k] = {
+              local cond = {
                 check = function(values)
                   return fun.any(function(c) return re:match(c) end, values)
                 end,
                 extract = extractor_func(k),
               }
-
+              local skey = process_compound_condition(cond, table_element,
+                  k)
               lua_util.debugm(N, rspamd_config, 'added %s condition to "%s": %s =~ %s',
-                  table_element, name, k, v)
+                  skey, name, k, v)
             end
           elseif type(v) == 'boolean' then
-            checks[table_element .. ':'..k] = {
+            local cond = {
               check = function(values)
-                return fun.any(function(c)
-                  if c and v then return true end
-                  if not c or not v then return true end
-                  return false
-                end, values)
+                if #values == 0 then return (not v) end
+                return v
               end,
               extract = extractor_func(k),
             }
 
-            lua_util.debugm(N, rspamd_config, 'added %s condition to "%s": %s =~ %s',
-                table_element, name, k, v)
+            local skey = process_compound_condition(cond, table_element,
+                k)
+            lua_util.debugm(N, rspamd_config, 'added %s condition to "%s": %s == %s',
+                skey, name, k, v)
           else
             rspamd_logger.errx(rspamd_config, 'invalid %s %s = %s', table_element, k, v)
           end
@@ -756,25 +791,18 @@ local function process_settings_table(tbl, allow_ids, mempool)
       return function(task)
         local rh = task:get_header_full(hname)
         if rh then
-          return fun.map(function(h) return h.decoded end, rh)
+          return fun.totable(fun.map(function(h) return h.decoded end, rh))
         end
         return {}
       end
     end)
 
     if elt['selector'] then
-      local sel = selectors_cache[name]
-      if not sel then
-        sel = lua_selectors.create_selector_closure(rspamd_config, elt.selector,
-            elt.delimiter or "")
-
-        if sel then
-          selectors_cache[name] = sel
-        end
-      end
+      local sel = lua_selectors.create_selector_closure(rspamd_config, elt.selector,
+          elt.delimiter or "")
 
       if sel then
-        checks['selector:' .. name] = {
+        local cond = {
           check = function(values)
             return fun.any(function(c)
               return c
@@ -782,8 +810,9 @@ local function process_settings_table(tbl, allow_ids, mempool)
           end,
           extract = sel,
         }
+        local skey = process_compound_condition(cond, 'selector', elt.selector)
         lua_util.debugm(N, rspamd_config, 'added selector condition to "%s": %s',
-            name, sel)
+            name, skey)
       end
 
     end
@@ -808,8 +837,10 @@ local function process_settings_table(tbl, allow_ids, mempool)
         local s = ' && '
         -- By De Morgan laws
         if inverse then s = ' || ' end
+        -- Exclude aliases and join all checks by key
         local expr_str = table.concat(fun.totable(fun.map(function(k, _)
-          return k end, checks)), s)
+          return k end,
+            fun.filter(function(k, _) return not aliases[k] end, checks))), s)
 
         if inverse then
           expr_str = string.format('!(%s)', expr_str)


More information about the Commits mailing list