]> git.ipfire.org Git - thirdparty/rspamd.git/log
thirdparty/rspamd.git
4 weeks agoMerge branch 'master' into vstakhov-stat-migrate
Vsevolod Stakhov [Thu, 12 Mar 2026 12:22:31 +0000 (12:22 +0000)] 
Merge branch 'master' into vstakhov-stat-migrate

4 weeks ago[Fix] Add reconnection with retry logic for statistics restore
Vsevolod Stakhov [Thu, 12 Mar 2026 12:20:31 +0000 (12:20 +0000)] 
[Fix] Add reconnection with retry logic for statistics restore

When restoring large datasets (24M+ lines), Redis connections can get
terminated mid-restore. Add retry logic with automatic reconnection
that resumes from the failed pipeline chunk, avoiding HINCRBYFLOAT
double-counting by skipping past ambiguously-applied chunks.

4 weeks agoMerge pull request #5924 from rspamd/vstakhov-neural-sharing
Vsevolod Stakhov [Wed, 11 Mar 2026 13:21:54 +0000 (13:21 +0000)] 
Merge pull request #5924 from rspamd/vstakhov-neural-sharing

[Feature] Add external pretrained neural model support

4 weeks agoMerge branch 'master' into vstakhov-neural-sharing 5924/head
Vsevolod Stakhov [Tue, 10 Mar 2026 17:37:28 +0000 (17:37 +0000)] 
Merge branch 'master' into vstakhov-neural-sharing

4 weeks ago[Fix] Fix external neural model merge defects
Vsevolod Stakhov [Tue, 10 Mar 2026 17:35:51 +0000 (17:35 +0000)] 
[Fix] Fix external neural model merge defects

- merge_weights returns boolean, not ANN object: use ext_ann directly
- Add missing digest/symbols/distance fields for external-only set.ann
- Fix inverted alpha in merge call (alpha meant external weight, not local)
- Add missing newline at EOF in lua_kann.c

4 weeks ago[Fix] Fix fuzzy TCP frame buffer overflow for encrypted v2 replies
Vsevolod Stakhov [Tue, 10 Mar 2026 12:57:09 +0000 (12:57 +0000)] 
[Fix] Fix fuzzy TCP frame buffer overflow for encrypted v2 replies

The rspamd_fuzzy_tcp_frame payload was sized for v1 encrypted replies
(136 bytes) but v2 encrypted replies are 184 bytes, causing a buffer
overflow when sending multi-flag responses over TCP with encryption.
Use a union to accommodate both v1 and v2 reply sizes.

Add multi-flag delete tests for all TCP transport modes.

4 weeks ago[Fix] Raise fasttext dictionary token limit
Vsevolod Stakhov [Tue, 10 Mar 2026 11:41:49 +0000 (11:41 +0000)] 
[Fix] Raise fasttext dictionary token limit

Allow valid fasttext models with long dictionary entries to load without tripping the shim safety cap. Fixes #5919.

4 weeks ago[Minor] Fix default date description in rspamadm dmarc_report help 5931/head
Rob4226 [Tue, 10 Mar 2026 09:04:36 +0000 (05:04 -0400)] 
[Minor] Fix default date description in rspamadm dmarc_report help

Change the help text for the `date` argument of `rspamadm dmarc_report`
from "today" to "yesterday". When the command is run without specifying
a date, it actually processes reports for yesterday, so this update
makes the help message match the command's behavior.

4 weeks agoMerge pull request #5923 from Jesssullivan/dev/dkim-sign-headers-segfault
Vsevolod Stakhov [Mon, 9 Mar 2026 12:57:09 +0000 (12:57 +0000)] 
Merge pull request #5923 from Jesssullivan/dev/dkim-sign-headers-segfault

[Feature] Prevent SIGSEGV when sign_headers is not a string

4 weeks ago[Fix] Accept fuzzy TCP v2 reply frames
Vsevolod Stakhov [Mon, 9 Mar 2026 12:50:18 +0000 (12:50 +0000)] 
[Fix] Accept fuzzy TCP v2 reply frames

Allow TCP fuzzy replies to use v2 frame sizes so valid 144-byte responses are not rejected as invalid frame lengths. Fixes #5930.

4 weeks ago[Fix] Prevent SIGSEGV when sign_headers is not a string 5923/head
Jess Sullivan [Thu, 5 Mar 2026 17:42:18 +0000 (12:42 -0500)] 
[Fix] Prevent SIGSEGV when sign_headers is not a string

Add NULL guard in rspamd_create_dkim_sign_context() before strlen(headers)
to return a proper GError instead of crashing when headers is NULL.

Add type validation at config load time for sign_headers in dkim_signing,
dkim, and arc modules — rejects non-string types with a clear error message.

Fixes: SIGSEGV in dkim.c when sign_headers is configured as a UCL array
(ucl_object_tostring returns NULL for arrays, which hits strlen).

-jess

4 weeks agoMerge pull request #5929 from moisseev/test
Vsevolod Stakhov [Sun, 8 Mar 2026 22:11:22 +0000 (22:11 +0000)] 
Merge pull request #5929 from moisseev/test

[Test] Skip scheduled integration tests in forks

4 weeks agoMerge pull request #5927 from moisseev/replies
Vsevolod Stakhov [Sun, 8 Mar 2026 22:11:04 +0000 (22:11 +0000)] 
Merge pull request #5927 from moisseev/replies

[Fix] Skip recipient check when no hash found in Redis

4 weeks agoMerge pull request #5928 from moisseev/droid-review
Vsevolod Stakhov [Sun, 8 Mar 2026 22:10:49 +0000 (22:10 +0000)] 
Merge pull request #5928 from moisseev/droid-review

[Test] Restrict droid review to upstream repository

4 weeks ago[Test] Skip scheduled integration tests in forks 5929/head
Alexander Moisseev [Sun, 8 Mar 2026 12:39:26 +0000 (15:39 +0300)] 
[Test] Skip scheduled integration tests in forks

Limit scheduled integration-test runs to rspamd/rspamd while keeping
manual start available in forks. This avoids unnecessary fork cron
runs and reduces noisy CI failures unrelated to upstream.

4 weeks ago[Test] Restrict droid review to upstream repository 5928/head
Alexander Moisseev [Sun, 8 Mar 2026 10:54:03 +0000 (13:54 +0300)] 
[Test] Restrict droid review to upstream repository

4 weeks ago[Fix] Skip recipient check when no hash found in Redis 5927/head
Alexander Moisseev [Sat, 7 Mar 2026 11:46:45 +0000 (14:46 +0300)] 
[Fix] Skip recipient check when no hash found in Redis

When a key is not found in Redis, lua_redis returns a redis.null
userdata (not nil), which is truthy and caused check_recipient()
to be called unconditionally, logging a misleading "no recipients
are matching hash" message despite no hash being stored.

4 weeks agoMerge branch 'master' into vstakhov-stat-migrate
Vsevolod Stakhov [Sat, 7 Mar 2026 09:51:57 +0000 (09:51 +0000)] 
Merge branch 'master' into vstakhov-stat-migrate

4 weeks agoMerge branch 'master' into vstakhov-neural-sharing
Vsevolod Stakhov [Sat, 7 Mar 2026 09:51:08 +0000 (09:51 +0000)] 
Merge branch 'master' into vstakhov-neural-sharing

4 weeks ago[Minor] Fix model name
Vsevolod Stakhov [Sat, 7 Mar 2026 09:50:53 +0000 (09:50 +0000)] 
[Minor] Fix model name

4 weeks agoMerge branch 'master' into vstakhov-neural-sharing
Vsevolod Stakhov [Sat, 7 Mar 2026 09:44:14 +0000 (09:44 +0000)] 
Merge branch 'master' into vstakhov-neural-sharing

4 weeks ago[Fix] Upgrade legacy cached maps to aligned format
Vsevolod Stakhov [Fri, 6 Mar 2026 18:48:53 +0000 (18:48 +0000)] 
[Fix] Upgrade legacy cached maps to aligned format

4 weeks ago[Fix] Revert invalid map cache seek workaround
Vsevolod Stakhov [Fri, 6 Mar 2026 18:46:37 +0000 (18:46 +0000)] 
[Fix] Revert invalid map cache seek workaround

4 weeks ago[Fix] Rewrite map cache headers from file start
Vsevolod Stakhov [Fri, 6 Mar 2026 18:03:01 +0000 (18:03 +0000)] 
[Fix] Rewrite map cache headers from file start

4 weeks ago[Minor] droid-code-review: switch model to gpt5.4, rewrite prompt for precision ...
Copilot [Fri, 6 Mar 2026 17:08:20 +0000 (17:08 +0000)] 
[Minor] droid-code-review: switch model to gpt5.4, rewrite prompt for precision (#5925)

4 weeks ago[Fix] Handle stale neural export profiles
Vsevolod Stakhov [Fri, 6 Mar 2026 17:06:49 +0000 (17:06 +0000)] 
[Fix] Handle stale neural export profiles

4 weeks agoMerge pull request #5922 from moisseev/replies
Vsevolod Stakhov [Fri, 6 Mar 2026 11:48:06 +0000 (11:48 +0000)] 
Merge pull request #5922 from moisseev/replies

[Minor] Skip empty In-Reply-To header in replies check

4 weeks ago[Feature] Add external pretrained neural model support
Vsevolod Stakhov [Fri, 6 Mar 2026 10:49:43 +0000 (10:49 +0000)] 
[Feature] Add external pretrained neural model support

This commit adds the ability to load pretrained neural network models
from external sources (HTTP/HTTPS) and merge them with locally trained
weights. Users can receive a pretrained model and fine-tune it with
their own data.

Model format (msgpack with magic "RNM1"):
- magic: format identifier
- version: format version (currently 1)
- model_version: model training version
- providers_digest: must match local providers config
- ann_data: serialized KANN (zstd compressed)
- pca_data: optional PCA matrix
- norm_stats, roc_thresholds: optional metadata

Key changes:
- lualib/lua_neural_external.lua: new module for external model handling
  - Model parsing, KANN loading, weight merging via interpolation
  - Map-based loading with signature verification support
  - Base model storage in Redis for future re-merge

- contrib/kann/kann.c: add kann_merge_weights() for weight interpolation
  - w_new = alpha * w_external + (1-alpha) * w_local
  - kann_is_compatible() for architecture compatibility check

- src/lua/lua_kann.c: Lua bindings for merge_weights and is_compatible

- Neural plugin integration:
  - Register external model as callback map at config time
  - Apply loaded model to all settings elements
  - Automatic update checking via map infrastructure

Configuration example:
  neural {
    rules {
      spam_filter = {
        external_model = {
          url = "https://your-provider.com/models/<digest>";
          sign_key = "your_key";
          merge_alpha = 0.6;  # 60% external, 40% local
        };
      };
    };
  }

4 weeks ago[Fix] Preserve content flags for injected query URLs
Vsevolod Stakhov [Fri, 6 Mar 2026 10:00:32 +0000 (10:00 +0000)] 
[Fix] Preserve content flags for injected query URLs

Propagate the parent URL flags when task:inject_url() extracts nested query URLs. This keeps the content flag on URLs injected from computed parts such as PDF text, so follow-up query URLs are classified the same way as the outer injected URL.

4 weeks ago[Fix] Preserve duplicate URLs across MIME parts
Vsevolod Stakhov [Fri, 6 Mar 2026 08:44:21 +0000 (08:44 +0000)] 
[Fix] Preserve duplicate URLs across MIME parts

Do not suppress URLs from mime_part:get_urls() when the same URL was already seen in another MIME part. This restores per-part URL visibility for multipart/alternative messages and keeps text/plain URLs available even when text/html contains the same links.

5 weeks ago[Fix] Speed up shard migration and harden restore batching
Vsevolod Stakhov [Thu, 5 Mar 2026 22:38:26 +0000 (22:38 +0000)] 
[Fix] Speed up shard migration and harden restore batching

5 weeks agoMerge pull request #5921 from rspamd/vstakhov-follow-master-mirror
Vsevolod Stakhov [Thu, 5 Mar 2026 21:03:52 +0000 (21:03 +0000)] 
Merge pull request #5921 from rspamd/vstakhov-follow-master-mirror

[Feature] Add follow_master option for proxy mirrors

5 weeks ago[Minor] Skip empty In-Reply-To header in replies check 5922/head
Alexander Moisseev [Thu, 5 Mar 2026 17:09:40 +0000 (20:09 +0300)] 
[Minor] Skip empty In-Reply-To header in replies check

An empty `In-Reply-To` header value ("") is truthy in Lua,
bypassing the `nil` check. In `replies_check` this caused a
misleading log entry "ignoring reply to  as no recipients
are matching hash ". In `replies_check_cookie` it triggered
an unnecessary `decrypt_cookie` call.

5 weeks ago[Fix] Fix restore stack overflow, migrate memory and speed
Vsevolod Stakhov [Thu, 5 Mar 2026 10:36:12 +0000 (10:36 +0000)] 
[Fix] Fix restore stack overflow, migrate memory and speed

Restore: chunk redis pipeline into batches of 1000 commands per
exec() call to prevent Lua stack overflow on large dump files.

Dump: chunk HGETALL pipeline the same way.

Migrate: replace EVAL scripts with direct pipelined commands.
Collect all misplaced prefixes upfront, then process each with
pipelined HGETALL/HMSET/DEL. Explicit collectgarbage() every
100 prefixes to prevent memory bloat.

Restore: periodic GC and progress logging every 10 batches.

5 weeks ago[Feature] Add follow_master option for proxy mirror connections 5921/head
Vsevolod Stakhov [Wed, 4 Mar 2026 11:10:18 +0000 (11:10 +0000)] 
[Feature] Add follow_master option for proxy mirror connections

When a mirror has a short timeout to avoid delays from misconfigured
mirrors, the mirror connection gets prematurely terminated if the
upstream takes longer than the mirror timeout. The new follow_master
option ties the mirror's lifetime to the master upstream: the mirror
stays alive while the upstream is processing and is terminated once the
upstream completes or permanently errors out.

5 weeks ago[Fix] Move --classifier to top-level parser
Vsevolod Stakhov [Tue, 3 Mar 2026 10:11:07 +0000 (10:11 +0000)] 
[Fix] Move --classifier to top-level parser

The --classifier option must be available for all subcommands
(dump, restore, migrate), not just dump and migrate. Move it
to the top-level parser and use select_classifier in restore
handler as well.

5 weeks agoMerge pull request #5917 from moisseev/playwright
Vsevolod Stakhov [Sun, 1 Mar 2026 17:54:55 +0000 (17:54 +0000)] 
Merge pull request #5917 from moisseev/playwright

[Test] Use Node.js 22 for legacy Playwright 1.45.3 job

5 weeks ago[Test] Use Node.js 22 for legacy Playwright 1.45.3 job 5917/head
Alexander Moisseev [Sun, 1 Mar 2026 14:14:50 +0000 (17:14 +0300)] 
[Test] Use Node.js 22 for legacy Playwright 1.45.3 job

Playwright 1.45.3 predates Node.js 24 support (added in v1.55),
causing an immediate crash on startup. Use Node.js 22 for the
legacy matrix entry.

5 weeks agoMerge pull request #5916 from moisseev/dev-dependencies
Vsevolod Stakhov [Sat, 28 Feb 2026 09:33:18 +0000 (09:33 +0000)] 
Merge pull request #5916 from moisseev/dev-dependencies

[Test] Update dev dependencies

5 weeks ago[Test] Update dev dependencies 5916/head
Alexander Moisseev [Sat, 28 Feb 2026 06:29:07 +0000 (09:29 +0300)] 
[Test] Update dev dependencies

- Update ESLint to v10, stylelint to v17, and related packages
- Bump Node.js to v24 LTS in CI

Closes #5915

5 weeks ago[Fix] Require classifier selection for multi-classifier configs
Vsevolod Stakhov [Fri, 27 Feb 2026 15:33:45 +0000 (15:33 +0000)] 
[Fix] Require classifier selection for multi-classifier configs

When multiple bayes classifiers are configured, dump and migrate now
require --classifier to select which one to operate on. With a single
classifier the flag is optional.

Also fix the SCAN pattern for token keys: use `<prefix>_*` instead of
`<prefix>*_*` to avoid matching tokens from unrelated prefixes.

5 weeks ago[Feature] Add shard migration and multi-class support to statistics_dump
Vsevolod Stakhov [Fri, 27 Feb 2026 14:25:25 +0000 (14:25 +0000)] 
[Feature] Add shard migration and multi-class support to statistics_dump

Add `rspamadm statistics_dump migrate` subcommand for migrating per-user
Bayes data between Redis shards after the Jump Hash to Ketama transition.
The tool scans all shards, identifies misplaced prefixes via
get_upstream_by_hash, and moves them in batches using Redis Lua scripts.

Also fix multi-class Bayes support: dump/restore now handles arbitrary
statfile classes (not just binary spam/ham) by collecting all symbols
from classifier config with proper class label mapping. The dump command
now iterates all shards via all_upstreams() for complete data export.

5 weeks ago[Fix] Force recompilation of stale hyperscan classes instead of skipping
Vsevolod Stakhov [Fri, 27 Feb 2026 11:15:36 +0000 (11:15 +0000)] 
[Fix] Force recompilation of stale hyperscan classes instead of skipping

When a cached hyperscan blob fails validation during load (stale IDs
pointing to wrong re_class), mark the class with needs_recompile flag.
On subsequent exists_async check in hs_helper, ignore the "exists"
result and proceed with recompilation instead of skipping.

5 weeks ago[Fix] Do not enable HS cleanup when disable_hyperscan is set
Vsevolod Stakhov [Fri, 27 Feb 2026 11:01:13 +0000 (11:01 +0000)] 
[Fix] Do not enable HS cleanup when disable_hyperscan is set

When disable_hyperscan is true, workers skip loading hyperscan databases
and never notify main about known cache files. This caused main to delete
all cached .hs.zst files on exit since none were marked as "known".
Also promote worker hyperscan notification to info log level.

5 weeks ago[Fix] Include HS magic in cache key hashes to force recompilation on version bump
Vsevolod Stakhov [Fri, 27 Feb 2026 10:42:20 +0000 (10:42 +0000)] 
[Fix] Include HS magic in cache key hashes to force recompilation on version bump

5 weeks ago[Feature] Per-class deterministic regexp IDs in re_cache
Vsevolod Stakhov [Fri, 27 Feb 2026 10:22:18 +0000 (10:22 +0000)] 
[Feature] Per-class deterministic regexp IDs in re_cache

Group regexp IDs by class instead of assigning them globally.
Each class gets a deterministic base_offset in the global array,
and hyperscan stores intra-class IDs (0..M-1). This prevents
adding/removing a regexp in one class from shifting IDs in all
other classes, eliminating stale hyperscan databases and
unnecessary recompilations.

Key changes:
- Sort classes by class_id, regexps within each class by content hash
- Assign contiguous global IDs per class (base_offset + local_index)
- Use class-local regexp count in per-class hash (not global count)
- Hyperscan compile stores intra-class IDs, callback translates back
- Bump blob magic version to reject old format databases

6 weeks agoMerge pull request #5909 from rspamd/vstakhov-fasttext-maps
Vsevolod Stakhov [Thu, 26 Feb 2026 13:13:02 +0000 (13:13 +0000)] 
Merge pull request #5909 from rspamd/vstakhov-fasttext-maps

[Feature] Fasttext models via maps infrastructure

6 weeks ago[Feature] Wire Lua rspamd_fasttext through maps infrastructure 5909/head
Vsevolod Stakhov [Thu, 26 Feb 2026 12:54:05 +0000 (12:54 +0000)] 
[Feature] Wire Lua rspamd_fasttext through maps infrastructure

Add load_map(cfg, path) to rspamd_fasttext module that loads FastText
models via the maps infrastructure (HTTP URLs + file with shared mmap).
The fasttext_embed neural provider now registers models as maps at
config time via a new init callback, enabling shared memory across
workers and automatic reload on map updates.

6 weeks ago[Fix] Fix SIGSEGV on termination in fasttext map dtor callback
Vsevolod Stakhov [Wed, 25 Feb 2026 15:26:58 +0000 (15:26 +0000)] 
[Fix] Fix SIGSEGV on termination in fasttext map dtor callback

Two bugs in the map callback lifecycle caused a crash during
rspamd_map_remove_all at shutdown:

1. Type mismatch: fin_callback published *target = model pointer
   (fasttext_model*), but the dtor cast it to fasttext_map_data* -
   the standard map pattern requires *target = data->cur_data.

2. Use-after-free: map->user_data pointed into the fasttext_langdet
   object which was destroyed before rspamd_map_remove_all ran.

Fix by allocating the user_data target on cfg->cfg_pool (outlives
the lang detector), following the standard map consumer pattern,
and accessing the model through a get_model() indirection.

6 weeks ago[Fix] Use 16K map cache header for mmap alignment on ARM64
Vsevolod Stakhov [Wed, 25 Feb 2026 14:59:53 +0000 (14:59 +0000)] 
[Fix] Use 16K map cache header for mmap alignment on ARM64

Apple Silicon requires mmap offsets to be 16K-aligned (page size is
16384, not 4096). Bump RSPAMD_MAP_CACHE_HEADER_SIZE to 16384 to work
on all common architectures.

6 weeks ago[Feature] Wire fasttext lang detector through maps infrastructure
Vsevolod Stakhov [Wed, 25 Feb 2026 14:51:07 +0000 (14:51 +0000)] 
[Feature] Wire fasttext lang detector through maps infrastructure

The fasttext language detector now supports HTTP/HTTPS URLs for model
loading via the maps system, enabling automatic download, disk caching,
periodic reload, and cross-worker mmap sharing.

Changes:
- fasttext_model::load() accepts an offset parameter for mmap at a
  non-zero position (used with page-aligned map cache files)
- fasttext_langdet uses rspamd_map_is_map() to detect URLs vs local
  paths; URLs go through rspamd_map_add() with RSPAMD_MAP_FILE_NO_READ
- Map callbacks (read/fin/dtor) handle atomic model swap on reload
- Local file paths continue to work as before with direct loading

6 weeks ago[Feature] Page-aligned map cache header for no_file_read mmap support
Vsevolod Stakhov [Wed, 25 Feb 2026 12:57:35 +0000 (12:57 +0000)] 
[Feature] Page-aligned map cache header for no_file_read mmap support

Upgrade HTTP map cache file format to use a page-aligned (4096 byte)
header so that no_file_read consumers (CDB, fasttext models) can mmap
the cached file directly at a fixed offset without needing a separate
sidecar file.

Changes:
- Bump cache magic to rmcd2001; old rmcd2000 files are read gracefully
  and rewritten on next update
- Header page (4096 bytes) contains struct + etag + zero padding; data
  payload always starts at RSPAMD_MAP_CACHE_HEADER_SIZE offset
- For no_file_read maps with HTTP backends, pass the cache file path
  to read_callback (instead of payload bytes) with no_file_read_offset
  set to 4096; for file backends offset remains 0
- Add rspamd_map_get_no_file_read_offset() public API for consumers
- Refactor cache path computation into rspamd_map_cache_file_path()
  helper, removing 4 duplicate hash+snprintf blocks
- Handle all 3 HTTP data delivery paths: live GET (controller),
  SHM cache read (scanner workers), disk cache preload (startup)

6 weeks agoMerge pull request #5908 from dragoangel/fix-bayes-expiry
Vsevolod Stakhov [Wed, 25 Feb 2026 10:28:35 +0000 (10:28 +0000)] 
Merge pull request #5908 from dragoangel/fix-bayes-expiry

fix: properly set bayes class labels

6 weeks agoMerge pull request #5907 from moisseev/senderscore
Vsevolod Stakhov [Wed, 25 Feb 2026 09:53:18 +0000 (09:53 +0000)] 
Merge pull request #5907 from moisseev/senderscore

[Conf] Disable Validity SenderScore RBLs by default

6 weeks agoMerge branch 'master' into fix-bayes-expiry 5908/head
Dmitriy Alekseev [Tue, 24 Feb 2026 21:58:27 +0000 (22:58 +0100)] 
Merge branch 'master' into fix-bayes-expiry

6 weeks ago[Fix] Propagate source/classification URL flags to query-extracted URLs
Vsevolod Stakhov [Tue, 24 Feb 2026 21:42:03 +0000 (21:42 +0000)] 
[Fix] Propagate source/classification URL flags to query-extracted URLs

When a URL is found inside the query string of another URL (e.g.
http://redir.com/?q=http://target.com), the inner URL now inherits
source/classification flags (FROM_TEXT, CONTENT, SUBJECT, INVISIBLE)
from the outer URL via RSPAMD_URL_FLAG_PROPAGATE_MASK.

Previously, inner URLs only received the QUERY flag, losing all context
about where the parent URL was found. This caused inconsistencies in
plugins that filter URLs by source flags (e.g. RBL content URL filtering).

Also fixes two bugs in the subject path (rspamd_url_task_subject_callback):
- hostlen check used outer URL instead of inner query URL
- QUERY flag was not set on URLs extracted from subject URL queries

6 weeks agofix: properly set bayes class labels to S and H for class spam and ham class names...
Dmitriy Alekseev [Tue, 24 Feb 2026 17:19:40 +0000 (18:19 +0100)] 
fix: properly set bayes class labels to S and H for class spam and ham class names, adjust bayes expiry to write occurrences as spam and ham instead of S and H

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
6 weeks ago[Conf] Disable Validity SenderScore RBLs by default 5907/head
Alexander Moisseev [Tue, 24 Feb 2026 14:45:55 +0000 (17:45 +0300)] 
[Conf] Disable Validity SenderScore RBLs by default

Both bl.score.senderscore.com and score.senderscore.com require
a registered MyValidity account to function. Unregistered IPs
receive 127.255.255.255 (blocked) for all queries, making the
RBLs non-functional without prior account setup regardless of
query volume.

Disable senderscore_reputation (score.senderscore.com) by default
and update the senderscore (bl.score.senderscore.com) comment to
reflect the actual registration requirement. Users must register
their querying IPs at https://my.validity.com before enabling
either RBL.

6 weeks ago[Fix] Move binary data from KEYS to ARGV in fuzzy and neural scripts
Vsevolod Stakhov [Tue, 24 Feb 2026 14:39:49 +0000 (14:39 +0000)] 
[Fix] Move binary data from KEYS to ARGV in fuzzy and neural scripts

Same expand_keys corruption issue as the Bayes fix: binary data passed
as KEYS gets template-expanded, stripping 0x24 bytes.

fuzzy_update: move operation parameters and raw digest to ARGV, keep
only hash_key, count_key, and shingle keys as KEYS.

neural_save_unlock: move compressed ANN weights, PCA matrix, and all
non-key parameters to ARGV, keep only Redis key names as KEYS.

6 weeks ago[Fix] Move binary msgpack data from KEYS to ARGV in Bayes Redis scripts
Vsevolod Stakhov [Tue, 24 Feb 2026 13:38:31 +0000 (13:38 +0000)] 
[Fix] Move binary msgpack data from KEYS to ARGV in Bayes Redis scripts

When expand_keys is enabled, lutil.template() is applied to all KEYS
arguments of EVALSHA commands. This corrupts binary msgpack blobs by
stripping 0x24 ('$') bytes, breaking str8 headers where the length
byte equals 36. Move non-key arguments (msgpack tokens, config, labels)
to ARGV which is not subject to key expansion.

Also fix msgpack_str_len off-by-one for str32 (4+len -> 5+len).

6 weeks ago[Fix] Fix subprocess cleanup race in spawn_process SIGCHLD handler
Vsevolod Stakhov [Tue, 24 Feb 2026 11:37:27 +0000 (11:37 +0000)] 
[Fix] Fix subprocess cleanup race in spawn_process SIGCHLD handler

When the I/O handler finishes reading the subprocess reply before
SIGCHLD arrives, neither handler would call rspamd_lua_cbdata_free:
the I/O handler skips cleanup because dead=FALSE, and the SIGCHLD
handler skips cleanup because replied=TRUE. This leaves the subprocess
in the main process workers table, causing shutdown to wait for a
child that has already exited.

Fix by always calling rspamd_lua_cbdata_free in the SIGCHLD handler
when replied=TRUE, since the I/O handler has already finished and
deferred cleanup to us.

6 weeks ago[Fix] Use main Lua state in config object destructors
Vsevolod Stakhov [Tue, 24 Feb 2026 11:29:54 +0000 (11:29 +0000)] 
[Fix] Use main Lua state in config object destructors

The periodic, cached config, and symbol callback destructors stored a
raw lua_State pointer captured at registration time. If this was a
thread/coroutine state from the thread pool, it could be garbage
collected before the destructor runs during config cleanup, causing a
use-after-free crash in luaL_unref.

Use RSPAMD_LUA_CFG_STATE(cfg) instead, which is the main Lua state
that remains valid throughout config_free until rspamd_lua_close.

6 weeks ago[Fix] Prevent LuaJIT GC stalls after neural training
Vsevolod Stakhov [Tue, 24 Feb 2026 11:07:50 +0000 (11:07 +0000)] 
[Fix] Prevent LuaJIT GC stalls after neural training

The LuaJIT GC atomic phase is non-incremental and processes the entire
gray/grayagain object graph in one uninterruptible pass. After neural
training completes, the controller's Lua heap is bloated with training
temporaries, causing the next GC cycle's atomic phase to stall the
event loop at 100% CPU for an extended period.

Two fixes:
- Force collectgarbage('collect') in ann_trained callback to clean up
  training temporaries before they accumulate
- Stop/restart GC around fork() in spawn_process to prevent the child
  from inheriting a mid-cycle GC state that triggers thrashing

6 weeks agoMerge pull request #5903 from rspamd/vstakhov-embeddings-pool
Vsevolod Stakhov [Tue, 24 Feb 2026 08:48:12 +0000 (08:48 +0000)] 
Merge pull request #5903 from rspamd/vstakhov-embeddings-pool

Fasttext embed: SIF weighting and conv1d pooling

6 weeks agoMerge pull request #5894 from rspamd/vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Tue, 24 Feb 2026 08:47:56 +0000 (08:47 +0000)] 
Merge pull request #5894 from rspamd/vstakhov-multi-fuzzy-flag

[Feature] Multi-flag fuzzy hash support

6 weeks agoMerge pull request #5898 from dragoangel/fix-multiclass-bayes
Vsevolod Stakhov [Tue, 24 Feb 2026 08:31:44 +0000 (08:31 +0000)] 
Merge pull request #5898 from dragoangel/fix-multiclass-bayes

[Fix] Fix multiclass Bayes learn cache and classifier isolation

6 weeks ago[Fix] Use backward compatible learned_ids for spam and ham class names 5898/head
Dmitriy Alekseev [Mon, 23 Feb 2026 23:28:09 +0000 (00:28 +0100)] 
[Fix] Use backward compatible learned_ids for spam and ham class names

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
6 weeks agoMerge branch 'master' into fix-multiclass-bayes
Dmitriy Alekseev [Mon, 23 Feb 2026 17:34:20 +0000 (18:34 +0100)] 
Merge branch 'master' into fix-multiclass-bayes

6 weeks agoMerge pull request #5900 from moisseev/classifiers
Vsevolod Stakhov [Mon, 23 Feb 2026 09:25:51 +0000 (09:25 +0000)] 
Merge pull request #5900 from moisseev/classifiers

[Feature] Extend /bayes/classifiers endpoint with metadata

6 weeks ago[Feature] Fasttext embed: multi-scale conv1d pooling for text features 5903/head
Vsevolod Stakhov [Mon, 23 Feb 2026 09:14:35 +0000 (09:14 +0000)] 
[Feature] Fasttext embed: multi-scale conv1d pooling for text features

Add conv1d output mode to the fasttext_embed provider that applies
multi-scale max-over-time pooling over sliding word windows in Lua,
producing compact feature vectors for the neural plugin's dense ANN.

For each kernel size (default {1, 3, 5}), word vectors are averaged
within sliding windows, then max-pooled across positions per channel.
Each scale's features are L2-normalized independently for balanced
contribution. This replaces the previous approach of feeding raw NCW
matrices into KANN conv1d layers.

Also adds max1d and input3d layer bindings to the KANN Lua API, and
includes conv1d settings (kernel_sizes, conv_pooling, max_words) in
the providers_config_digest for automatic retraining on config change.

6 weeks ago[Feature] Fasttext embed: SIF word weighting for sentence vectors
Vsevolod Stakhov [Sun, 22 Feb 2026 18:11:44 +0000 (18:11 +0000)] 
[Feature] Fasttext embed: SIF word weighting for sentence vectors

Add Smooth Inverse Frequency (SIF) weighting to the fasttext embedding
provider. Common words (the, is, a) get near-zero weight while
distinctive words (viagra, invoice) get high weight, significantly
improving embedding quality without changing dimensionality.

Expose get_word_frequency() from the fasttext shim C++ API and Lua
bindings, returning p(word) = count/ntokens from the model vocabulary.

SIF is enabled by default (sif_weight=true, sif_a=1e-3). Combined with
multi-model mean+max pooling, improves F1 from 0.87 to 0.90 in testing.

6 weeks agoMerge pull request #5897 from rspamd/vstakhov-more-embeddings
Vsevolod Stakhov [Sun, 22 Feb 2026 17:34:43 +0000 (17:34 +0000)] 
Merge pull request #5897 from rspamd/vstakhov-more-embeddings

Replace libfasttext with mmap-based built-in shim

6 weeks agoMerge branch 'master' into fix-multiclass-bayes
Dmitriy Alekseev [Sun, 22 Feb 2026 17:31:30 +0000 (18:31 +0100)] 
Merge branch 'master' into fix-multiclass-bayes

6 weeks agoMerge pull request #5902 from dragoangel/patch-16
Vsevolod Stakhov [Sun, 22 Feb 2026 17:08:57 +0000 (17:08 +0000)] 
Merge pull request #5902 from dragoangel/patch-16

[Fix] Properly count rspamd-proxy-milter conns count

6 weeks ago[Minor] Replace hash table with linear search for class deduplication 5900/head
Alexander Moisseev [Sun, 22 Feb 2026 15:45:03 +0000 (18:45 +0300)] 
[Minor] Replace hash table with linear search for class deduplication

Number of classes per classifier is always small (N < 10 in practice),
so hash table overhead outweighs its O(1) lookup benefit. Linear search
over the already-built UCL array is simpler and faster here.

6 weeks agoMerge branch 'master' into patch-16 5902/head
Dmitriy Alekseev [Sun, 22 Feb 2026 15:25:46 +0000 (16:25 +0100)] 
Merge branch 'master' into patch-16

6 weeks agoRefactor nconns decrement handling in milter sessions
Dmitriy Alekseev [Sun, 22 Feb 2026 13:46:51 +0000 (14:46 +0100)] 
Refactor nconns decrement handling in milter sessions

Update nconns decrement logic for milter sessions to ensure accurate connection tracking during session lifecycle without memory leaks.

6 weeks ago[Feature] Fasttext embed: multi-model and mean+max pooling 5897/head
Vsevolod Stakhov [Sun, 22 Feb 2026 11:07:49 +0000 (11:07 +0000)] 
[Feature] Fasttext embed: multi-model and mean+max pooling

Use all configured language_models for every message by default
(multi_model=true), concatenating vectors from each model for
richer cross-lingual representations.

Add mean+max pooling (pooling="mean_max" default) which concatenates
the average word vector with element-wise max pooling, capturing both
typical and prominent semantic features.

With 2 quantized 50-dim models this produces 200-dim vectors instead
of 50, significantly improving classification (F1 0.51 -> 0.87 in
testing).

6 weeks ago[Minor] Rename llm_embeddings classifier type to neural in classifier_test
Vsevolod Stakhov [Sun, 22 Feb 2026 11:00:56 +0000 (11:00 +0000)] 
[Minor] Rename llm_embeddings classifier type to neural in classifier_test

Accept 'llm_embeddings' as a legacy alias for backward compatibility.

6 weeks agoMerge pull request #5901 from dragoangel/patch-15
Vsevolod Stakhov [Sat, 21 Feb 2026 19:14:44 +0000 (19:14 +0000)] 
Merge pull request #5901 from dragoangel/patch-15

[Fix] Actualize links in README.md

6 weeks ago[Fix] Fasttext shim: fix quantized model support and add hierarchical softmax
Vsevolod Stakhov [Sat, 21 Feb 2026 18:54:10 +0000 (18:54 +0000)] 
[Fix] Fasttext shim: fix quantized model support and add hierarchical softmax

- Add pruneidx support in dictionary subword computation: pruned models
  (like lid.176.ftz) remap subword hash IDs through the prune index table,
  matching FastText's pushHash() behavior
- Apply per-row norm from quantized norm PQ (NPQ) when decoding QMatrix
  rows, fixing vector magnitude for quantized models
- Implement hierarchical softmax prediction via Huffman tree DFS,
  matching FastText's HierarchicalSoftmaxLoss for models with loss=hs
- Add predict() method to Lua rspamd_fasttext bindings

6 weeks ago[Fix] Fasttext shim: fix binary format parsing and harden against corrupt models
Vsevolod Stakhov [Sat, 21 Feb 2026 17:27:42 +0000 (17:27 +0000)] 
[Fix] Fasttext shim: fix binary format parsing and harden against corrupt models

- Fix QMatrix load order: read codesize+codes before PQ (not after)
- Fix PQ centroid count: use dim*ksub (not nsubq*ksub*dsub)
- Fix PQ centroid addressing: match FastText's get_centroids() for last sub-quantizer
- Fix dictionary load: read size_ field before nwords/nlabels
- Fix output matrix: always read qout bool between input and output matrices
- Fix subword n-gram skip: only skip single-char BOW/EOW, not full wrapped word

Add comprehensive sanity checks for all untrusted values from model files:
- Validate dimensions, entry counts, matrix sizes against sane upper bounds
- Overflow-safe multiplication for matrix element counts
- Bounds checks on centroid, codes, and dense matrix data access
- Null pointer guards on all matrix operations
- Replace throwing .at() with bounds-checked pointer return
- Limit string reads to 1024 bytes to prevent runaway allocation
- Return nullptr/false from loaders on validation failure
- Guard Lua bindings against empty/short vectors from get_word_vector

6 weeks ago[Feature] WebUI: Add multi-class classifier support to learning UI
Alexander Moisseev [Sat, 21 Feb 2026 15:56:04 +0000 (18:56 +0300)] 
[Feature] WebUI: Add multi-class classifier support to learning UI

 - Support /learnclass endpoint with Classifier and Class headers
   for multi-class learning.

 - Handle both old (array) and new (metadata object) /bayes/classifiers
   response formats for backward compatibility.

 - Add dynamic UI switching based on classifier type:
   * Binary classifiers: show HAM/SPAM upload buttons
   * Multi-class classifiers: show class dropdown + Learn button

 - Display classifier metadata as text badges in dropdown:
   [multi-class] [per-user]

 - Hide "All classifiers" option when multi-class classifiers present
   (different classifiers may have different class sets).

6 weeks ago[Minor] Fasttext shim: addressing review comments
Vsevolod Stakhov [Sat, 21 Feb 2026 16:46:19 +0000 (16:46 +0000)] 
[Minor] Fasttext shim: addressing review comments

- Use ICU U8_NEXT for UTF-8 iteration instead of handcrafted code
- Replace exception-based error handling with fail-bit pattern in
  binary_reader, propagating errors via tl::expected
- Replace std::sort with std::reverse after min-heap extraction

6 weeks ago[Feature] Replace libfasttext with mmap-based built-in shim
Vsevolod Stakhov [Fri, 20 Feb 2026 11:21:38 +0000 (11:21 +0000)] 
[Feature] Replace libfasttext with mmap-based built-in shim

Replace the external libfasttext shared library dependency with a
zero-dependency C++20 shim that reads .bin/.ftz models directly.
The large input matrix is mmap'd with MAP_SHARED/PROT_READ so all
worker processes share the same physical pages after fork.

This eliminates:
- C++ exception ABI issues across shared library boundaries
  (no more fork-probe hack in lua_fasttext)
- The ENABLE_FASTTEXT cmake option (always compiled in now)
- Per-worker heap copies of the model (~500MB-7GB savings)

New files:
- contrib/fasttext_shim/fasttext_shim.{h,cxx} - built-in shim
- contrib/fasttext_shim/CMakeLists.txt
- lualib/plugins/neural/providers/ - embedding providers
- src/lua/lua_fasttext.cxx - rewritten Lua bindings

6 weeks agoMerge pull request #5899 from moisseev/multi-class
Vsevolod Stakhov [Sat, 21 Feb 2026 16:37:56 +0000 (16:37 +0000)] 
Merge pull request #5899 from moisseev/multi-class

[Minor] Use explicit class for colorizing ham/spam in Bayesian statis…

6 weeks ago[Fix] Properly count rspamd-proxy-milter conns count
Dmitriy Alekseev [Sat, 21 Feb 2026 14:51:42 +0000 (15:51 +0100)] 
[Fix] Properly count rspamd-proxy-milter conns count

6 weeks agoUpdate README links to point to new documentation site 5901/head
Dmitriy Alekseev [Sat, 21 Feb 2026 13:39:28 +0000 (14:39 +0100)] 
Update README links to point to new documentation site

6 weeks agoUpdate logo image source in README.md
Dmitriy Alekseev [Sat, 21 Feb 2026 13:24:01 +0000 (14:24 +0100)] 
Update logo image source in README.md

6 weeks ago[Feature] Extend /bayes/classifiers endpoint with metadata
Alexander Moisseev [Sat, 21 Feb 2026 06:43:54 +0000 (09:43 +0300)] 
[Feature] Extend /bayes/classifiers endpoint with metadata

- Return classifier metadata (type, per_user, classes) instead of just names array.

Old format: ["bayes", "bayes_multi"]
New format: {classifiers: [{name, type, per_user, classes: [...]}]}

- Export rspamd_classifier_is_per_user() and rspamd_classifier_type()
helper functions to stat_api.h for reuse.

6 weeks ago[Minor] Use explicit class for colorizing ham/spam in Bayesian statistics table 5899/head
Alexander Moisseev [Fri, 20 Feb 2026 19:18:37 +0000 (22:18 +0300)] 
[Minor] Use explicit class for colorizing ham/spam in Bayesian statistics table

Use statfile.class field from /stat response to determine CSS class
for table cells instead of guessing from symbol name. Falls back to
symbol name matching for backward compatibility with older Rspamd.

6 weeks ago[Fix] Fix multiclass Bayes learn cache and classifier isolation
Dmitriy Alekseev [Fri, 20 Feb 2026 16:31:33 +0000 (17:31 +0100)] 
[Fix] Fix multiclass Bayes learn cache and classifier isolation

Propagate task->classifier from the HTTP header so class-targeted
learning skips unrelated classifiers early. Validate the requested
class exists in a statfile before tokenisation to fail fast with a
clear 404 error on misconfigured setups.

Replace numeric class_id hash cache keys in learned_ids with direct class
name strings throughout the Redis cache layer (C, Lua, Redis scripts) to
fix uint64_t precision loss through Lua's 53-bit doubles, which caused
equality checks to always fail for arbitrary multiclass class names.

Add RSPAMD_FLAG_CLASSIFIER_MULTICLASS at config load time
to route probability lookups to the correct result type per classifier.

Store multiclass and binary Bayes results under per-classifier mempool
keys (multiclass_result:<name>, bayes_prob:<name>) and update the
set/get API to take a classifier_name parameter, preventing
cross-contamination when multiple classifiers are configured.

Switch multiclass result allocation from heap (g_new0/g_new) to task
memory pool, eliminating the need for rspamd_multiclass_result_free.

Inject can_learn_prob and can_learn_class into the mempool before
invoking Lua learn conditions, replacing the legacy asymmetric
spam_min/ham_max pair with a unified min_prob threshold.

Remove the cl_skipped gate in rspamd_stat_cache_check so the Redis
learned_ids cache is always consulted before learn conditions run for
proper logging and response handling on relearning same emails.

6 weeks agoMerge pull request #5893 from moisseev/multi-class
Vsevolod Stakhov [Fri, 20 Feb 2026 10:38:19 +0000 (10:38 +0000)] 
Merge pull request #5893 from moisseev/multi-class

[Feature] WebUI: Display classifier metadata in Bayesian statistics table

6 weeks ago[Minor] Remove unnecessary ucl_object_insert_key return check 5893/head
Alexander Moisseev [Fri, 20 Feb 2026 08:52:20 +0000 (11:52 +0300)] 
[Minor] Remove unnecessary ucl_object_insert_key return check

No other place in the codebase checks ucl_object_insert_key return
value. Remove the check to match Rspamd coding convention.

7 weeks ago[Fix] Backward-compatible version negotiation for multi-flag fuzzy 5894/head
Vsevolod Stakhov [Thu, 19 Feb 2026 17:32:10 +0000 (17:32 +0000)] 
[Fix] Backward-compatible version negotiation for multi-flag fuzzy

Use an unused bit (bit 4) in the version byte as a v2 capability flag.
New clients send version 4|0x10 initially; old servers mask to 4 and
work normally, new servers see the cap bit and send v2 replies. Once
a v2 reply is received, subsequent requests use native version 5.

7 weeks agoMerge branch 'master' into vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Thu, 19 Feb 2026 16:52:19 +0000 (16:52 +0000)] 
Merge branch 'master' into vstakhov-multi-fuzzy-flag

7 weeks ago[Fix] Store small PDF objects without counting toward limit
Vsevolod Stakhov [Thu, 19 Feb 2026 16:16:18 +0000 (16:16 +0000)] 
[Fix] Store small PDF objects without counting toward limit

Small objects are now stored but don't count toward max_pdf_objects,
so padding evasion objects can't exhaust the budget while legitimate
small references (like JavaScript dictionaries) are preserved.

7 weeks agoMerge branch 'master' into vstakhov-multi-fuzzy-flag
Vsevolod Stakhov [Thu, 19 Feb 2026 15:01:48 +0000 (15:01 +0000)] 
Merge branch 'master' into vstakhov-multi-fuzzy-flag

7 weeks ago[Fix] Defeat PDF object padding evasion in extract_outer_objects
Vsevolod Stakhov [Thu, 19 Feb 2026 14:59:58 +0000 (14:59 +0000)] 
[Fix] Defeat PDF object padding evasion in extract_outer_objects

Decouple iteration limit from storage limit so that thousands of tiny
dummy obj/endobj pairs no longer consume all max_pdf_objects slots.

- Add min_obj_content_size (default 32) config: objects smaller than
  this are skipped during extraction (they carry no useful content)
- extract_outer_objects now iterates ALL start/end positions but only
  stores objects that pass the size filter, up to max_pdf_objects
- attach_pdf_streams similarly iterates all stream positions
- Add timeout checks in both loops to stay within pdf_process_timeout

7 weeks ago[Fix] Adjust positions in URL matcher properly
Vsevolod Stakhov [Thu, 19 Feb 2026 14:17:26 +0000 (14:17 +0000)] 
[Fix] Adjust positions in URL matcher properly