From b0fcb297ca3e38c56409996541a09314e7c78317 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Mon, 26 Oct 2026 04:28:46 +0000 Subject: [PATCH 1/2] Reject signed or out-of-range timezone offsets in parse_iso_date Found while testing crates/doc/src/vars.rs:~186 (parse_iso_date, the zone '+'/'-' branch: `z.get(0..2)?.parse::()` and the minutes parse) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/doc/src/vars.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/doc/src/vars.rs b/crates/doc/src/vars.rs index c53eedb1a..f31a31002 100644 --- a/crates/doc/src/vars.rs +++ b/crates/doc/src/vars.rs @@ -195,8 +195,12 @@ pub fn parse_iso_date(s: &str) -> Option { None | Some('Z') => Some(secs), Some(sign @ ('+' | '-')) => { let z = zone.get(1..)?; - let zh: i64 = z.get(0..2)?.parse().ok()?; - let zm: i64 = z.get(2..).map(|r| r.trim_start_matches(':')).filter(|r| !r.is_empty()).map_or(Some(0), |r| r.parse().ok())?; + let digits = |t: &str| -> Option { t.bytes().all(|c| c.is_ascii_digit()).then(|| t.parse().ok()).flatten() }; + let zh = digits(z.get(0..2)?)?; + let zm = z.get(2..).map(|r| r.trim_start_matches(':')).filter(|r| !r.is_empty()).map_or(Some(0), digits)?; + if zh > 23 || zm > 59 { + return None; + } let off = zh * 3600 + zm * 60; Some(if sign == '+' { secs - off } else { secs + off }) } @@ -336,6 +340,13 @@ mod tests { } } + #[test] + fn iso_date_rejects_malformed_zone() { + for bad in ["2019-12-16T09:48:02+-5:00", "2019-12-16T09:48:02+05:+30", "2019-12-16T09:48:02+24:00", "2019-12-16T09:48:02+05:60"] { + assert_eq!(parse_iso_date(bad), None, "{bad}"); + } + } + #[test] fn chars() { assert_eq!(var_index(var_char(3).unwrap()), Some(3)); From 6df8d6d93407877a4ce15619d78bea93ddbe480f Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Tue, 3 Nov 2026 19:03:59 +0000 Subject: [PATCH 2/2] Apply review fixes - crates/doc/src/vars.rs --- crates/doc/src/vars.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/doc/src/vars.rs b/crates/doc/src/vars.rs index f31a31002..9183a20e0 100644 --- a/crates/doc/src/vars.rs +++ b/crates/doc/src/vars.rs @@ -197,7 +197,11 @@ pub fn parse_iso_date(s: &str) -> Option { let z = zone.get(1..)?; let digits = |t: &str| -> Option { t.bytes().all(|c| c.is_ascii_digit()).then(|| t.parse().ok()).flatten() }; let zh = digits(z.get(0..2)?)?; - let zm = z.get(2..).map(|r| r.trim_start_matches(':')).filter(|r| !r.is_empty()).map_or(Some(0), digits)?; + let zm = match z.get(2..)? { + "" => 0, + r if r.len() == 2 => digits(r)?, + r => digits(r.strip_prefix(':').filter(|m| m.len() == 2)?)?, + }; if zh > 23 || zm > 59 { return None; } @@ -342,7 +346,16 @@ mod tests { #[test] fn iso_date_rejects_malformed_zone() { - for bad in ["2019-12-16T09:48:02+-5:00", "2019-12-16T09:48:02+05:+30", "2019-12-16T09:48:02+24:00", "2019-12-16T09:48:02+05:60"] { + for bad in [ + "2019-12-16T09:48:02+-5:00", + "2019-12-16T09:48:02+05:+30", + "2019-12-16T09:48:02+24:00", + "2019-12-16T09:48:02+05:60", + "2019-12-16T09:48:02+05::30", + "2019-12-16T09:48:02+05:", + "2019-12-16T09:48:02+053", + "2019-12-16T09:48:02+05:3", + ] { assert_eq!(parse_iso_date(bad), None, "{bad}"); } }