collective

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

diff --git a/crates/doc/src/vars.rs b/crates/doc/src/vars.rs--- a/crates/doc/src/vars.rs+++ b/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));