From cddc9cbc3fb760b4659f7ae3d605ef79a093a66b Mon Sep 17 00:00:00 2001 From: DlubalG Date: Thu, 3 Sep 2026 00:36:09 +0200 Subject: [PATCH] test: address parser limit review feedback --- src/formats/ppt/mod.rs | 17 +++++++++-------- src/formats/rtf/mod.rs | 2 +- src/package/limits.rs | 12 +++++++----- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/formats/ppt/mod.rs b/src/formats/ppt/mod.rs index 0bf14ba2..88b2ca82 100644 --- a/src/formats/ppt/mod.rs +++ b/src/formats/ppt/mod.rs @@ -660,14 +660,15 @@ mod tests { /// `depth` NotesContainers (0x03F0), each holding the next. No NotesAtom /// child, so none of them reads as the notes master. fn nested_notes(depth: usize) -> Vec { - let mut buf: Vec = Vec::new(); - for _ in 0..depth { - let mut outer = Vec::with_capacity(buf.len() + 8); - outer.extend_from_slice(&0x000Fu16.to_le_bytes()); // container - outer.extend_from_slice(&0x03F0u16.to_le_bytes()); // NotesContainer - outer.extend_from_slice(&(buf.len() as u32).to_le_bytes()); - outer.extend_from_slice(&buf); - buf = outer; + let stream_len = depth.checked_mul(8).expect("nested record stream length overflow"); + let mut buf = vec![0; stream_len]; + for (index, header) in buf.chunks_exact_mut(8).enumerate() { + header[0..2].copy_from_slice(&0x000Fu16.to_le_bytes()); // container + header[2..4].copy_from_slice(&0x03F0u16.to_le_bytes()); // NotesContainer + let inner_len = stream_len - (index + 1) * 8; + header[4..8].copy_from_slice( + &u32::try_from(inner_len).expect("nested record stream exceeds u32").to_le_bytes(), + ); } buf } diff --git a/src/formats/rtf/mod.rs b/src/formats/rtf/mod.rs index 8682d649..6b8af78e 100644 --- a/src/formats/rtf/mod.rs +++ b/src/formats/rtf/mod.rs @@ -1366,7 +1366,7 @@ mod tests { /// dropping the tree. #[test] fn deep_math_nesting_stays_within_the_xml_depth_bound() { - let mut src = String::from(r"{\rtf1\ansi {\*\mmath "); + let mut src = String::from(r"{\rtf1\ansi {\mmath "); // Stay under the group bound so this exercises the math cap alone. let groups = limits::MAX_XML_DEPTH * 2; src.push_str(&"{".repeat(groups)); diff --git a/src/package/limits.rs b/src/package/limits.rs index 802b7aed..1c360d63 100644 --- a/src/package/limits.rs +++ b/src/package/limits.rs @@ -1,10 +1,12 @@ -//! Fixed safety limits, applied identically to every conversion. +//! Fixed safety limits for document conversion. //! //! These are hard caps against attack/abuse input shapes (decompression -//! bombs, pathological nesting, runaway expansion) - crossing one returns -//! [`ConvertError::ResourceLimit`](crate::ConvertError::ResourceLimit), -//! always. They are deliberately not configurable: real-world documents sit -//! orders of magnitude below every value here. +//! bombs, pathological nesting, runaway expansion). Most limits return +//! [`ConvertError::ResourceLimit`](crate::ConvertError::ResourceLimit) when +//! crossed; limits whose comments explicitly describe graceful degradation +//! instead retain partial/default output. They are deliberately not +//! configurable: real-world documents sit orders of magnitude below every +//! value here. /// Maximum decompressed size of a single archive entry: 128 MiB. pub const MAX_ENTRY_BYTES: u64 = 128 * 1024 * 1024;