From 84c2d37a4986c2e842a1b549779cd3ced5feffae Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:57:06 +0100 Subject: [PATCH] Cap list label length when expanding level template text (#4003) Fixes #3638 Co-authored-by: Claude Haiku 5.5 --- crates/doc/src/numbering.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/doc/src/numbering.rs b/crates/doc/src/numbering.rs index d4323cddd..30e7acfcc 100644 --- a/crates/doc/src/numbering.rs +++ b/crates/doc/src/numbering.rs @@ -357,7 +357,8 @@ impl Counters { return Some((def.text.clone(), def)); } let mut label = String::new(); - let mut chars = def.text.chars().peekable(); + // Word's level text is short; .docx and the lists command already cap it at 64. + let mut chars = def.text.chars().take(MAX_LEVEL_TEXT).peekable(); while let Some(c) = chars.next() { if c == '%' && let Some(d) = chars.peek().and_then(|d| d.to_digit(10)) @@ -375,6 +376,9 @@ impl Counters { f => f, }; label.push_str(&fmt.format(val)); + if label.len() > MAX_LABEL_BYTES { + break; + } } else { label.push(c); } @@ -383,10 +387,29 @@ impl Counters { } } +/// Longest level text (in characters) expanded into a list label. +const MAX_LEVEL_TEXT: usize = 64; +/// Labels stop growing once they pass this many bytes. +const MAX_LABEL_BYTES: usize = 256; + #[cfg(test)] mod tests { use super::*; + #[test] + fn a_huge_level_text_gives_a_bounded_label() { + let mut n = Numbering::default(); + let id = n.add_list(ListKind::Numbered); + let huge = Level { format: NumFormat::UpperRoman, text: "%1".repeat(30_000), start: u32::MAX, ..Level::default() }; + if let Some(num) = n.nums.iter_mut().find(|x| x.id == id) { + num.level_overrides = vec![(0, huge)]; + } + let mut c = Counters::default(); + let label = c.next_label(&n, id, 0).unwrap().0; + assert!(label.len() <= 512, "label is {} bytes", label.len()); + assert!(label.starts_with("MMMM")); + } + #[test] fn a_list_s_own_level_definitions_win() { // A specification's article numbers: level 1 counts parts with no label of its own, level 2