From 74ac7bc27f56d2631592877c683b76e8dd951afd Mon Sep 17 00:00:00 2001 From: Jacob Marble Date: Fri, 3 Jul 2026 00:16:12 +0000 Subject: [PATCH] json: require RFC3339 'T' separator in date-time format validator Since time 0.3.37 (time-rs/time#700), the crate's RFC3339 parser accepts any byte as the date/time separator. JSON Schema's date-time format is defined by the RFC3339 ABNF rule, which permits only 'T' (case-insensitive), so check the separator explicitly before delegating to the time crate. Fixes #3108 --- crates/json/src/schema/formats.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/json/src/schema/formats.rs b/crates/json/src/schema/formats.rs index ed0baaf03e7..40ff94eec6c 100644 --- a/crates/json/src/schema/formats.rs +++ b/crates/json/src/schema/formats.rs @@ -86,6 +86,13 @@ impl Format { .is_ok() } Self::DateTime => { + // Since 0.3.37, `time`'s RFC3339 parser accepts any byte as the + // date/time separator (time-rs/time#700). JSON Schema's `date-time` + // is the RFC3339 ABNF rule, which permits only `T` (case-insensitive), + // so check the separator ourselves. + if !matches!(val.as_bytes().get(10), Some(b'T' | b't')) { + return false; + } time::OffsetDateTime::parse(val, &time::format_description::well_known::Rfc3339) .is_ok() } @@ -214,6 +221,10 @@ mod test { ("date-time", "2022-09-11T10:31:25+00:00", true), ("date-time", "2022-09-11T10:31:25-00:00", true), ("datetime", "2022-09-11T10:31:25.123Z", true), // Accepted alias. + ("date-time", "2022-09-11 10:31:25.123Z", false), // Space separator (regression: time-rs/time#700). + ("date-time", "2022-09-11x10:31:25.123Z", false), // Arbitrary separator. + ("date-time", "2022-09-11t10:31:25.123Z", true), // Lowercase `t` is valid per RFC3339 ABNF. + ("date-time", "2022-09-11", false), // Date only, no separator position. ("date-time", "10:31:25.123Z", false), ("time", "10:31:25.123Z", true), ("time", "10:31:25.123z", true),