diff --git a/CHANGELOG.md b/CHANGELOG.md index 8644aa3e..cc5a153e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,9 +26,9 @@ If you are upgrading from v0.11.x or v0.12.x, this version includes **breaking c - IMAP: Include `administer` permission in ACL responses. - IMAP: Add owner rights to ACL get responses. - IMAP: Do not auto-train Bayes when moving messages from Junk to Trash. -- IMAP/ManageSieve: Increase maximum quoted argument size (fixes #2039). -- CalDAV: Limit recurrence expansions in calendar reports. -- WebDAV: Do not fix percent encoding on WebDAV FS (closes #2036). +- IMAP/ManageSieve: Increase maximum quoted argument size (#2039). +- CalDAV: Limit recurrence expansions in calendar reports ([CVE-2025-59045](https://github.com/stalwartlabs/stalwart/security/advisories/GHSA-xv4r-q6gr-6pfg)). +- WebDAV: Do not fix percent encoding on WebDAV FS (#2036). ## [0.13.2] - 2025-07-28 diff --git a/crates/imap-proto/src/receiver.rs b/crates/imap-proto/src/receiver.rs index e8632b78..4d543d40 100644 --- a/crates/imap-proto/src/receiver.rs +++ b/crates/imap-proto/src/receiver.rs @@ -4,11 +4,11 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +use super::{ResponseCode, ResponseType}; +use compact_str::{CompactString, format_compact}; use std::fmt::Display; -use compact_str::{CompactString, format_compact}; - -use super::{ResponseCode, ResponseType}; +const QUOTED_ARG_MAX_LEN: usize = 4096; #[derive(Debug, Clone)] pub enum Error { @@ -277,7 +277,7 @@ impl Receiver { if !escaped { self.push_argument(true)?; self.state = State::Argument { last_ch: b' ' }; - } else if self.buf.len() < 4096 { + } else if self.buf.len() < QUOTED_ARG_MAX_LEN { self.buf.push(ch); self.state = State::ArgumentQuoted { escaped: false }; } else { @@ -286,7 +286,11 @@ impl Receiver { } b'\\' => { if escaped { - self.buf.push(ch); + if self.buf.len() < QUOTED_ARG_MAX_LEN { + self.buf.push(ch); + } else { + return Err(self.error_reset("Quoted argument too long.")); + } } self.state = State::ArgumentQuoted { escaped: !escaped }; } @@ -294,7 +298,7 @@ impl Receiver { return Err(self.error_reset("Unterminated quoted argument.")); } _ => { - if self.buf.len() < 4096 { + if self.buf.len() < QUOTED_ARG_MAX_LEN { if escaped { self.buf.push(b'\\'); } @@ -340,7 +344,6 @@ impl Receiver { self.buf.push(ch); } else { // Digit found after non-sync '+' flag - return Err(self.error_reset("Invalid literal.")); } } diff --git a/tests/src/smtp/inbound/limits.rs b/tests/src/smtp/inbound/limits.rs index 3d0f3e08..9012d4c6 100644 --- a/tests/src/smtp/inbound/limits.rs +++ b/tests/src/smtp/inbound/limits.rs @@ -40,7 +40,7 @@ async fn limits() { // Exceed max line length let mut session = Session::test_with_shutdown(TestSMTP::from_core(core).server, rx); session.data.remote_ip_str = "10.0.0.1".into(); - let mut buf = vec![b'A'; 2049]; + let mut buf = vec![b'A'; 4097]; session.ingest(&buf).await.unwrap(); session.ingest(b"\r\n").await.unwrap(); session.response().assert_code("554 5.3.4");