]> git.ipfire.org Git - thirdparty/rspamd.git/log
thirdparty/rspamd.git
27 hours ago[Fix] Fix infinite loop and OOB read in archive processing master
Vsevolod Stakhov [Wed, 8 Apr 2026 19:27:39 +0000 (20:27 +0100)] 
[Fix] Fix infinite loop and OOB read in archive processing

- RAR v5 extra area parsing: fix wrong loop boundary (used advanced `p`
  instead of `ex + extra_sz`), wrong advancement (`cur_sz` alone didn't
  account for the size vint bytes), and wrong remain calculation for the
  second vint read. Add bounds validation for `cur_sz`.
- 7zip codec ID: validate `p + sz <= end` before reading codec ID bytes
  to prevent out-of-bounds read with malformed archives.

39 hours agoMerge pull request #5968 from moisseev/autolearnstats
Vsevolod Stakhov [Wed, 8 Apr 2026 07:49:21 +0000 (08:49 +0100)] 
Merge pull request #5968 from moisseev/autolearnstats

[Test] Add functional test for rspamadm autolearnstats command

4 days ago[Minor] Update version to 4.0.2
Vsevolod Stakhov [Sun, 5 Apr 2026 19:32:44 +0000 (20:32 +0100)] 
[Minor] Update version to 4.0.2

4 days agoRelease 4.0.1 4.0.1
Vsevolod Stakhov [Sun, 5 Apr 2026 18:56:32 +0000 (19:56 +0100)] 
Release 4.0.1

4 days ago[Feature] Allow custom response codes for fuzzy dynamic blocks
Vsevolod Stakhov [Sun, 5 Apr 2026 12:13:16 +0000 (13:13 +0100)] 
[Feature] Allow custom response codes for fuzzy dynamic blocks

Dynamic blocks always returned 503, making them indistinguishable
from static blacklist blocks on the client side.  Ratelimit scripts
need to return 403 so that fuzzy_check clients can map the response
to FUZZY_RATELIMITED instead of FUZZY_FORBIDDEN.

Add response_code field to rspamd_fuzzy_dynamic_ban and propagate
it through rspamd_fuzzy_check_client (now returns int: 0=allowed,
>0=block code) and rspamd_fuzzy_block_addr.  The Lua API
block_fuzzy_client accepts an optional 6th argument for the code.

4 days ago[Test] Add functional test for rspamadm autolearnstats command 5968/head
Alexander Moisseev [Sat, 4 Apr 2026 08:33:09 +0000 (11:33 +0300)] 
[Test] Add functional test for rspamadm autolearnstats command

Scans sample messages through rspamd to generate autolearn logs dynamically,
then validates autolearnstats output parsing, column headers, and filtering.

4 days agoMerge pull request #5966 from rspamd/vstakhov-arc-aar-fix
Vsevolod Stakhov [Sun, 5 Apr 2026 09:20:32 +0000 (10:20 +0100)] 
Merge pull request #5966 from rspamd/vstakhov-arc-aar-fix

[Fix] Use LPeg grammar for AAR header parsing

5 days ago[Fix] Use LPeg grammar for AAR header parsing 5966/head
Vsevolod Stakhov [Sat, 4 Apr 2026 15:41:17 +0000 (16:41 +0100)] 
[Fix] Use LPeg grammar for AAR header parsing

Replace the naive split-by-semicolon + regex approach in
parse_arc_header with a proper LPeg-based parser that respects
RFC 8601 comments (...) and quoted strings "..." when splitting
AAR elements.  The old unanchored pattern matched "i=N" inside
comments like arc=pass ("domain:s=sel:i=2"), misclassifying
the element as an ARC instance index.  This left cur_aar.ar nil,
crashing gen_arc_seal_cb and producing cv=fail on valid chains.

Add parse_aar_header() to lua_auth_results.lua with shared LPeg
atoms for comment/quote-aware semicolon splitting, and unit tests
covering the #5963 scenario and edge cases.

Closes #5963

5 days ago[Fix] Keep original fd in proxy session after milter dup
Vsevolod Stakhov [Sat, 4 Apr 2026 11:14:54 +0000 (12:14 +0100)] 
[Fix] Keep original fd in proxy session after milter dup

Revert the client_sock = -1 added in 2c56437e7 which incorrectly
assumed the milter library owned the accepted fd. The milter library
calls dup() internally (since 4a75d59cc) and works with the copy,
so the original fd must stay in session->client_sock for
proxy_session_dtor to close it. Without this, every milter connection
leaked one fd permanently, causing unbounded CLOSE_WAIT accumulation.

This restores the pre-2c56437e7 behaviour where proxy_session_dtor
closed the original fd and the milter dtor closed the dup'd copy.

6 days ago[Fix] Use task_timeout instead of upstream timeout for proxy self-scan
Vsevolod Stakhov [Fri, 3 Apr 2026 14:53:55 +0000 (15:53 +0100)] 
[Fix] Use task_timeout instead of upstream timeout for proxy self-scan

The proxy self-scan path was using default_upstream->timeout (the
milter/HTTP wire timeout, typically 120s) as the task processing
timeout. This caused tasks to linger for up to 2 minutes before
being reaped, leading to massive CLOSE_WAIT fd accumulation under
load.

Use ctx->task_timeout (derived from cfg->task_timeout, default 8s)
instead, matching how the normal worker handles task timeouts. The
previous else-if branch also had a bug where the repeat interval
used upstream timeout while the initial used cfg->task_timeout.

6 days agoMerge pull request #5962 from rspamd/vstakhov-fuzzy-limits
Vsevolod Stakhov [Fri, 3 Apr 2026 14:48:02 +0000 (15:48 +0100)] 
Merge pull request #5962 from rspamd/vstakhov-fuzzy-limits

[Feature] Fuzzy worker dynamic block API

6 days ago[Feature] Expose ratelimit_whitelist check to Lua via worker method 5962/head
Vsevolod Stakhov [Fri, 3 Apr 2026 11:31:09 +0000 (12:31 +0100)] 
[Feature] Expose ratelimit_whitelist check to Lua via worker method

Add worker:is_ratelimit_whitelisted(addr) Lua method that checks the
fuzzy worker's existing ratelimit_whitelist radix map, so Lua scripts
can skip provisional blocks for whitelisted IPs (e.g. spamtraps).

7 days ago[Fix] Use ev_now() instead of rspamd_get_ticks() for dynamic ban expiry
Vsevolod Stakhov [Thu, 2 Apr 2026 15:54:37 +0000 (16:54 +0100)] 
[Fix] Use ev_now() instead of rspamd_get_ticks() for dynamic ban expiry

rspamd_get_ticks(FALSE) returns CLOCK_MONOTONIC (seconds since boot),
while rspamd_util.get_time() in Lua returns ev_time() (wall-clock unix
epoch). Using mismatched clocks made expire_ts comparisons nonsensical.

Switch to ev_now(ctx->event_loop) which matches the ev_time() epoch used
by Lua callers of block_fuzzy_client.

7 days ago[Feature] Add dynamic block API to fuzzy storage worker
Vsevolod Stakhov [Thu, 2 Apr 2026 09:30:43 +0000 (10:30 +0100)] 
[Feature] Add dynamic block API to fuzzy storage worker

Adds worker:block_fuzzy_client(addr, prefix_len[, expire_ts[, reason]])
Lua method that inserts a CIDR network block into a per-worker btrie
(dynamic_blocked_nets). Checked in rspamd_fuzzy_check_client() after
the static blocked_ips map.

expire_ts is an absolute monotonic timestamp (0 = permanent). Callers
compute get_time() once and pass it to a batch of block calls — C never
calls get_time() itself. Re-inserting an existing prefix updates the
expire_ts and reason in-place (idempotent).

Each fuzzy worker independently applies bans via Redis polling; no new
IPC commands or control socket extensions are needed.

8 days agoMerge pull request #5959 from rspamd/vstakhov-settings-merge
Vsevolod Stakhov [Wed, 1 Apr 2026 21:04:46 +0000 (22:04 +0100)] 
Merge pull request #5959 from rspamd/vstakhov-settings-merge

Layered settings merge with weak dependencies

9 days ago[Fix] Rename remaining dep.weak to dep.hard in rdeps 5959/head
Vsevolod Stakhov [Tue, 31 Mar 2026 21:16:02 +0000 (22:16 +0100)] 
[Fix] Rename remaining dep.weak to dep.hard in rdeps

9 days ago[Feature] Mark genuinely hard dependencies in plugins
Vsevolod Stakhov [Tue, 31 Mar 2026 16:00:29 +0000 (17:00 +0100)] 
[Feature] Mark genuinely hard dependencies in plugins

Annotate dependencies where the dependent truly cannot function
without the dep's output:
- DMARC_MUNGED → DMARC_CHECK (nothing to munge without policy)
- ARC_SIGNED → ARC_CHECK, DMARC_CHECK (needs cache and AAR data)
- ARC_DMARC_ADJUSTMENT → DMARC_CHECK, ARC_CHECK (reads both)
- BIMI_CHECK → DMARC_CHECK (BIMI requires DMARC pass)
- SETTINGS_APPLY → SETTINGS_CHECK, REDIS_SETTINGS (merge needs data)

DMARC → SPF/DKIM remains weak: DMARC falls back to whichever
mechanism is available. Disabling one shouldn't kill DMARC.

9 days ago[Rework] Make hard deps opt-in, weak deps default
Vsevolod Stakhov [Tue, 31 Mar 2026 15:57:12 +0000 (16:57 +0100)] 
[Rework] Make hard deps opt-in, weak deps default

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).

9 days ago[Test] Add functional tests for settings merge
Vsevolod Stakhov [Tue, 31 Mar 2026 11:26:40 +0000 (12:26 +0100)] 
[Test] Add functional tests for settings merge

9 Robot Framework tests covering:
- No settings baseline (all symbols fire)
- Settings-ID enables symbol subset via bitsets
- Coexistence: Settings-ID + inline action override
- Coexistence: Settings-ID + inline score override
- Inline symbols_enabled filtering
- Weak dep: disabled dep doesn't cascade to dependent
- Hard dep: disabled dep cascades to dependent
- Prefilter deps: SETTINGS_APPLY runs after SETTINGS_CHECK
- Coexistence: settings-ID groups + inline action override

9 days ago[Test] Add unit tests for settings merge infrastructure
Vsevolod Stakhov [Tue, 31 Mar 2026 11:18:02 +0000 (12:18 +0100)] 
[Test] Add unit tests for settings merge infrastructure

11 test cases covering: single layer passthrough, actions/scores
override by higher layer, symbols_enabled union, enable vs disable
conflict resolution, same-layer disable-wins, whitelist any-layer,
merge metadata, empty layers, flags union, subject override.

Also fix use-after-free: layer_entry now takes UCL ref via RAII
(ref in constructor, unref in destructor/move).

9 days ago[Rework] Replace GHashTable with id_list for force-enabled IDs
Vsevolod Stakhov [Tue, 31 Mar 2026 11:08:09 +0000 (12:08 +0100)] 
[Rework] Replace GHashTable with id_list for force-enabled IDs

Move force_enabled_ids from GHashTable mempool variable to a
native id_list* field in symcache_runtime. Uses lazy allocation
and cleanup in savepoint_dtor. Avoids GLib in C++ internals
and mempool variable indirection.

9 days ago[Fix] Use finished status in disable_all_symbols
Vsevolod Stakhov [Tue, 31 Mar 2026 10:45:33 +0000 (11:45 +0100)] 
[Fix] Use finished status in disable_all_symbols

disable_all_symbols implements "disable all, then enable some"
for symbols_enabled/groups_enabled. Using `disabled` status here
caused cascade-disable of hard dependents: if an enabled symbol
depended on a non-enabled one, the enabled symbol was wrongly
cascade-disabled.

Revert to `finished` for disable_all_symbols. Reserve `disabled`
status for explicit symbol/group disable via settings rules
(disable_symbol), where cascade-disable IS the intended behavior.

9 days agoMerge pull request #5960 from lab8-hmurakami/fix/redis-backend-include-memory
Vsevolod Stakhov [Tue, 31 Mar 2026 09:10:28 +0000 (10:10 +0100)] 
Merge pull request #5960 from lab8-hmurakami/fix/redis-backend-include-memory

[Fix] Add missing #include <memory> in redis_backend.cxx

9 days ago[Fix] Create rdeps for same-stage non-filter dependencies
Vsevolod Stakhov [Tue, 31 Mar 2026 09:03:11 +0000 (10:03 +0100)] 
[Fix] Create rdeps for same-stage non-filter dependencies

When a prefilter depends on another prefilter (or postfilter on
postfilter, etc.), create reverse dependencies so that
process_item_rdeps can eagerly start dependents when async deps
complete. Previously rdeps were only created for filter-type deps,
causing same-stage async deps to wait for the next event loop pass.

9 days ago[Fix] Enable deps in pre/postfilters and fix settings flow
Vsevolod Stakhov [Tue, 31 Mar 2026 08:55:34 +0000 (09:55 +0100)] 
[Fix] Enable deps in pre/postfilters and fix settings flow

Add dependency checking to process_pre_postfilters, allowing
prefilters, postfilters and idempotent filters to declare deps
on symbols in the same stage. Previously deps only worked for
the FILTER stage.

Use this to make SETTINGS_APPLY depend on SETTINGS_CHECK (and
REDIS_SETTINGS* when configured), ensuring proper ordering via
the dep system instead of fragile priority hacks.

Restore immediate settings application in collectors for backward
compatibility - SETTINGS_APPLY now only triggers when multiple
layers need merging.

Also fix missing #include <algorithm> for std::sort on GCC.

9 days ago[Fix] Add missing #include <memory> in redis_backend.cxx 5960/head
Hiroshi Murakami [Tue, 31 Mar 2026 04:55:33 +0000 (13:55 +0900)] 
[Fix] Add missing #include <memory> in redis_backend.cxx

std::make_unique is used (line 584) but <memory> is not explicitly
included. This causes compilation failures with some compiler/platform
combinations (e.g. gcc-toolset-12 on Rocky Linux 8) where <memory>
is not transitively included by other headers.

10 days ago[Feature] Add force-enable override for settings conflicts
Vsevolod Stakhov [Mon, 30 Mar 2026 15:30:59 +0000 (16:30 +0100)] 
[Feature] Add force-enable override for settings conflicts

When merged settings explicitly enable a symbol that settings_elt
has in forbidden_ids, the merged result now wins. Uses a per-task
GHashTable (mempool variable "force_enabled_ids") populated during
process_settings() and checked in is_allowed() before consulting
the precomputed bitsets. This implements the "enable > disable,
explicit > implicit" conflict resolution rule.

10 days ago[Feature] Add settings merge point and collect-then-merge flow
Vsevolod Stakhov [Mon, 30 Mar 2026 15:09:40 +0000 (16:09 +0100)] 
[Feature] Add settings merge point and collect-then-merge flow

Refactor settings.lua from first-match-wins to collect-then-merge:
- Settings sources (rules, HTTP, redis, external maps) now collect
  layers via collect_settings_layer() instead of applying directly
- New SETTINGS_APPLY prefilter (priority high=9, below top=10)
  merges collected layers and applies the result
- Prefilter priority ordering ensures SETTINGS_APPLY runs after
  all collectors (including async redis callbacks) complete
- Single-layer case applies directly without merge overhead
- New task:merge_and_apply_settings() Lua method calls C merge

10 days ago[Feature] Add settings merge infrastructure
Vsevolod Stakhov [Mon, 30 Mar 2026 15:05:25 +0000 (16:05 +0100)] 
[Feature] Add settings merge infrastructure

New settings_merge.cxx implements layered settings merge with
proper per-field semantics: actions/scores override by higher
layer, symbols_enabled/disabled union with conflict resolution
(per-symbol > per-group, higher layer wins, disable wins at
equal specificity), whitelist any-true, flags/headers union.

Merge metadata (_merge_info) is embedded in the result for
debugging and logging.

10 days ago[Feature] Allow settings_elt and settings to coexist
Vsevolod Stakhov [Mon, 30 Mar 2026 15:02:38 +0000 (16:02 +0100)] 
[Feature] Allow settings_elt and settings to coexist

Remove mutual exclusion between Settings-ID and Settings header.
Both mechanisms now live on the same task: settings_elt provides
precomputed bitset gating, settings UCL provides runtime overrides
for scores, actions, and symbol enable/disable.

Also allow lua_task_set_settings to replace existing settings
instead of erroring, preparing for future merge infrastructure.

10 days ago[Feature] Add weak dependencies and disabled status to symcache
Vsevolod Stakhov [Mon, 30 Mar 2026 15:00:43 +0000 (16:00 +0100)] 
[Feature] Add weak dependencies and disabled status to symcache

Introduce weak/hard dependency distinction in the symbol cache.
When a hard dependency is disabled by settings, the dependent symbol
is cascade-disabled. Weak dependencies allow the dependent to proceed
without the dependency's output.

Add `disabled` status to cache_item_status (distinct from `finished`)
so cascade-disable logic can distinguish "genuinely completed" from
"disabled by settings".

Annotate known weak deps in plugins: DKIM signing, DMARC, ARC, RBL,
bounce detection, and header checks.

10 days ago[Minor] Update version to 4.0.1
Vsevolod Stakhov [Mon, 30 Mar 2026 13:31:09 +0000 (14:31 +0100)] 
[Minor] Update version to 4.0.1

10 days agoRelease 4.0.0 4.0.0
Vsevolod Stakhov [Mon, 30 Mar 2026 11:56:55 +0000 (12:56 +0100)] 
Release 4.0.0

** Incompatible changes **
* Replace Jump Hash with Ring Hash (Ketama) for consistent upstream hashing;
  per-user Bayes with Redis sharding requires migration via rspamadm statistics_dump migrate
* Include content URLs by default in URL API calls (include_content_urls = true)
* Auto-detect SSL from bind sockets, remove ssl = true worker option
* Replace libfasttext with built-in mmap-based shim; ENABLE_FASTTEXT cmake option removed
* Replace builtin_suspicious TLDs with map-based configuration
* Rename neural autolearn options to match RBL module naming
* proxy: Enable token bucket load balancing by default
* Disable Validity SenderScore RBLs by default (requires MyValidity account)
* Make unknown and broken DKIM keys behaviour conforming to RFC

** Major features **
* Add /checkv3 multipart scan endpoint with per-part zstd compression
* Pluggable async hyperscan cache backend with Redis storage support
* Add multi-flag fuzzy hash support with Lua-based Redis update path (epoch 12)
* Add dual-mode HTML fuzzy: template matching + phishing detection
* Implement HTTPS server support for workers
* proxy: Implement token bucket load balancing for upstreams
* Expose milter headers and extended symbols in legacy RSPAMC/SPAMC protocol
* Add native UUID v7 per task with ClickHouse support
* Add structured formatter to metadata_exporter with zstd compression
* arc: Add trusted_authserv_id option for reuse_auth_results
* Add external pretrained neural model support
* Fasttext embed: multi-model, mean+max pooling, SIF word weighting
* Multi-layer funnel architecture for LLM embeddings
* Add expression-based autolearn for neural LLM providers
* Add rspamadm autolearnstats, logstats, mapstats subcommands
* Add shard migration and multi-class support to statistics_dump
* headers_checks: Add Reply-To address validity checks
* Store matched fuzzy hashes in Redis history
* Add HTTP content negotiation framework with zstd compression
* Extend /stat and /bayes/classifiers endpoints with classifier metadata
* WebUI: Add multi-class classifier support to learning UI
* Split attachment filenames into sub-tokens for Bayes classifier
* Make GPT consensus thresholds configurable with context_augment hook

** Bug fixes **
* [CritFix] Stop ev_io watcher in fuzzy UDP session destroy
* Fix CPU busy-loop in fuzzy TCP client
* Fix SPF address family flag inheritance
* Fix RHEL/CentOS 10+ crypto-policies for SHA-1 DKIM verification
* Fix EVP_PKEY_CTX memory leak in DKIM RSA signing
* Fix ratelimit compatibility with old records
* Fix use-after-free in pending regexp map and multipattern queues
* Self-healing hyperscan cache: delete stale blobs and trigger recompile
* Move binary data from KEYS to ARGV in Redis scripts
* Rework alternative parts detection
* Add PCRE2 complexity checks before JIT compilation
* Default map URL path to "/" when no path component is present

10 days agoMerge pull request #5956 from moisseev/att-fn-sub-tokens
Vsevolod Stakhov [Mon, 30 Mar 2026 08:39:27 +0000 (09:39 +0100)] 
Merge pull request #5956 from moisseev/att-fn-sub-tokens

[Feature] Split attachment filenames into sub-tokens for Bayes classifier

10 days agoMerge pull request #5958 from moisseev/helo-literals
Vsevolod Stakhov [Mon, 30 Mar 2026 08:37:25 +0000 (09:37 +0100)] 
Merge pull request #5958 from moisseev/helo-literals

[Fix] Skip HELO IP address literals for SPF domain resolution

10 days ago[Fix] Skip HELO IP address literals for SPF domain resolution 5958/head
Alexander Moisseev [Mon, 30 Mar 2026 06:14:23 +0000 (09:14 +0300)] 
[Fix] Skip HELO IP address literals for SPF domain resolution

Per RFC 7208 §2.3, IP address literals (e.g. [192.0.2.1]) cannot be
used as SPF domains. When MAIL FROM is empty, rspamd falls back to
HELO as the SPF domain.

With an IP literal in HELO, rspamd_spf_resolve() failed (DNS name
rejected by the resolver), but spf_cred was non-NULL, so the result
was incorrectly set to R_SPF_DNSFAIL instead of R_SPF_NA.

Skip building spf_cred from HELO when it starts with '[', so
rspamd_spf_get_cred() returns NULL and the result is R_SPF_NA.

12 days ago[Test] Add unit tests for lua_stat filename_to_tokens 5956/head
Alexander Moisseev [Sat, 28 Mar 2026 17:47:13 +0000 (20:47 +0300)] 
[Test] Add unit tests for lua_stat filename_to_tokens

12 days ago[Feature] Split attachment filenames into sub-tokens for Bayes classifier
Alexander Moisseev [Sat, 28 Mar 2026 17:09:06 +0000 (20:09 +0300)] 
[Feature] Split attachment filenames into sub-tokens for Bayes classifier

Instead of a single whole-filename token, generate a structured set:
- #f:  original filename (case-preserved, backward-compatible)
- #fe: last extension + double extension when present (e.g. #fe:gz, #fe:tar.gz)
- #fp: meaningful parts split by delimiters, CamelCase and letter/digit
    boundaries; purely numeric and single-character parts discarded

Improves generalization for emails with similar attachment names differing
only in dates, IDs or other numeric suffixes, and provides a strong signal
for malware disguise patterns (e.g. pdf.exe, doc.vbs).

12 days ago[Fix] Default map URL path to "/" when no path component is present
Vsevolod Stakhov [Sat, 28 Mar 2026 09:14:15 +0000 (09:14 +0000)] 
[Fix] Default map URL path to "/" when no path component is present

When an HTTP(S) map URL has no path (e.g. "https://maps.rspamd.com"),
the URL parser leaves hdata->path as NULL. This causes a segfault in
write_http_request() which calls strlen() on the NULL path pointer.

Default path to "/" and rest to "" when UF_PATH is not set, matching
standard HTTP semantics.

Fixes: #5955
12 days ago[Fix] Exclude injected parts from SA body/rawbody regexp scanning
Vsevolod Stakhov [Sat, 28 Mar 2026 09:07:19 +0000 (09:07 +0000)] 
[Fix] Exclude injected parts from SA body/rawbody regexp scanning

PDF text extraction injects synthetic text parts that were being scanned
by sa_body and sa_raw_body rules, causing 30x false positive increase on
rules matching null bytes (e.g. /\x00/{sa_raw_body}). PDF hex strings
produce raw bytes including \x00 which are not meaningful in extracted text.

Two fixes:
- Strip null bytes and control characters from extracted PDF text in
  sanitize_pdf_text() for the non-UTF-16 code path
- Skip RSPAMD_MIME_PART_COMPUTED parts in SA body and rawbody scanning
  to follow original SA semantics where only real MIME text parts are matched

13 days ago[CritFix] Stop ev_io watcher in fuzzy UDP session destroy
Vsevolod Stakhov [Fri, 27 Mar 2026 09:10:56 +0000 (09:10 +0000)] 
[CritFix] Stop ev_io watcher in fuzzy UDP session destroy

fuzzy_session_destroy() freed the session without stopping a
potentially active ev_io watcher (pending UDP write retry on EAGAIN).
The watcher remained in libev's anfds[fd].head linked list pointing
to freed memory, corrupting the list and causing fd_reify() to loop
infinitely on the next fd_change — making the worker consume 100% CPU
and stop processing queries entirely.

The TCP counterpart (fuzzy_tcp_session_destroy) already had the
correct ev_io_stop guard. Apply the same pattern for UDP sessions.

2 weeks ago[Fix] Handle OpenAI-compatible response format in Ollama GPT module
Vsevolod Stakhov [Thu, 26 Mar 2026 12:03:57 +0000 (12:03 +0000)] 
[Fix] Handle OpenAI-compatible response format in Ollama GPT module

Ollama's /v1/chat/completions endpoint returns responses in OpenAI
format ({choices: [{message: {content: ...}}]}) rather than native
Ollama format ({message: {content: ...}}). The ollama conversion
functions only handled the native format, failing with "bad message
in reply" for users connecting via the OpenAI-compatible endpoint.

Add ollama_extract_content helper that handles both response formats.

Issue: #5942

2 weeks ago[Fix] Add text attachment fallback in get_displayed_text_part
Vsevolod Stakhov [Thu, 26 Mar 2026 11:51:04 +0000 (11:51 +0000)] 
[Fix] Add text attachment fallback in get_displayed_text_part

When all text parts are classified as attachments (e.g. due to
Content-Type name= parameter), get_displayed_text_part returned nil
causing GPT module to skip with "no text part found". Add text/plain
attachment fallback (< 100KB) similar to existing HTML attachment
fallback.

Also propagate min_words setting consistently through the extraction
pipeline: gpt.lua -> llm_common.build_llm_input -> extract_text_limited
-> get_displayed_text_part, so all three call sites use the same
threshold.

2 weeks agoMerge pull request #5954 from moisseev/dependabot
Vsevolod Stakhov [Thu, 26 Mar 2026 10:29:40 +0000 (10:29 +0000)] 
Merge pull request #5954 from moisseev/dependabot

[Project] Disable all npm dependency updates including security fixes

2 weeks ago[Fix] Clear inherited address family flags when creating new SPF addr nodes
Vsevolod Stakhov [Thu, 26 Mar 2026 10:23:06 +0000 (10:23 +0000)] 
[Fix] Clear inherited address family flags when creating new SPF addr nodes

When spf_record_process_addr creates a new address node for subsequent
DNS replies (MX A/AAAA resolution), it copies the original addr via
memcpy, inheriting all flags including the address family flag from the
first reply. The new reply's flag was OR'd in without clearing the
inherited one, resulting in nodes with both IPV4 and IPV6 flags set.
Since spf_addr_mask_to_string checks IPV4 before IPV6, such nodes would
always display as IPv4, causing duplicate IPv4 addresses in output
instead of the correct IPv4 + IPv6 pair.

Issue: #5951

2 weeks ago[Project] Disable all npm dependency updates including security fixes 5954/head
Alexander Moisseev [Thu, 26 Mar 2026 05:55:56 +0000 (08:55 +0300)] 
[Project] Disable all npm dependency updates including security fixes

2 weeks ago[Fix] Load only the notified scope on per-scope hyperscan notifications
Vsevolod Stakhov [Wed, 25 Mar 2026 21:28:41 +0000 (21:28 +0000)] 
[Fix] Load only the notified scope on per-scope hyperscan notifications

When hs_helper compiles scopes sequentially, it sends a per-scope
HS_LOADED notification after each scope finishes. Workers previously
ignored the scope field and attempted to reload ALL scopes on every
notification, causing spurious "no valid expressions" errors for scopes
that hadn't been compiled yet.

Now workers dispatch per-scope notifications to a new single-scope
loader (rspamd_re_cache_load_hyperscan_single_scope_async), and only
the final empty-scope notification triggers a full reload of all scopes.

2 weeks ago[Fix] Defer settings application for symbols registered after settings init
Vsevolod Stakhov [Wed, 25 Mar 2026 19:48:35 +0000 (19:48 +0000)] 
[Fix] Defer settings application for symbols registered after settings init

When regexp_rules maps (or other dynamic sources) register symbols after
settings have already been processed, the symbols_enabled/symbols_disabled
lists were silently ignored. Store pending settings operations and apply
them when the symbol is later registered via add_symbol_with_callback or
add_virtual_symbol.

2 weeks ago[Fix] Only request recompile on deserialize failure, not cache miss
Vsevolod Stakhov [Wed, 25 Mar 2026 16:18:41 +0000 (16:18 +0000)] 
[Fix] Only request recompile on deserialize failure, not cache miss

Avoid infinite recompile loop: only send RSPAMD_SRV_RECOMPILE_REQUEST
when a cached blob exists but fails to deserialize (stale/corrupt),
not on cache misses where hs_helper cannot compile certain expressions.

2 weeks agoMerge pull request #5952 from rspamd/vstakhov-hyperscan-healing
Vsevolod Stakhov [Wed, 25 Mar 2026 13:05:01 +0000 (13:05 +0000)] 
Merge pull request #5952 from rspamd/vstakhov-hyperscan-healing

[Fix] Self-healing hyperscan cache

2 weeks ago[Fix] Self-healing hyperscan cache: delete stale blobs and trigger recompile 5952/head
Vsevolod Stakhov [Wed, 25 Mar 2026 11:35:58 +0000 (11:35 +0000)] 
[Fix] Self-healing hyperscan cache: delete stale blobs and trigger recompile

When workers fail to deserialize a cached hyperscan blob (version
mismatch, corruption, etc.), the system previously got stuck in PCRE
fallback mode permanently because hs_helper's exists check would pass
for the stale entry and skip recompilation.

Now workers delete bad cache entries immediately on deserialize failure
and send RSPAMD_SRV_RECOMPILE_REQUEST to trigger hs_helper to recompile.
A 5-second debounce in hs_helper prevents thrashing from multiple workers
detecting the same bad blobs simultaneously.

Also removes the dead needs_recompile flag which was set on the worker's
re_class but checked during compilation in hs_helper (different process).

2 weeks agoMerge pull request #5949 from rspamd/vstakhov-jemalloc-conf
Vsevolod Stakhov [Wed, 25 Mar 2026 08:39:40 +0000 (08:39 +0000)] 
Merge pull request #5949 from rspamd/vstakhov-jemalloc-conf

[Minor] Tune jemalloc for single-threaded architecture

2 weeks agoMerge pull request #5948 from rspamd/vstakhov-exim-headers
Vsevolod Stakhov [Wed, 25 Mar 2026 08:39:21 +0000 (08:39 +0000)] 
Merge pull request #5948 from rspamd/vstakhov-exim-headers

[Feature] Expose milter headers in legacy RSPAMC protocol

2 weeks ago[Fix] Backport fixes from libucl
Vsevolod Stakhov [Wed, 25 Mar 2026 08:32:38 +0000 (08:32 +0000)] 
[Fix] Backport fixes from libucl

- ucl_msgpack: add bounds check after switch in ucl_msgpack_parse_ignore
  to prevent heap-buffer-overread when len is modified by fixext types
- ucl_sexp: fix broken length parsing, correct bounds check direction,
  add null pointer checks, fix memory leaks in error paths

2 weeks ago[Feature] Add worker:get_mem_config() Lua method for jemalloc stats 5949/head
Vsevolod Stakhov [Tue, 24 Mar 2026 08:44:35 +0000 (08:44 +0000)] 
[Feature] Add worker:get_mem_config() Lua method for jemalloc stats

Exposes structured jemalloc information via mallctl() as a nested table:
- stats: allocated, active, metadata, resident, mapped
- config: narenas, dirty_decay_ms, muzzy_decay_ms, tcache,
  background_thread, malloc_conf
- version: jemalloc version string
Returns nil when jemalloc is not compiled in.

2 weeks agoMerge pull request #5950 from moisseev/dependabot
Vsevolod Stakhov [Sun, 22 Mar 2026 14:36:53 +0000 (14:36 +0000)] 
Merge pull request #5950 from moisseev/dependabot

[Project] Disable npm dependency version update checks

2 weeks ago[Project] Disable npm dependency version update checks 5950/head
Alexander Moisseev [Sat, 21 Mar 2026 15:01:56 +0000 (18:01 +0300)] 
[Project] Disable npm dependency version update checks

2 weeks ago[Minor] Tune jemalloc for single-threaded multi-process architecture
Vsevolod Stakhov [Sat, 21 Mar 2026 14:37:53 +0000 (14:37 +0000)] 
[Minor] Tune jemalloc for single-threaded multi-process architecture

Set narenas:1 (one arena sufficient without threads), dirty_decay_ms:5000
(return dirty pages faster than default 10s), and muzzy_decay_ms:30000
(hold lazy-release pages for 30s before returning to OS).

2 weeks ago[Fix] Fix use-after-free in pending regexp map and multipattern queues
Vsevolod Stakhov [Sat, 21 Mar 2026 13:53:12 +0000 (13:53 +0000)] 
[Fix] Fix use-after-free in pending regexp map and multipattern queues

When a map reloads while awaiting async hyperscan compilation, the old
re_map/mp is destroyed but rspamd_regexp_map_add_pending (and its
multipattern counterpart) blindly appended a new entry without removing
the stale one. The linear search in find_pending then returned the first
(dangling) pointer, causing a heap-buffer-overflow in rspamd_snprintf
when accessing re_map->re_digest.

Fix by replacing existing entries with the same name in-place instead of
appending duplicates.

2 weeks ago[Minor] Optimize fuzzy_tcp_refresh_timeout
Vsevolod Stakhov [Sat, 21 Mar 2026 13:40:21 +0000 (13:40 +0000)] 
[Minor] Optimize fuzzy_tcp_refresh_timeout

Use ev_timer_again instead of stop/set/start triple, reducing three
libev calls to one. Move timeout refresh out of the write loop so it
is called once after draining the queue rather than on every write().

2 weeks ago[Fix] Fix CPU busy-loop in fuzzy TCP client due to EV_WRITE not being cleared
Vsevolod Stakhov [Sat, 21 Mar 2026 13:36:18 +0000 (13:36 +0000)] 
[Fix] Fix CPU busy-loop in fuzzy TCP client due to EV_WRITE not being cleared

When the TCP write queue drained, EV_WRITE was left armed on the socket
watcher. Since a connected TCP socket is always writable, libev fired
EV_WRITE continuously causing 100% CPU usage. The UDP path correctly
switched to EV_READ only after writing.

- Drop EV_WRITE when write queue empties in fuzzy_tcp_write_handler
- Only arm EV_WRITE on connection established if data is actually queued
- Remove redundant per-IO fuzzy_tcp_check_pending_timeouts call (timer
  callback already handles this), which amplified the spin with O(N)
  hash scans and GHashTable allocations on every iteration

2 weeks agoMerge pull request #5946 from moisseev/autolearnstats
Vsevolod Stakhov [Sat, 21 Mar 2026 13:26:16 +0000 (13:26 +0000)] 
Merge pull request #5946 from moisseev/autolearnstats

[Feature] Add rspamadm autolearnstats subcommand

2 weeks ago[Test] Add functional tests for legacy protocol milter headers 5948/head
Vsevolod Stakhov [Sat, 21 Mar 2026 12:12:10 +0000 (12:12 +0000)] 
[Test] Add functional tests for legacy protocol milter headers

Tests RSPAMC and SPAMC protocol output for:
- X-Milter-Add: lines (add headers)
- X-Milter-Del: lines (remove headers)
- X-Symbol: lines (extended symbol info with options)
- Backward compatibility of existing Symbol: lines

Also fix rspamc()/spamc() test helpers to read full response
instead of truncating at 2048 bytes.

2 weeks ago[Feature] Expose milter headers and extended symbols in legacy RSPAMC/SPAMC protocol
Vsevolod Stakhov [Sat, 21 Mar 2026 11:15:22 +0000 (11:15 +0000)] 
[Feature] Expose milter headers and extended symbols in legacy RSPAMC/SPAMC protocol

Previously, milter add/remove headers and symbol options were only
available via the JSON (HTTP) protocol. Legacy RSPAMC clients like
Exim had no way to access them through $spam_report.

This adds three new line types to the legacy text protocol output:

- X-Milter-Add: Header: value — milter headers to add (with optional
  [N] position bracket for insert-at-position)
- X-Milter-Del: Header — milter headers to remove (with optional [N]
  for specific instance removal)
- X-Symbol: Name(score); description [opt1, opt2] — extended symbol
  info with descriptions and options

Existing Symbol: lines are preserved for backward compatibility.

3 weeks ago[Minor] autolearnstats: fix false-positive learned detection 5946/head
Alexander Moisseev [Thu, 19 Mar 2026 16:42:45 +0000 (19:42 +0300)] 
[Minor] autolearnstats: fix false-positive learned detection

 rspamd_stat_check_autolearn emits error log lines sharing the
 same function prefix as its success messages. Narrow re_confirmed
 to match only success lines, which start with <MSG-ID>: autolearn.

3 weeks ago[Feature] Add rspamadm autolearnstats subcommand
Alexander Moisseev [Thu, 19 Mar 2026 15:59:30 +0000 (18:59 +0300)] 
[Feature] Add rspamadm autolearnstats subcommand

Reports Bayes autolearn events parsed from rspamd log.
Shows autolearn candidates with learning confirmation status,
sender IP, score, and per-verdict statistics.

3 weeks ago[Minor] Bump version to 4.0.0
Vsevolod Stakhov [Wed, 18 Mar 2026 21:23:29 +0000 (21:23 +0000)] 
[Minor] Bump version to 4.0.0

3 weeks ago[Fix] Weighted round-robin not respecting upstream weights across cycles
Vsevolod Stakhov [Wed, 18 Mar 2026 15:00:54 +0000 (15:00 +0000)] 
[Fix] Weighted round-robin not respecting upstream weights across cycles

When all cur_weights reached zero after one complete weighted cycle,
the code fell through to the min_checked path which selects the
least-used upstream regardless of configured weights. This caused
effectively equal distribution (1:1:1) instead of the configured
ratio (e.g. 100:100:1).

Fix: when all cur_weights are exhausted and upstreams have configured
weights, reset all cur_weights simultaneously to restart the weighted
cycle. The min_checked fallback is now only used when all original
weights are truly zero.

3 weeks agoMerge pull request #5914 from rspamd/vstakhov-stat-migrate
Vsevolod Stakhov [Wed, 18 Mar 2026 11:37:59 +0000 (11:37 +0000)] 
Merge pull request #5914 from rspamd/vstakhov-stat-migrate

[Feature] Shard migration and multi-class Bayes in statistics_dump

3 weeks agoMerge pull request #5944 from dragoangel/fix/hs-helper-properly-populate-opts
Vsevolod Stakhov [Wed, 18 Mar 2026 11:37:46 +0000 (11:37 +0000)] 
Merge pull request #5944 from dragoangel/fix/hs-helper-properly-populate-opts

[Fix] Register redis and http as known hs_helper worker options

3 weeks agoMerge pull request #5943 from dragoangel/feat/add-test-dockerfile
Vsevolod Stakhov [Tue, 17 Mar 2026 15:42:47 +0000 (15:42 +0000)] 
Merge pull request #5943 from dragoangel/feat/add-test-dockerfile

feat: add Dockerfile.test to simplify running tests locally

3 weeks agoMerge pull request #5939 from moisseev/mapstats
Vsevolod Stakhov [Tue, 17 Mar 2026 15:42:29 +0000 (15:42 +0000)] 
Merge pull request #5939 from moisseev/mapstats

[Minor] Read from STDIN when no log file is specified

3 weeks agoMerge pull request #5941 from dragoangel/feat/lupa-add-more-features
Vsevolod Stakhov [Tue, 17 Mar 2026 15:42:10 +0000 (15:42 +0000)] 
Merge pull request #5941 from dragoangel/feat/lupa-add-more-features

feat(lupa): Extends the Lupa Jinja template engine

3 weeks ago[Fix] Register redis and http as known hs_helper worker options 5944/head
Dmitriy Alekseev [Tue, 17 Mar 2026 10:43:51 +0000 (11:43 +0100)] 
[Fix] Register redis and http as known hs_helper worker options

The redis and http configuration blocks in the hs_helper worker section
were not registered via rspamd_rcl_register_worker_option, causing
rspamadm configdump to emit "unknown worker attribute: redis" warnings.
The Lua backend reads these blocks at runtime through the full UCL
options object, so they worked correctly despite not being registered.

Add proper RCL registration for both redis and http as ucl_object_t
fields so the config schema recognizes them as valid worker attributes.

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
3 weeks agofeat: add Dockerfile.test to simplify running tests locally 5943/head
Dmitriy Alekseev [Mon, 16 Mar 2026 18:40:41 +0000 (19:40 +0100)] 
feat: add Dockerfile.test to simplify running tests locally

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
3 weeks agofeat(lupa): fix unit tests 5941/head
Dmitriy Alekseev [Mon, 16 Mar 2026 18:20:05 +0000 (19:20 +0100)] 
feat(lupa): fix unit tests

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
3 weeks agoMerge branch 'master' into feat/lupa-add-more-features
Dmitriy Alekseev [Mon, 16 Mar 2026 17:35:19 +0000 (18:35 +0100)] 
Merge branch 'master' into feat/lupa-add-more-features

3 weeks ago[Feature] Make GPT consensus thresholds configurable
Vsevolod Stakhov [Mon, 16 Mar 2026 13:07:07 +0000 (13:07 +0000)] 
[Feature] Make GPT consensus thresholds configurable

Add consensus_spam_threshold (default 0.75) and
consensus_ham_threshold (default 0.25) settings for
single-model setups that need lower thresholds.

3 weeks agoMerge branch 'master' into feat/lupa-add-more-features
Dmitriy Alekseev [Mon, 16 Mar 2026 12:32:35 +0000 (13:32 +0100)] 
Merge branch 'master' into feat/lupa-add-more-features

3 weeks agofeat(lupa): add validation filters, UCL-aware tests, and comprehensive unit tests
Dmitriy Alekseev [Mon, 16 Mar 2026 12:17:07 +0000 (13:17 +0100)] 
feat(lupa): add validation filters, UCL-aware tests, and comprehensive unit tests

New filters:
- mandatory(msg): error if nil or empty
- require_int(msg): error if not a valid integer
- require_number(msg): error if not a valid number
- require_bool(msg): error if not a UCL boolean (true/false/yes/no/on/off/1/0)
- require_duration(msg): parse duration string to seconds, error if invalid
  (supports s, ms, min, m, h, d, w, y)
- require_json(msg): error if not valid JSON/UCL
- require_size(msg): error if not a valid size (number with optional b/Kb/Mb/Gb)
- fromjson: parse JSON/UCL string into Lua table
- tobytes: convert size string to bytes (1Kb=1024, 1Mb=1048576, 1Gb=1073741824)

Modified tests to handle string inputs (env vars are always strings):
- is_number: now returns true for numeric strings like "42" or "3.14"
- is_integer: now returns true for integer strings like "42"
- is_float: now returns true for float strings like "3.14"
- is_true: now checks UCL truthy values (true/yes/on/1, case-insensitive)
- is_false: now checks UCL falsy values (false/no/off/0, case-insensitive)

New tests:
- is_json: check if value is valid JSON/UCL
- is_size: check if value is a valid size string

Added test/lua/unit/lupa.lua with 90+ test cases covering all filters,
tests, env var patterns, and real-world configuration scenarios.

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
3 weeks ago[Feature] Store GPT result in mempool variable
Vsevolod Stakhov [Mon, 16 Mar 2026 12:19:23 +0000 (12:19 +0000)] 
[Feature] Store GPT result in mempool variable

Store full GPT classification result (probability, reason,
categories, model) as JSON in task mempool variable gpt_result
for use by downstream postfilter plugins.

3 weeks ago[Fix] Handle nil sel_part in GPT module, add min_words 5940/head
Vsevolod Stakhov [Mon, 16 Mar 2026 09:01:16 +0000 (09:01 +0000)] 
[Fix] Handle nil sel_part in GPT module, add min_words

- get_displayed_text_part now accepts optional min_words param
- GPT module gets sel_part separately via get_displayed_text_part
- Add min_words setting (default 10) to scan short messages
- Guard redis_cache_key, insert_results and check_llm_cached
  against nil sel_part to prevent crashes

3 weeks agoMerge pull request #5938 from rspamd/vstakhov-lupa-enhancements
Vsevolod Stakhov [Sun, 15 Mar 2026 16:42:33 +0000 (16:42 +0000)] 
Merge pull request #5938 from rspamd/vstakhov-lupa-enhancements

[Feature] Add missing Jinja2 filters and tests to Lupa

3 weeks ago[Minor] Read from STDIN when no log file is specified 5939/head
Alexander Moisseev [Sun, 15 Mar 2026 12:52:01 +0000 (15:52 +0300)] 
[Minor] Read from STDIN when no log file is specified

3 weeks ago[Fix] Add PCRE2 complexity checks before JIT compilation
Vsevolod Stakhov [Sun, 15 Mar 2026 12:26:17 +0000 (12:26 +0000)] 
[Fix] Add PCRE2 complexity checks before JIT compilation

Check compiled pattern size, frame size, and capture count
before calling pcre2_jit_compile to avoid crashes on
pathological patterns. Also set map->map pointer consistently
in lua_config_add_map for all map types.

3 weeks ago[Fix] Fix build with ENABLE_HYPERSCAN=OFF
Vsevolod Stakhov [Sun, 15 Mar 2026 11:48:13 +0000 (11:48 +0000)] 
[Fix] Fix build with ENABLE_HYPERSCAN=OFF

Unset WITH_HYPERSCAN cache variable when hyperscan is disabled,
and guard hs_magic usage in re_cache with WITH_HYPERSCAN ifdef.

3 weeks ago[Fix] Remove unsupported Delta codec from ClickHouse UUID column
Vsevolod Stakhov [Sun, 15 Mar 2026 10:55:31 +0000 (10:55 +0000)] 
[Fix] Remove unsupported Delta codec from ClickHouse UUID column

ClickHouse does not support Delta compression for UUID type (16 bytes),
as Delta codec only works with 1/2/4/8 byte data types.

3 weeks ago[Feature] Add missing Jinja2 filters and tests to Lupa template engine 5938/head
Vsevolod Stakhov [Sun, 15 Mar 2026 10:10:19 +0000 (10:10 +0000)] 
[Feature] Add missing Jinja2 filters and tests to Lupa template engine

Add commonly needed Jinja2 filters: split, trim, tojson, unique, min,
max, items, keys, values, wordwrap. Add tests: is_in, is_startswith,
is_endswith, is_match, is_boolean, is_true, is_false, is_integer,
is_float, and comparison tests (is_eq, is_ne, is_lt, is_le, is_gt,
is_ge).

3 weeks ago[Fix] Fix Clickhouse column name mismatch: UUID -> TaskUUID
Vsevolod Stakhov [Sat, 14 Mar 2026 22:22:28 +0000 (22:22 +0000)] 
[Fix] Fix Clickhouse column name mismatch: UUID -> TaskUUID

The insert field list used 'UUID' but the actual column is named
'TaskUUID' (as defined in schema and migration 9), causing
NO_SUCH_COLUMN_IN_TABLE errors on insert.

3 weeks ago[Feature] Add context_augment hook to GPT module
Vsevolod Stakhov [Sat, 14 Mar 2026 22:13:42 +0000 (22:13 +0000)] 
[Feature] Add context_augment hook to GPT module

Add a new `context_augment` configuration option that accepts Lua code
returning an async function(task, content, callback).  The callback
receives a string that gets injected as additional context into the
LLM prompt alongside existing user/domain and search contexts.

This enables external Lua code to enrich LLM requests with arbitrary
context — e.g., Telegram channel topic and recent messages for
community spam detection.

The augment function runs in parallel with other context fetchers
and supports async operations (Redis, HTTP).

3 weeks ago[Fix] Fix rspamc neural_learn config fetch and output
Vsevolod Stakhov [Sat, 14 Mar 2026 20:31:15 +0000 (20:31 +0000)] 
[Fix] Fix rspamc neural_learn config fetch and output

- Fix config fetch port: when user specifies the default scan port
  (11333), redirect the /plugins/neural/config preflight request to
  the controller port (11334) where the endpoint actually lives.
  Previously, the config fetch used the user-specified port verbatim,
  causing "invalid command" errors on the normal worker.

- Fix misleading output: rspamc_neural_learn_output no longer defaults
  to "success = true" when the response lacks an explicit success field.
  For scan-based learning (checkv2 path), it now detects the scan
  response and shows method = "scan".  For missing/error responses,
  it correctly reports success = false.

- Add user-visible note when the config fetch fails, explaining the
  fallback to scan-based learning.

3 weeks agoMerge pull request #5937 from dragoangel/allow-template-maps-mirror
Vsevolod Stakhov [Sat, 14 Mar 2026 20:19:19 +0000 (20:19 +0000)] 
Merge pull request #5937 from dragoangel/allow-template-maps-mirror

feat: allow utilize own rspamd mirror via env variable

3 weeks agoMerge branch 'master' into allow-template-maps-mirror 5937/head
Dmitriy Alekseev [Sat, 14 Mar 2026 19:44:48 +0000 (20:44 +0100)] 
Merge branch 'master' into allow-template-maps-mirror

3 weeks agofeat: allow utilize own rspamd mirror via env variable
Dmitriy Alekseev [Sat, 14 Mar 2026 19:44:00 +0000 (20:44 +0100)] 
feat: allow utilize own rspamd mirror via env variable

Signed-off-by: Dmitriy Alekseev <1865999+dragoangel@users.noreply.github.com>
3 weeks agoMerge pull request #5935 from rspamd/vstakhov-http-caseless-lua
Vsevolod Stakhov [Fri, 13 Mar 2026 14:17:15 +0000 (14:17 +0000)] 
Merge pull request #5935 from rspamd/vstakhov-http-caseless-lua

[Feature] Add case-insensitive table type for HTTP headers

3 weeks ago[Minor] Fix copyright year to 2026 5935/head
Vsevolod Stakhov [Fri, 13 Mar 2026 14:15:00 +0000 (14:15 +0000)] 
[Minor] Fix copyright year to 2026

3 weeks ago[Test] Add unit tests for caseless table
Vsevolod Stakhov [Fri, 13 Mar 2026 13:58:33 +0000 (13:58 +0000)] 
[Test] Add unit tests for caseless table

25 tests covering creation, case-insensitive lookup, key case
preservation, assignment, deletion, has_key, iteration via each(),
to_table conversion, multi-value get_all, and edge cases including
long keys, empty keys, and metamethod isolation.

3 weeks ago[Feature] Add case-insensitive table type for HTTP headers
Vsevolod Stakhov [Fri, 13 Mar 2026 13:54:35 +0000 (13:54 +0000)] 
[Feature] Add case-insensitive table type for HTTP headers

Introduce rspamd{caseless_table} userdata type that provides
case-insensitive key lookup while preserving original key case.
HTTP response headers now use this type instead of plain Lua tables,
fixing two issues: headers were forcibly lowercased (mutating original
data) and duplicate headers were silently lost.

Multi-value headers are stored as arrays and can be retrieved via
get_all(key). The __index metamethod returns the first value for
convenience. The type is generic and reusable beyond HTTP.

3 weeks ago[Minor] Improve restore logging and add incremental GC 5914/head
Vsevolod Stakhov [Fri, 13 Mar 2026 12:20:09 +0000 (12:20 +0000)] 
[Minor] Improve restore logging and add incremental GC

Add progress reporting every 10 seconds with throughput rate and
Lua memory usage. Run incremental GC after each batch flush and
full GC between files to avoid memory spikes on large restores.

4 weeks agoMerge pull request #5931 from rob4226/fix-dmarc-help
Vsevolod Stakhov [Thu, 12 Mar 2026 20:16:27 +0000 (20:16 +0000)] 
Merge pull request #5931 from rob4226/fix-dmarc-help

[Minor] Fix default date description in rspamadm dmarc_report help