Skip to content

Commit 51268d5

Browse files
committed
Handle too long dates passed to DateTime.parse
Since Ruby 3.0.3, this method raises an ArgumentError with a different message from the invalid date message, as mitigation for CVE-2021-41817. https://www.ruby-lang.org/en/news/2021/11/15/date-parsing-method-regexp-dos-cve-2021-41817/ Example: ArgumentError: string length (150) exceeds the limit 128 In this case we should just return nil, like when the date itself is invalid.
1 parent bf02b67 commit 51268d5

File tree

5 files changed

+51
-25
lines changed

5 files changed

+51
-25
lines changed

lib/mail/elements/received_element.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ def to_s(*args)
2525

2626
private
2727
def datetime_for(received)
28-
::DateTime.parse("#{received.date} #{received.time}")
29-
rescue ArgumentError => e
30-
raise e unless e.message == 'invalid date'
31-
warn "WARNING: Invalid date field for received element (#{received.date} #{received.time}): #{e.class}: #{e.message}"
32-
nil
28+
Utilities.parse_date_time("#{received.date} #{received.time}").tap do |datetime|
29+
unless datetime
30+
warn "WARNING: Invalid date field for received element (#{received.date} #{received.time})"
31+
end
32+
end
3333
end
3434
end
3535
end

lib/mail/fields/common_date_field.rb

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,24 @@
22

33
module Mail
44
class CommonDateField < NamedStructuredField #:nodoc:
5-
def self.singular?
6-
true
7-
end
5+
class << self
6+
def singular?
7+
true
8+
end
89

9-
def self.normalize_datetime(string)
10-
if Utilities.blank?(string)
11-
datetime = ::DateTime.now
12-
else
13-
stripped = string.to_s.gsub(/\(.*?\)/, '').squeeze(' ')
14-
begin
15-
datetime = ::DateTime.parse(stripped)
16-
rescue ArgumentError => e
17-
raise unless 'invalid date' == e.message
10+
def normalize_datetime(string)
11+
if Utilities.blank?(string)
12+
datetime = ::DateTime.now
13+
else
14+
stripped = string.to_s.gsub(/\(.*?\)/, '').squeeze(' ')
15+
datetime = Utilities.parse_date_time(stripped)
1816
end
19-
end
2017

21-
if datetime
22-
datetime.strftime('%a, %d %b %Y %H:%M:%S %z')
23-
else
24-
string
18+
if datetime
19+
datetime.strftime('%a, %d %b %Y %H:%M:%S %z')
20+
else
21+
string
22+
end
2523
end
2624
end
2725

@@ -31,9 +29,7 @@ def initialize(value = nil, charset = nil)
3129

3230
# Returns a date time object of the parsed date
3331
def date_time
34-
::DateTime.parse("#{element.date_string} #{element.time_string}")
35-
rescue ArgumentError => e
36-
raise e unless e.message == 'invalid date'
32+
Utilities.parse_date_time("#{element.date_string} #{element.time_string}")
3733
end
3834

3935
def default

lib/mail/utilities.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,21 @@ def underscoreize( str )
222222
str.to_s.downcase.tr(Constants::HYPHEN, Constants::UNDERSCORE)
223223
end
224224

225+
# Parses a string into a DateTime object, returning nil
226+
# if the string provided is not a valid date or if its
227+
# size exceeds Ruby's default input length limit for
228+
# parsing dates
229+
#
230+
# Example:
231+
#
232+
# DateTime.parse("2022-12-07 20:57:43 +0100") #=> <DateTime: 2022-12-07T20:57:43+01:00 ((2459921j,71863s,0n),+3600s,2299161j)>
233+
# DateTime.parse("invalid") #=> nil
234+
def parse_date_time(string)
235+
::DateTime.parse(string)
236+
rescue ArgumentError => e
237+
raise unless e.message =~ /\A(invalid date|string length \(\d+\) exceeds the limit \d+)\z/
238+
end
239+
225240
def map_lines( str, &block )
226241
str.each_line.map(&block)
227242
end

spec/mail/fields/date_field_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,11 @@
6666
field = Mail::DateField.new("12 Aug 2009 30:00:02 GMT")
6767
expect(field.date_time).to be_nil
6868
end
69+
70+
it "should handle too long invalid date" do
71+
# field = Mail::DateField.new("12 Aug 2009 30:00:02 GMT")
72+
field = Mail::DateField.new("Wed, 23 Jan 2019 30:51:32 -0500")
73+
expect(field.date_time).to be_nil
74+
end
6975
end
7076
end

spec/mail/fields/received_field_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,13 @@
5555
expect(t.date_time).to eq nil
5656
expect(t.formatted_date).to eq nil
5757
end
58+
59+
it "should handle too long invalid date" do
60+
t = Mail::ReceivedField.new("mail.example.com (192.168.1.1) by mail.example.com with (esmtp) id (qid) for <[email protected]>; Mon, (envelope-from <[email protected] ([email protected])>) 29 Jul 2013 23:12:46 +0900")
61+
62+
expect(t.name).to eq "Received"
63+
expect(t.info).to eq "mail.example.com (192.168.1.1) by mail.example.com with (esmtp) id (qid) for <[email protected]>"
64+
expect(t.date_time).to eq nil
65+
expect(t.formatted_date).to eq nil
66+
end
5867
end

0 commit comments

Comments
 (0)