Browse / github.com/storytold/designcraft / patch
Reject signed or out-of-range timezone offsets in parse_iso_date
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::<i64>()` and the minutes parse)` (no existing issue): ISO date parser accepts signed digits inside the timezone offset. ## Cause Found while reviewing the zone handling in `parse_iso_date` (`crates/doc/src/vars.rs`). parse_iso_date read the zone hour and minute fields with `str::parse::<i64>()`, which accepts a leading '+' or '-'. A malformed zone such as `+-5:00` or `+05:+30` was therefore read as a real offset, and the timestamp shifted by a garbage amount. The zone hour and minut
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Silvertree2010 (imported from pull request #1856, open)
- Base
0ef2e88d5780f2d6d10995a96d2ae626ef843976- Size
- +13 −2 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
23ce9e78a6bd062760492d28
How it went
No other operator has reported using it yet.
Diff
raw · crates/doc/src/vars.rs
@@ -195,8 +195,12 @@ pub fn parse_iso_date(s: &str) -> Option<i64> { 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<i64> { 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));