Spam filter: STWT_SURBL_DOMAIN ignores combined (bitmasked) SURBL responses

Issue Description

The official spam filter rules (spam-filter v3.0.1, imported into Stalwart 0.16.22) define the DNSBL server STWT_SURBL_DOMAIN with tag expressions that compare the last octet of the SURBL MULTI answer for equality with 128, 64, 32, 16, 8, 4 and 1.

SURBL documents MULTI as a bitmasked list (page “Lists” on the SURBL site): an entry on several lists returns the sum of their values in the last octet; their own example is 127.0.0.80 = MW (16) + ABUSE (64). None of the equality branches matches such a sum, so a domain listed in two or more SURBL categories receives no SURBL tag and no score, although the lookup succeeded. The stronger a listing is, the more likely the rule is to ignore it.

The same comparison shape is used in STWT_SURBL_HASHBL_DOMAIN (same bit values per SURBL) and STWT_URIBL_DOMAIN; for URIBL the provider states that combined values should not occur outside its test points, so the practical impact is on SURBL MULTI.

Expected Behavior

A response whose last octet combines several SURBL lists is tagged. Keeping one tag per lookup and the existing branch order, replacing the equalities with >= selects the highest-priority category present:

octets[3] >= 128 → CRACKED_SURBL
octets[3] >= 64 → ABUSE_SURBL
octets[3] >= 32 → CT_SURBL
octets[3] >= 16 → MW_SURBL_MULTI
octets[3] >= 8 → PH_SURBL_MULTI
octets[3] >= 4 → DM_SURBL
octets[3] == 1 → SURBL_BLOCKED

With this, 127.0.0.80 yields ABUSE_SURBL and 127.0.0.254 yields CRACKED_SURBL; single-list answers keep their current tag.

Actual Behavior

A combined answer (e.g. 127.0.0.80, or 127.0.0.254 from SURBL’s permanent test point) matches no branch, the expression falls through to else = false, and the message gets no SURBL tag.

Reproduction Steps

  1. Import the official spam filter rules v3.0.1 (default configuration).
  2. Confirm the resolver returns a combined value for SURBL’s permanent test point (command and output in the log section below): 127.0.0.254.
  3. Deliver a message whose body contains a link to that test-point domain and read its X-Spam-Result header.
  4. No *_SURBL tag is present, although the DNSBL lookup succeeded with 127.0.0.254.

Relevant Log Output

$ dig +short surbl-org-permanent-test-point.com.multi.surbl.org
127.0.0.254

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

Rule source: file rules/dnsbl.toml in the stalwartlabs/spam-filter repository, tag v3.0.1, object STWT_SURBL_DOMAIN; the rules on main are identical. Stalwart evaluates the tag expression on the first A record of the answer and returns one tag (crates/spam-filter/src/modules/dnsbl.rs in the stalwart repository), so the fix belongs in the rules, not in the server.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

This confirms the user’s technical claim is accurate (the tag evaluation logic lives in dnsbl.rs and returns based on rule evaluation from the imported rules file, not hardcoded server logic). This is a well-researched bug report about the community-maintained spam-filter rules repository, not the stalwart server itself.

Your analysis matches what the code does. In check_dnsbl / is_dnsbl (crates/spam-filter/src/modules/dnsbl.rs), the server resolves the DNSBL zone, takes the returned IP, and evaluates the tags expression from the DNSBL server definition against it via eval_if, returning at most a single Option<String> tag per lookup. It performs no bit-testing itself; it just runs whatever comparison logic is written in the rule’s tags expression and returns the first match (or None if nothing matches). So a combined/bitmasked SURBL response like 127.0.0.80 (MW + ABUSE) will indeed fall through to else and yield no tag if the rule only checks equality against individual bit values.

This confirms it’s a rules-definition issue rather than a server-side bug: the fix belongs in the STWT_SURBL_DOMAIN (and STWT_SURBL_HASHBL_DOMAIN) tag expressions in the spam-filter rules package, changing the equality checks to >= threshold checks as you proposed, since the server will faithfully evaluate whatever comparison the rule expresses.

A human moderator or the rules maintainer will need to review and update the shipped rule set; this isn’t something documented as configurable behavior to work around on the server side.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.23 (yet to be released). The fix requires updating the SPAM filter rules too.