Sieve string test with a key list only matches the last key (sieve-rs 0.7)

Issue Description

A Sieve string test (and environment, which shares the code) with more than one key returns the result of the last key only. Earlier keys that match are ignored.

In sieve-rs 0.7.3, src/runtime/tests/test_string.rs (around lines 62–95) loops over the keys on the outside and the sources on the inside. On a match, break leaves only the inner loop over sources. The outer loop then moves on to the next key and overwrites result, so a match on any key but the last is lost.

This is already fixed in sieve-rs 1.0.0, which uses break 'outer. Stalwart still depends on sieve-rs = “0.7” in v0.16.24 and on main, so every current release has the bug. header, address and envelope tests return on the first matching key and are not affected.

Expected Behavior

Per RFC 5228 §2.7.1, a test with a list of keys is true if any key matches, so string :is “b” [“a”, “b”, “c”] is true.

Actual Behavior

It is false. Only string :is “c” [“a”, “b”, “c”], the last key, is true. The same applies to :matches, :contains, :regex and :value. The scripts compile and validate, and nothing is logged, so the failure is silent.

Workaround: one single-key test per value (if … elsif …).

Reproduction Steps

  1. Any trusted or user Sieve script:
require ["variables"];
set "v" "b";
if string :is "${v}" ["a", "b", "c"] {
    addheader "X-Test" "matched";
}
  1. Deliver a message through it.

I hit this in a data-stage system script whose test was string :is “${folder}” [“receipts”, “shipping”, “newsletters”]. Only newsletters ever matched.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

  • Seen on 0.16.22; v0.16.24 pins the same sieve-rs = “0.7”.
  • Fix: the one-line change from sieve-rs 1.0 (break 'outer), released as a 0.7.x patch, or a move to sieve-rs 1.x.

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

on

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

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

This confirms the current server code still pins sieve-rs = “0.7” across all crates (jmap, email, common, managesieve, smtp, tests), consistent with what you found. This is a legitimate bug report rather than a documentation question, so I can’t verify a fix against docs, but your analysis and reproduction are clear and well-documented.

Thanks for the detailed writeup, including the exact file/line reference in sieve-rs and the minimal reproduction script. This is a code-level bug in a pinned dependency version, not something addressable via configuration, so there’s no workaround beyond the if/elsif restructuring you already identified. A human maintainer will need to review and decide whether to patch the vendored dependency or bump to sieve-rs 1.x.

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

This is fixed in v1.0.0. The fix can’t be backported to v0.16, but v1.0.0 will be released in late October or early November.