]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Add remove_ar_from option for selective Authentication-Results header removal
authorVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 8 Dec 2025 09:10:46 +0000 (09:10 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 8 Dec 2025 09:10:46 +0000 (09:10 +0000)
lualib/lua_auth_results.lua
src/plugins/lua/milter_headers.lua

index 8c907d9b7198133e564fa8fac0f7c745b6596b25..d82d6ee73f86939b3c9866cbc1d7d0802fc4e7b3 100644 (file)
@@ -298,4 +298,19 @@ local function parse_ar_element(elt)
 end
 exports.parse_ar_element = parse_ar_element
 
+local function get_ar_hostname(ar_value)
+  if not ar_value or ar_value == '' then
+    return nil
+  end
+
+  local hostname = ar_value:match('^%s*([^;%s%(]+)')
+
+  if hostname then
+    return hostname:lower()
+  end
+
+  return nil
+end
+exports.get_ar_hostname = get_ar_hostname
+
 return exports
index 26406a5c4b2b305fb96408b3c848b9c09f4bcd44..0894480cfbfc828ee2c72b188a3e4755603ec5ea 100644 (file)
@@ -112,6 +112,7 @@ local settings = {
     ['authentication-results'] = {
       header = 'Authentication-Results',
       remove = 0,
+      remove_ar_from = nil,
       add_smtp_user = true,
       stop_chars = ';',
     },
@@ -538,14 +539,70 @@ local function milter_headers(task)
       return
     end
     local ar = require "lua_auth_results"
+    local local_mod = settings.routines['authentication-results']
+
+    if local_mod.remove_ar_from then
+      local hdr_name = local_mod.header
+      local existing_hdrs = task:get_header_full(hdr_name)
+
+      if existing_hdrs and #existing_hdrs > 0 then
+        local indices_to_remove = {}
+
+        for idx, hdr in ipairs(existing_hdrs) do
+          local ar_hostname = ar.get_ar_hostname(hdr.decoded or hdr.value)
+          if ar_hostname then
+            local should_remove = false
+
+            if type(local_mod.remove_ar_from) == 'userdata' then
+              if local_mod.remove_ar_from:get_key(ar_hostname) then
+                should_remove = true
+              else
+                local domain_part = ar_hostname:match('%.(.+)$')
+                if domain_part and local_mod.remove_ar_from:get_key('.' .. domain_part) then
+                  should_remove = true
+                end
+              end
+            elseif type(local_mod.remove_ar_from) == 'table' then
+              for _, pattern in ipairs(local_mod.remove_ar_from) do
+                local pattern_lower = pattern:lower()
+                if pattern_lower == ar_hostname then
+                  should_remove = true
+                  break
+                elseif pattern_lower:sub(1, 1) == '.' then
+                  if ar_hostname:sub(-#pattern_lower) == pattern_lower then
+                    should_remove = true
+                    break
+                  end
+                end
+              end
+            elseif type(local_mod.remove_ar_from) == 'string' then
+              local pattern_lower = local_mod.remove_ar_from:lower()
+              if pattern_lower == ar_hostname then
+                should_remove = true
+              elseif pattern_lower:sub(1, 1) == '.' then
+                if ar_hostname:sub(-#pattern_lower) == pattern_lower then
+                  should_remove = true
+                end
+              end
+            end
+
+            if should_remove then
+              lua_util.debugm(N, task, 'removing AR header from %s (idx %d)', ar_hostname, idx)
+              table.insert(indices_to_remove, idx)
+            end
+          end
+        end
 
-    if settings.routines['authentication-results'].remove then
-      remove[settings.routines['authentication-results'].header] = settings.routines['authentication-results'].remove
+        if #indices_to_remove > 0 then
+          remove[hdr_name] = indices_to_remove
+        end
+      end
+    elseif local_mod.remove then
+      remove[local_mod.header] = local_mod.remove
     end
 
     local res = ar.gen_auth_results(task,
-      lua_util.override_defaults(ar.default_settings,
-        settings.routines['authentication-results']))
+      lua_util.override_defaults(ar.default_settings, local_mod))
 
     if res then
       add_header('authentication-results', res, ';', 1)
@@ -766,6 +823,20 @@ if opts.extended_headers_rcpt then
     'set', 'Extended headers recipients')
 end
 
+if settings.routines['authentication-results'] and
+    settings.routines['authentication-results'].remove_ar_from then
+  local ar_from = settings.routines['authentication-results'].remove_ar_from
+  if type(ar_from) == 'table' and (ar_from.url or ar_from.file or ar_from.name) then
+    settings.routines['authentication-results'].remove_ar_from =
+      lua_maps.rspamd_map_add_from_ucl(ar_from, 'set', 'AR headers removal hostnames')
+  elseif type(ar_from) == 'string' then
+    if ar_from:match('^[/~]') or ar_from:match('^https?://') or ar_from:match('^file://') then
+      settings.routines['authentication-results'].remove_ar_from =
+        lua_maps.rspamd_map_add_from_ucl(ar_from, 'set', 'AR headers removal hostnames')
+    end
+  end
+end
+
 rspamd_config:register_symbol({
   name = 'MILTER_HEADERS',
   type = 'idempotent',