diff --git a/crates/doc/src/vars.rs b/crates/doc/src/vars.rs index 4cbf73605..1f5fd7456 100644 --- a/crates/doc/src/vars.rs +++ b/crates/doc/src/vars.rs @@ -198,8 +198,16 @@ 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 = 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; + } let off = zh * 3600 + zm * 60; Some(if sign == '+' { secs - off } else { secs + off }) } @@ -345,6 +353,22 @@ 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", + "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}"); + } + } + #[test] fn chars() { assert_eq!(var_index(var_char(3).unwrap()), Some(3));