]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Rework] Make hard deps opt-in, weak deps default
authorVsevolod Stakhov <vsevolod@rspamd.com>
Tue, 31 Mar 2026 15:57:12 +0000 (16:57 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Tue, 31 Mar 2026 15:57:12 +0000 (16:57 +0100)
Invert the dependency semantics for backward compatibility:
- Default (no flag) = weak/soft dep, no cascade-disable (old behavior)
- hard=true = cascade-disable when dep is disabled by settings

This preserves backward compat: all existing register_dependency
calls without flags behave exactly as before. Hard cascade-disable
is an explicit opt-in feature.

Lua API: register_dependency('A', 'B', true) now means hard dep.
Remove redundant weak annotations from plugins (default is weak).

15 files changed:
rules/bounce.lua
rules/headers_checks.lua
src/libserver/rspamd_symcache.h
src/libserver/symcache/symcache_c.cxx
src/libserver/symcache/symcache_impl.cxx
src/libserver/symcache/symcache_internal.hxx
src/libserver/symcache/symcache_item.hxx
src/libserver/symcache/symcache_runtime.cxx
src/lua/lua_config.c
src/plugins/lua/arc.lua
src/plugins/lua/dkim_signing.lua
src/plugins/lua/dmarc.lua
src/plugins/lua/mid.lua
src/plugins/lua/rbl.lua
test/functional/lua/settings_merge.lua

index 7694c6980991f0a4e45a88bf86c5e937aeb49ce4..404a5a3de092ec59c52a1438b246c4cf378ac92d 100644 (file)
@@ -115,4 +115,4 @@ rspamd_config.BOUNCE = {
 }
 
 -- Weak: BOUNCE can fire on sender pattern alone; subject match is an independent path
-rspamd_config:register_dependency('BOUNCE', 'SUBJ_BOUNCE_WORDS', true)
+rspamd_config:register_dependency('BOUNCE', 'SUBJ_BOUNCE_WORDS')
index 1d76831afce349ce615b9175ff5be69b3cc749ec..cd9904f84466da3d0b9b48412569f0a3aec91e7b 100644 (file)
@@ -393,7 +393,7 @@ rspamd_config:register_symbol {
 }
 
 -- Weak: only one sub-check (REPLYTO_EMAIL_HAS_TITLE) needs FROM_NAME_HAS_TITLE
-rspamd_config:register_dependency('CHECK_REPLYTO', 'CHECK_FROM', true)
+rspamd_config:register_dependency('CHECK_REPLYTO', 'CHECK_FROM')
 
 local check_mime_id = rspamd_config:register_symbol {
   name = 'CHECK_MIME',
index 32c855ded00e677704c8419910ffbb4b37084ba0..2d63016d9acd98f185d766e8b5bc8faee315e46d 100644 (file)
@@ -241,11 +241,11 @@ void rspamd_symcache_inc_frequency(struct rspamd_symcache *_cache,
  * @param cache
  * @param from
  * @param to
- * @param weak if TRUE, disabling the dependency does not cascade-disable the dependent
+ * @param hard if TRUE, disabling the dependency cascade-disables the dependent
  */
 void rspamd_symcache_add_delayed_dependency(struct rspamd_symcache *cache,
                                                                                        const char *from, const char *to,
-                                                                                       gboolean weak);
+                                                                                       gboolean hard);
 
 /**
  * Get abstract callback data for a symbol (or its parent symbol)
index 27dcc652508f44ff546370fbf87422778e4b1edf..a185305f9b59ef151a2c6ce064ada5baa28cce40 100644 (file)
@@ -226,10 +226,10 @@ void rspamd_symcache_inc_frequency(struct rspamd_symcache *cache, struct rspamd_
 
 void rspamd_symcache_add_delayed_dependency(struct rspamd_symcache *cache,
                                                                                        const char *from, const char *to,
-                                                                                       gboolean weak)
+                                                                                       gboolean hard)
 {
        auto *real_cache = C_API_SYMCACHE(cache);
-       real_cache->add_delayed_dependency(from, to, weak);
+       real_cache->add_delayed_dependency(from, to, hard);
 }
 
 const char *
index e9401b8042bc4b3303a45aec9586d196c76faa09..457182570d87de455b58592f1e6f1ecf4a096feb 100644 (file)
@@ -135,13 +135,13 @@ auto symcache::init() -> bool
 
                        if (!disabled_ids.contains(real_source->id)) {
                                msg_debug_cache("delayed %sbetween %s(%d:%d) -> %s",
-                                                               delayed_dep.weak ? "weak " : "",
+                                                               delayed_dep.hard ? "weak " : "",
                                                                delayed_dep.from.data(),
                                                                real_source->id, virt_source->id,
                                                                delayed_dep.to.data());
                                add_dependency(real_source->id, delayed_dep.to, real_destination->id,
                                                           virt_source != real_source ? virt_source->id : -1,
-                                                          delayed_dep.weak);
+                                                          delayed_dep.hard);
                        }
                        else {
                                msg_debug_cache("no delayed between %s(%d:%d) -> %s; %s is disabled",
@@ -541,7 +541,7 @@ auto symcache::get_item_by_name_mut(std::string_view name, bool resolve_parent)
        return it->second;
 }
 
-auto symcache::add_dependency(int id_from, std::string_view to, int id_to, int virtual_id_from, bool weak) -> void
+auto symcache::add_dependency(int id_from, std::string_view to, int id_to, int virtual_id_from, bool hard) -> void
 {
        g_assert(id_from >= 0 && id_from < (int) items_by_id.size());
        g_assert(id_to >= 0 && id_to < (int) items_by_id.size());
@@ -552,11 +552,11 @@ auto symcache::add_dependency(int id_from, std::string_view to, int id_to, int v
 
        if (!source->deps.contains(id_to)) {
                msg_debug_cache("add %sdependency %s(%d) -> %s(%d)",
-                                               weak ? "weak " : "",
+                                               hard ? "hard " : "",
                                                source->symbol.c_str(), source->id, to.data(), dest->id);
                source->deps.emplace(id_to, cache_dependency{dest.get(),
                                                                                                         std::string(to),
-                                                                                                        -1, weak});
+                                                                                                        -1, hard});
        }
        else {
                msg_debug_cache("duplicate dependency %s -> %s",
@@ -573,11 +573,11 @@ auto symcache::add_dependency(int id_from, std::string_view to, int id_to, int v
 
                if (!vsource->deps.contains(id_to)) {
                        msg_debug_cache("add virtual %sdependency %s -> %s",
-                                                       weak ? "weak " : "",
+                                                       hard ? "hard " : "",
                                                        vsource->symbol.c_str(), to.data());
                        vsource->deps.emplace(id_to, cache_dependency{dest.get(),
                                                                                                                  std::string(to),
-                                                                                                                 virtual_id_from, weak});
+                                                                                                                 virtual_id_from, hard});
                }
                else {
                        msg_debug_cache("duplicate virtual dependency %s -> %s",
index c40790fdc87a4b2ee1d3fdf583531fb810f25c44..48acd239123e18b5c942f5e631ca6422dcae2558 100644 (file)
@@ -135,10 +135,10 @@ using order_generation_ptr = std::shared_ptr<order_generation>;
 struct delayed_cache_dependency {
        std::string from;
        std::string to;
-       bool weak;
+       bool hard;
 
-       delayed_cache_dependency(std::string_view _from, std::string_view _to, bool _weak = false)
-               : from(_from), to(_to), weak(_weak)
+       delayed_cache_dependency(std::string_view _from, std::string_view _to, bool _hard = false)
+               : from(_from), to(_to), hard(_hard)
        {
        }
 };
@@ -373,20 +373,20 @@ public:
         * @param virtual_id_from
         * @return
         */
-       auto add_dependency(int id_from, std::string_view to, int id_to, int virtual_id_from, bool weak = false) -> void;
+       auto add_dependency(int id_from, std::string_view to, int id_to, int virtual_id_from, bool hard = false) -> void;
 
        /**
         * Add a delayed dependency between symbols that will be resolved on the init stage
         * @param from
         * @param to
         */
-       auto add_delayed_dependency(std::string_view from, std::string_view to, bool weak = false) -> void
+       auto add_delayed_dependency(std::string_view from, std::string_view to, bool hard = false) -> void
        {
                if (!delayed_deps) {
                        delayed_deps = std::make_unique<std::vector<delayed_cache_dependency>>();
                }
 
-               delayed_deps->emplace_back(from, to, weak);
+               delayed_deps->emplace_back(from, to, hard);
        }
 
        /**
index dd6dd2040ea572898f8204217dca066becf629d9..898f9af79c954a6fbc7d34c8e1ccfedfcc98877f 100644 (file)
@@ -182,11 +182,11 @@ struct cache_dependency {
        cache_item *item;      /* Real dependency */
        std::string sym;       /* Symbolic dep name */
        int virtual_source_id; /* Virtual source */
-       bool weak;             /* Weak dep: don't cascade-disable when dep is disabled */
+       bool hard;             /* Hard dep: cascade-disable dependent when dep is disabled by settings */
 public:
        /* Default piecewise constructor */
-       explicit cache_dependency(cache_item *_item, std::string _sym, int _vid, bool _weak = false)
-               : item(_item), sym(std::move(_sym)), virtual_source_id(_vid), weak(_weak)
+       explicit cache_dependency(cache_item *_item, std::string _sym, int _vid, bool _hard = false)
+               : item(_item), sym(std::move(_sym)), virtual_source_id(_vid), hard(_hard)
        {
        }
 };
index 49f8e6eeaa90370f7459a9c3f42d24d71a9a00f9..c608298ea4ebc606079dbaea6f21f860bec3e302 100644 (file)
@@ -658,7 +658,7 @@ auto symcache_runtime::check_item_deps(struct rspamd_task *task, symcache &cache
 
                        if (dep_dyn_item->status == cache_item_status::disabled) {
                                /* Dependency was disabled by settings */
-                               if (!dep.weak) {
+                               if (dep.hard) {
                                        /* Hard dependency disabled: cascade-disable this item */
                                        dyn_item->status = cache_item_status::disabled;
                                        msg_debug_cache_task_lambda("cascade disable %d(%s) because hard dependency "
@@ -667,9 +667,9 @@ auto symcache_runtime::check_item_deps(struct rspamd_task *task, symcache &cache
                                                                                                dest_id, dep.sym.c_str());
                                        return true; /* Item is "done" (disabled) */
                                }
-                               /* Weak dependency disabled: proceed without it */
-                               msg_debug_cache_task_lambda("weak dependency %d(%s) for symbol %d(%s) is "
-                                                                                       "disabled, proceeding",
+                               /* Normal (weak) dependency disabled: proceed without it (backward compat) */
+                               msg_debug_cache_task_lambda("dependency %d(%s) for symbol %d(%s) is "
+                                                                                       "disabled, proceeding (not a hard dep)",
                                                                                        dest_id, dep.sym.c_str(), item->id, item->symbol.c_str());
                                continue;
                        }
@@ -690,7 +690,7 @@ auto symcache_runtime::check_item_deps(struct rspamd_task *task, symcache &cache
                                                }
                                                else if (dep_dyn_item->status == cache_item_status::disabled) {
                                                        /* Dep was cascade-disabled during recursive check */
-                                                       if (!dep.weak) {
+                                                       if (dep.hard) {
                                                                dyn_item->status = cache_item_status::disabled;
                                                                msg_debug_cache_task_lambda("cascade disable %d(%s) because hard dependency "
                                                                                                                        "%d(%s) was cascade-disabled",
index 70666e18c47165f812a1d0860048609a3ccff687..7190dc638d004652c1e51ecbddb984989ba22c05 100644 (file)
@@ -2815,15 +2815,16 @@ lua_config_register_dependency(lua_State *L)
        else {
                child = luaL_checkstring(L, 2);
                parent = luaL_checkstring(L, 3);
-               gboolean weak = FALSE;
+               /* Optional 4th arg: true = hard dep (cascade-disable when dep disabled) */
+               gboolean hard = FALSE;
 
                if (lua_isboolean(L, 4)) {
-                       weak = lua_toboolean(L, 4);
+                       hard = lua_toboolean(L, 4);
                }
 
                if (child != NULL && parent != NULL) {
                        rspamd_symcache_add_delayed_dependency(cfg->cache, child,
-                                                                                                  parent, weak);
+                                                                                                  parent, hard);
                }
        }
 
index d9528dd65aa28e4fc7f06bf6e45cb9b3e73a4c1d..67446e207e42d4f0cc0ef9824d0d237649a9882f 100644 (file)
@@ -567,8 +567,8 @@ rspamd_config:register_symbol({
 })
 
 -- Weak deps: ARC verification works without SPF/DKIM, just less info in AAR header
-rspamd_config:register_dependency('ARC_CHECK', 'SPF_CHECK', true)
-rspamd_config:register_dependency('ARC_CHECK', 'DKIM_CHECK', true)
+rspamd_config:register_dependency('ARC_CHECK', 'SPF_CHECK')
+rspamd_config:register_dependency('ARC_CHECK', 'DKIM_CHECK')
 
 local function arc_sign_seal(task, params, header)
   local arc_seals = task:cache_get('arc-seals')
index d5b9c7f52b0341ff867ca1abab78c52ff21de93a..e9b194e1889a065f0d8d6a2a7966e2a7c79cf4b1 100644 (file)
@@ -180,4 +180,4 @@ end
 
 rspamd_config:register_symbol(sym_reg_tbl)
 -- Add weak dependency on DKIM checks (signing works without it, just loses R_DKIM_REJECT suppression)
-rspamd_config:register_dependency(settings['symbol'], 'DKIM_CHECK', true)
+rspamd_config:register_dependency(settings['symbol'], 'DKIM_CHECK')
index 969bdfc3773f94331bc6a14195ad228e68034569..e2b3dedcc9d1745382f3e61841762e0e1ffd6013 100644 (file)
@@ -745,8 +745,8 @@ rspamd_config:register_symbol({
 })
 
 -- Weak deps: DMARC falls back to DKIM-only or SPF-only alignment if the other is absent
-rspamd_config:register_dependency('DMARC_CHECK', settings.symbols['spf_allow_symbol'], true)
-rspamd_config:register_dependency('DMARC_CHECK', settings.symbols['dkim_allow_symbol'], true)
+rspamd_config:register_dependency('DMARC_CHECK', settings.symbols['spf_allow_symbol'])
+rspamd_config:register_dependency('DMARC_CHECK', settings.symbols['dkim_allow_symbol'])
 
 -- DMARC munging support
 if settings.munging then
@@ -796,8 +796,8 @@ if settings.munging then
 
   rspamd_config:register_dependency('DMARC_MUNGED', 'DMARC_CHECK')
   -- Weak: signing works without munging, just ensures proper header rewrite order
-  rspamd_config:register_dependency('DKIM_SIGNED', 'DMARC_MUNGED', true)
-  rspamd_config:register_dependency('ARC_SIGNED', 'DMARC_MUNGED', true)
+  rspamd_config:register_dependency('DKIM_SIGNED', 'DMARC_MUNGED')
+  rspamd_config:register_dependency('ARC_SIGNED', 'DMARC_MUNGED')
 
   rspamd_logger.infox(rspamd_config, 'enabled DMARC munging')
 end
index d3e4c01bbe1cbd91977051c23ebe807d93292d5e..44dfe0ad42e56aac2e415635e7d1cc5290527ccf 100644 (file)
@@ -116,7 +116,7 @@ if opts then
             settings['symbol_missing_mid']))
 
     -- Weak: reads R_DKIM_ALLOW options but works without them
-    rspamd_config:register_dependency('KNOWN_MID_CALLBACK', 'DKIM_CHECK', true)
+    rspamd_config:register_dependency('KNOWN_MID_CALLBACK', 'DKIM_CHECK')
   else
     rspamd_logger.infox(rspamd_config, 'source is not a valid map definition, disabling module')
     lua_util.disable_module(N, "config")
index b8e14734bc9f628a0f162eb445e564fac071092e..ce5b0391f5bf9618681d1ba52bca9ee7e3498d0b 100644 (file)
@@ -1213,7 +1213,7 @@ local function add_rbl(key, rbl, global_opts)
 
     if rbl.dkim then
       -- Weak: RBL has other query sources; DKIM-domain queries just won't happen
-      rspamd_config:register_dependency(check_sym, 'DKIM_CHECK', true)
+      rspamd_config:register_dependency(check_sym, 'DKIM_CHECK')
     end
 
     if rbl.require_symbols then
index bb3d2de71f8b698eca52cb7795dea68dfadbbbed..f103ded7320ad19d20acfcfc89378d8c6827d4d0 100644 (file)
@@ -19,9 +19,9 @@ rspamd_config:register_symbol({
     return true, 'hard_dep'
   end
 })
-rspamd_config:register_dependency('MERGE_HARD_DEP', 'MERGE_TEST_BASIC')
+rspamd_config:register_dependency('MERGE_HARD_DEP', 'MERGE_TEST_BASIC', true)
 
--- Symbol with a WEAK dependency on MERGE_TEST_BASIC
+-- Symbol with a WEAK (default) dependency on MERGE_TEST_BASIC
 rspamd_config:register_symbol({
   name = 'MERGE_WEAK_DEP',
   score = 1.0,
@@ -30,7 +30,7 @@ rspamd_config:register_symbol({
     return true, 'weak_dep'
   end
 })
-rspamd_config:register_dependency('MERGE_WEAK_DEP', 'MERGE_TEST_BASIC', true)
+rspamd_config:register_dependency('MERGE_WEAK_DEP', 'MERGE_TEST_BASIC')
 
 -- Symbol in a separate group for group enable/disable tests
 rspamd_config:register_symbol({