From b1c974675ee561ce38b9cc4c194f45799c339e3d Mon Sep 17 00:00:00 2001 From: Olaf Alders Date: Thu, 25 Jun 2026 14:36:33 +0000 Subject: [PATCH] Reject malformed ISO 8601 timezones with a doubled colon The ISO 8601 timezone sub-pattern used a capturing group `(:?\d\d)` where a non-capturing group `(?:\d\d)` was intended. The transposed `:?`/`?:` let an extra colon slip through, so parse_date() accepted malformed offsets like `-01::00` and returned them verbatim as the timezone. Use a non-capturing group so the colon separator is matched only by the outer `:?`. Well-formed offsets (-01:00, -0100, -01) are unaffected. Closes #7 Co-Authored-By: Claude Opus 4.8 --- lib/HTTP/Date.pm | 2 +- t/date.t | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/HTTP/Date.pm b/lib/HTTP/Date.pm index a33a2de..be4ea5c 100644 --- a/lib/HTTP/Date.pm +++ b/lib/HTTP/Date.pm @@ -171,7 +171,7 @@ sub parse_date ($) { (?::?([0-9][0-9](?:\.[0-9]*)?))? # optional seconds (and fractional) )? # optional clock \s* - ([-+]?[0-9][0-9]?:?(:?[0-9][0-9])? + ([-+]?[0-9][0-9]?:?(?:[0-9][0-9])? |Z|z)? # timezone (Z is "zero meridian", i.e. GMT) \s*$ /x diff --git a/t/date.t b/t/date.t index 03181c5..3ae4e69 100644 --- a/t/date.t +++ b/t/date.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 152; +use Test::More tests => 156; use HTTP::Date; # test str2time for supported dates. Test cases with 2 digit year @@ -198,6 +198,17 @@ use HTTP::Date qw(parse_date); my @d = parse_date("Jan 1 2001"); is_deeply( \@d, [2001, 1, 1, 0, 0, 0, undef] ); +# A malformed timezone with a doubled colon must be rejected, not silently +# captured as the timezone. RT #101378 / GH #7. +is_deeply( [ parse_date("1996-02-29 12:00:00 -01::00") ], + [], 'malformed timezone -01::00 is rejected' ); + +# Well-formed timezone variants must still parse, returning the timezone intact. +for my $tz ('-01:00', '-0100', '-01') { + my @d = parse_date("1996-02-29 12:00:00 $tz"); + is( $d[6], $tz, "timezone $tz still accepted" ); +} + # This test will break around year 2070 is( parse_date("03-Feb-20"), "2020-02-03 00:00:00" );