From f32992ca36ee38fe03b85a77a79234022d35db27 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Tue, 22 Sep 2026 20:22:29 -0700 Subject: [PATCH 1/2] Fix the antispam test: pin the rules it scores against, stop live Pyzor It failed everywhere but upstream's machines, for two reasons: - The spam rules, which carry every score, came from a path on an upstream developer's own disk. Without SPAM_RULES_URL none loaded, every score was 0.00 and the combined case came out ham instead of spam at 13.70. The published rules of spam-filter v3.0.2 are now pinned beside the test cases (Apache-2.0 or MIT, taken as MIT; in THIRD-PARTY.md). SPAM_RULES_URL still overrides. - The first combined case expects a Pyzor hit, and its digest (that of an empty body) wasn't among the three the test mode answers, so it went to a public Pyzor server: it failed offline and would drift with that server's counts. Test mode now answers every digest from a fixed table, with the empty body's added, and never reaches the network. The test passes online and offline, alone and with the rest of the SMTP tests. queue_retry, unrelated, still fails when it runs after the others in one process, though it passes alone every time. --- THIRD-PARTY.md | 1 + crates/spam-filter/src/modules/pyzor.rs | 61 ++++++++++-------- .../smtp/antispam/spam-filter-rules.json.gz | Bin 0 -> 21488 bytes tests/src/smtp/inbound/antispam.rs | 13 +++- 4 files changed, 46 insertions(+), 29 deletions(-) create mode 100644 tests/resources/smtp/antispam/spam-filter-rules.json.gz diff --git a/THIRD-PARTY.md b/THIRD-PARTY.md index efc418c..499061a 100644 --- a/THIRD-PARTY.md +++ b/THIRD-PARTY.md @@ -24,6 +24,7 @@ carry their own license files. | `crates/common/src/network/acme/directory.rs`, `crates/common/src/network/acme/jose.rs`, `crates/common/src/network/acme/order.rs` | [rustls-acme](https://github.com/FlorianUekermann/rustls-acme) (MIT or Apache-2.0) | Copyright (c) Florian Uekermann | | `crates/types/src/id.rs` | [crockford](https://github.com/archer884/crockford) (MIT or Apache-2.0) | Copyright (c) 2017 J/A | | `crates/nlp/src/tokenizers/types.rs` | test cases from [linkify](https://github.com/robinst/linkify) (MIT or Apache-2.0) | Copyright (c) 2017 Robin Stocker | +| `tests/resources/smtp/antispam/spam-filter-rules.json.gz` | the published rules of [spam-filter](https://github.com/stalwartlabs/spam-filter) v3.0.2, unmodified, for the spam filter's tests (MIT or Apache-2.0) | Copyright (C) 2024, Stalwart Labs LLC | Each notice above applies with this permission notice: diff --git a/crates/spam-filter/src/modules/pyzor.rs b/crates/spam-filter/src/modules/pyzor.rs index d24f0cd..bffdc6b 100644 --- a/crates/spam-filter/src/modules/pyzor.rs +++ b/crates/spam-filter/src/modules/pyzor.rs @@ -2,6 +2,8 @@ * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + * + * Modified by Coffey Labs in 2026 for INBUXA. */ use common::config::mailstore::spamfilter::PyzorConfig; @@ -43,35 +45,14 @@ pub(crate) async fn pyzor_check( // Hash message let request = message.pyzor_check_message(); + // Send message to address. inbuxa: in tests, a fixed table answers + // instead of a public server (test_response). + #[cfg(not(feature = "test_mode"))] + let response = pyzor_send_message(config.address, config.timeout, &request).await; #[cfg(feature = "test_mode")] - { - if request.contains("b5b476f0b5ba6e1c038361d3ded5818dd39c90a2") { - return Ok(PyzorResponse { - code: 200, - count: 1000, - wl_count: 0, - } - .into()); - } else if request.contains("d67d4b8bfc3860449e3418bb6017e2612f3e2a99") { - return Ok(PyzorResponse { - code: 200, - count: 60, - wl_count: 10, - } - .into()); - } else if request.contains("81763547012b75e57a20d18ce0b93014208cdfdb") { - return Ok(PyzorResponse { - code: 200, - count: 50, - wl_count: 20, - } - .into()); - } - } + let response = std::io::Result::Ok(test_response(&request)); - // Send message to address - pyzor_send_message(config.address, config.timeout, &request) - .await + response .map(Into::into) .map_err(|err| { trc::SpamEvent::PyzorError @@ -82,6 +63,32 @@ pub(crate) async fn pyzor_check( }) } +/// inbuxa: the answers tests get, by digest, instead of a public server's, +/// whose counts change and which a test may not be able to reach. Upstream +/// answered the first three here and sent every other digest to the network. +#[cfg(feature = "test_mode")] +fn test_response(request: &str) -> PyzorResponse { + let (count, wl_count) = if request.contains("b5b476f0b5ba6e1c038361d3ded5818dd39c90a2") + // The digest of an empty body, as an HTML-only message with no text + // to hash produces; public servers report it widely. + || request.contains("da39a3ee5e6b4b0d3255bfef95601890afd80709") + { + (1000, 0) + } else if request.contains("d67d4b8bfc3860449e3418bb6017e2612f3e2a99") { + (60, 10) + } else if request.contains("81763547012b75e57a20d18ce0b93014208cdfdb") { + (50, 20) + } else { + (0, 0) + }; + PyzorResponse { + code: 200, + count, + wl_count, + } +} + +#[cfg_attr(feature = "test_mode", allow(dead_code))] async fn pyzor_send_message( addr: SocketAddr, timeout: Duration, diff --git a/tests/resources/smtp/antispam/spam-filter-rules.json.gz b/tests/resources/smtp/antispam/spam-filter-rules.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6deb9d2be3bf21945e22c69f7dd16f62a6002708 GIT binary patch literal 21488 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 Date: Tue, 22 Sep 2026 20:55:42 -0700 Subject: [PATCH 2/2] antispam test: run it serially, like the tests sharing its port It listens on HTTP 19048, as the dkim2 DSN and report tests do. Those are marked serial, this wasn't, so when the SMTP tests ran together (as upstream's CI runs them) it could start beside them and requests reached whichever server had the port: missing JMAP creates here, and 'You must authenticate first' in dkim2_dsn_is_signed. --- tests/src/smtp/inbound/antispam.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/src/smtp/inbound/antispam.rs b/tests/src/smtp/inbound/antispam.rs index d2cb876..a960156 100644 --- a/tests/src/smtp/inbound/antispam.rs +++ b/tests/src/smtp/inbound/antispam.rs @@ -68,6 +68,10 @@ use std::{ time::{Duration, Instant}, }; +// inbuxa: its HTTP listener (19048) is shared with the dkim2 and report +// tests, which are serial; without this it ran beside them and each got the +// other's server. +#[serial_test::serial] #[tokio::test(flavor = "multi_thread")] async fn antispam() { let mut test = TestServerBuilder::new("smtp_antispam_test")