From e4c117749ba24a66f8ec5a08eddf68deeb425ccd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Feb 2024 11:05:06 -0800 Subject: [PATCH 1/4] Fixing ReDoS in header parsing Thanks svalkanov [CVE-2024-26146] --- lib/rack/utils.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index c8e61ea18..0ed64b7a5 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -142,8 +142,8 @@ def build_nested_query(value, prefix = nil) end def q_values(q_value_header) - q_value_header.to_s.split(/\s*,\s*/).map do |part| - value, parameters = part.split(/\s*;\s*/, 2) + q_value_header.to_s.split(',').map do |part| + value, parameters = part.split(';', 2).map(&:strip) quality = 1.0 if parameters && (md = /\Aq=([\d.]+)/.match(parameters)) quality = md[1].to_f From 62457686b26d33a15a254c7768c2076e8e02b48b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 13 Feb 2024 13:34:34 -0800 Subject: [PATCH 2/4] Return an empty array when ranges are too large If the sum of the requested ranges is larger than the file itself, return an empty array. In other words, refuse to respond with any bytes. [CVE-2024-26141] --- lib/rack/utils.rb | 3 +++ test/spec_utils.rb | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index 0ed64b7a5..ccf39e301 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -380,6 +380,9 @@ def get_byte_ranges(http_range, size) end ranges << (r0..r1) if r0 <= r1 end + + return [] if ranges.map(&:size).sum > size + ranges end diff --git a/test/spec_utils.rb b/test/spec_utils.rb index 90676258f..6b069914d 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -590,6 +590,10 @@ def initialize(*) end describe Rack::Utils, "byte_range" do + it "returns an empty list if the sum of the ranges is too large" do + assert_equal [], Rack::Utils.byte_ranges({ "HTTP_RANGE" => "bytes=0-20,0-500" }, 500) + end + it "ignore missing or syntactically invalid byte ranges" do Rack::Utils.byte_ranges({}, 500).must_be_nil Rack::Utils.byte_ranges({ "HTTP_RANGE" => "foobar" }, 500).must_be_nil From d9c163a443b8cadf4711d84bd2c58cb9ef89cf49 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 6 Dec 2023 18:32:19 +0100 Subject: [PATCH 3/4] Avoid 2nd degree polynomial regexp in MediaType --- lib/rack/media_type.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/rack/media_type.rb b/lib/rack/media_type.rb index 41937c994..7fc1e39db 100644 --- a/lib/rack/media_type.rb +++ b/lib/rack/media_type.rb @@ -4,7 +4,7 @@ module Rack # Rack::MediaType parse media type and parameters out of content_type string class MediaType - SPLIT_PATTERN = %r{\s*[;,]\s*} + SPLIT_PATTERN = /[;,]/ class << self # The media type (type/subtype) portion of the CONTENT_TYPE header @@ -15,7 +15,11 @@ class << self # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 def type(content_type) return nil unless content_type - content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase! + if type = content_type.split(SPLIT_PATTERN, 2).first + type.rstrip! + type.downcase! + type + end end # The media type parameters provided in CONTENT_TYPE as a Hash, or @@ -27,9 +31,10 @@ def params(content_type) return {} if content_type.nil? content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh| + s.strip! k, v = s.split('=', 2) - - hsh[k.tap(&:downcase!)] = strip_doublequotes(v) + k.downcase! + hsh[k] = strip_doublequotes(v) end end From e83001100ad9dd24e1744b13669dcb2736a13ebd Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Feb 2024 11:22:39 -0800 Subject: [PATCH 4/4] bump version --- lib/rack/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rack/version.rb b/lib/rack/version.rb index cc4de31aa..e0035a937 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -20,7 +20,7 @@ def self.version VERSION.join(".") end - RELEASE = "2.2.8" + RELEASE = "2.2.8.1" # Return the Rack release as a dotted string. def self.release