@@ -234,7 +234,7 @@ impl Readable for LSPSRequestId {
234234}
235235
236236/// An object representing datetimes as described in bLIP-50 / LSPS0.
237- #[ derive( Clone , Debug , Copy , PartialEq , Eq , Hash , Deserialize , Serialize ) ]
237+ #[ derive( Clone , Debug , Copy , PartialEq , Eq , Hash , Serialize ) ]
238238#[ serde( transparent) ]
239239pub struct LSPSDateTime ( pub chrono:: DateTime < chrono:: Utc > ) ;
240240
@@ -275,8 +275,23 @@ impl LSPSDateTime {
275275impl FromStr for LSPSDateTime {
276276 type Err = ( ) ;
277277 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
278- let datetime = chrono:: DateTime :: parse_from_rfc3339 ( s) . map_err ( |_| ( ) ) ?;
279- Ok ( Self ( datetime. into ( ) ) )
278+ let datetime: chrono:: DateTime < chrono:: Utc > =
279+ chrono:: DateTime :: parse_from_rfc3339 ( s) . map_err ( |_| ( ) ) ?. into ( ) ;
280+ // Reject pre-epoch datetimes here so peer-controlled `valid_until` /
281+ // `expires_at` fields can never produce an `LSPSDateTime` with a negative
282+ // UNIX timestamp, which would otherwise panic the `i64 -> u64` cast in
283+ // `is_past`.
284+ if datetime. timestamp ( ) < 0 {
285+ return Err ( ( ) ) ;
286+ }
287+ Ok ( Self ( datetime) )
288+ }
289+ }
290+
291+ impl < ' de > Deserialize < ' de > for LSPSDateTime {
292+ fn deserialize < D : Deserializer < ' de > > ( deserializer : D ) -> Result < Self , D :: Error > {
293+ let s = String :: deserialize ( deserializer) ?;
294+ Self :: from_str ( & s) . map_err ( |( ) | de:: Error :: custom ( "invalid LSPSDateTime" ) )
280295 }
281296}
282297
@@ -996,4 +1011,15 @@ mod tests {
9961011 assert_eq ! ( later. duration_since( & earlier) , Duration :: from_secs( 60 ) ) ;
9971012 assert_eq ! ( earlier. duration_since( & later) , Duration :: ZERO ) ;
9981013 }
1014+
1015+ #[ test]
1016+ fn is_past_handles_pre_epoch_datetime ( ) {
1017+ // A peer-controlled RFC3339 datetime before 1970 must be rejected at parse
1018+ // time, so it can never reach `is_past` (or any other consumer) and panic.
1019+ assert ! ( LSPSDateTime :: from_str( "1900-01-01T00:00:00Z" ) . is_err( ) ) ;
1020+
1021+ // JSON deserialization (the path peer messages take) must reject it too.
1022+ let json = "\" 1900-01-01T00:00:00Z\" " ;
1023+ assert ! ( serde_json:: from_str:: <LSPSDateTime >( json) . is_err( ) ) ;
1024+ }
9991025}
0 commit comments