From 9209909ff78da930fa8d934315f3c13460379bbb Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Tue, 13 Oct 2026 17:48:15 +0200 Subject: [PATCH] Count the partial quartet in the base64 decoded-size cap (#468) --- crates/model/src/b64.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/model/src/b64.rs b/crates/model/src/b64.rs index 8288c466..1435d313 100644 --- a/crates/model/src/b64.rs +++ b/crates/model/src/b64.rs @@ -37,15 +37,19 @@ fn value(c: u8) -> Option { /// Decodes padded (or unpadded) standard base64. `None` for malformed input or a result larger /// than [`MAX_DECODED_BYTES`]. pub fn decode(text: &str) -> Option> { + if text.bytes().filter(|c| !c.is_ascii_whitespace()).count() > MAX_DECODED_BYTES.div_ceil(3).saturating_mul(4) { + return None; + } let syms: Vec = text.bytes().filter(|c| !c.is_ascii_whitespace()).collect(); let body = syms.strip_suffix(b"==").or_else(|| syms.strip_suffix(b"=")).unwrap_or(&syms); if body.contains(&b'=') || (syms.len() != body.len() && !syms.len().is_multiple_of(4)) || body.len() % 4 == 1 { return None; } - if body.len() / 4 * 3 > MAX_DECODED_BYTES { + let decoded_len = body.len() / 4 * 3 + body.len() % 4 * 3 / 4; + if decoded_len > MAX_DECODED_BYTES { return None; } - let mut out = Vec::with_capacity(body.len() / 4 * 3 + 2); + let mut out = Vec::with_capacity(decoded_len); for chunk in body.chunks(4) { let mut n = 0u32; for (i, &c) in chunk.iter().enumerate() { @@ -81,4 +85,17 @@ mod tests { assert!(decode(bad).is_none(), "{bad}"); } } + + #[test] + fn size_cap_counts_the_partial_quartet() { + for (extra, ok) in [(-1, true), (0, true), (1, false), (2, false)] { + let data = vec![7u8; MAX_DECODED_BYTES.saturating_add_signed(extra)]; + let padded = encode(&data); + let unpadded = padded.trim_end_matches('='); + assert_eq!(decode(&padded).is_some(), ok, "padded, {extra}"); + assert_eq!(decode(unpadded).is_some(), ok, "unpadded, {extra}"); + let spaced = format!("{}\n {}", &padded[..8], &padded[8..]); + assert_eq!(decode(&spaced).is_some(), ok, "whitespace, {extra}"); + } + } }