From 2d2123d2512b72e21cd71fd1d9be48969050e50c Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:32:06 +0200 Subject: [PATCH] Reject signs and non-digits in parse_date fields (#3575) Found while testing crates/engine/src/cmd/controls.rs:113-123 (parse_date) Co-authored-by: Claude Opus 5.5 --- crates/engine/src/cmd/controls.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/cmd/controls.rs b/crates/engine/src/cmd/controls.rs index 24b2788cce..6ad46d76d6 100644 --- a/crates/engine/src/cmd/controls.rs +++ b/crates/engine/src/cmd/controls.rs @@ -112,9 +112,11 @@ fn symbol_param(v: &Value, k: &str) -> Option { /// A date `YYYY-MM-DD` (a full ISO date-time is fine too): (year, month, day), checked. pub fn parse_date(t: &str) -> Option<(i64, u32, u32)> { let t = t.trim(); - let y: i64 = t.get(0..4)?.parse().ok()?; - let m: u32 = t.get(5..7)?.parse().ok()?; - let d: u32 = t.get(8..10)?.parse().ok()?; + // Digits only: `str::parse` would also accept a leading '+'. + let field = |r: std::ops::Range| t.get(r).filter(|s| s.bytes().all(|b| b.is_ascii_digit())); + let y: i64 = field(0..4)?.parse().ok()?; + let m: u32 = field(5..7)?.parse().ok()?; + let d: u32 = field(8..10)?.parse().ok()?; if t.get(4..5) != Some("-") || t.get(7..8) != Some("-") || !(1..=9999).contains(&y) || !(1..=12).contains(&m) || d == 0 || d > days_in_month(y, m) { return None; @@ -761,6 +763,15 @@ mod tests { assert_eq!(super::super::civil_from_days(days_from_civil(2000, 3, 1)), (2000, 3, 1)); } + #[test] + fn parse_date_rejects_signs_and_non_digits() { + for bad in ["2024-+1-+5", "+123-01-01", "2024-01-+5", "2024-+1-05", "-024-01-01"] { + assert_eq!(parse_date(bad), None, "{bad}"); + } + assert_eq!(parse_date("2024-01-05"), Some((2024, 1, 5))); + assert_eq!(parse_date("2024-01-05T10:00:00"), Some((2024, 1, 5))); + } + #[test] fn days_from_civil_and_days_in_month_exact_beyond_chronos_range() { // Past `NaiveDate`'s ±262143 the 400-year cycles carry the result; values from the