From 5da2393e28c4504bc7416c94c3afd2dafccf6e14 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 8 Nov 2026 18:14:57 +0100 Subject: [PATCH] Reject signed row numbers in XLSX cell references Found while testing crates/textimport/src/xlsx.rs:15 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/textimport/src/xlsx.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/textimport/src/xlsx.rs b/crates/textimport/src/xlsx.rs index 2356f9e0b..8ad1bb004 100644 --- a/crates/textimport/src/xlsx.rs +++ b/crates/textimport/src/xlsx.rs @@ -12,7 +12,11 @@ use crate::{ImportError, Imported}; /// "B12" → (row 11, column 1). fn cell_ref(r: &str) -> Option<(usize, usize)> { let letters: String = r.chars().take_while(char::is_ascii_alphabetic).collect(); - let row: usize = r[letters.len()..].parse().ok()?; + let digits = &r[letters.len()..]; + if !digits.bytes().all(|b| b.is_ascii_digit()) { + return None; + } + let row: usize = digits.parse().ok()?; let col = letters.chars().try_fold(0usize, |acc, c| { let digit = (c.to_ascii_uppercase() as u8).checked_sub(b'A')? as usize + 1; acc.checked_mul(26)?.checked_add(digit) @@ -331,6 +335,15 @@ mod tests { buf.into_inner() } + #[test] + fn cell_ref_rejects_signed_or_malformed_rows() { + assert_eq!(cell_ref("B12"), Some((11, 1))); + assert_eq!(cell_ref("A+1"), None); + assert_eq!(cell_ref("A-1"), None); + assert_eq!(cell_ref("A"), None); + assert_eq!(cell_ref("A1x"), None); + } + /// A `t="s"` cell materialises its own copy of the shared string, so one big string with many /// references multiplies: the audit's case is a 1 MB string and 3M cells, terabytes, built /// before the grid cap applies (#1584). Collection now carries its own byte budget.