From a227cd793778c7c3a827d32808058571569cda6f 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 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index 99d696dbf..62c2502cc 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -143,8 +143,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 @@ -157,9 +157,10 @@ def forwarded_values(forwarded_header) return nil unless forwarded_header forwarded_header = forwarded_header.to_s.gsub("\n", ";") - forwarded_header.split(/\s*;\s*/).each_with_object({}) do |field, values| - field.split(/\s*,\s*/).each do |pair| - return nil unless pair =~ /\A\s*(by|for|host|proto)\s*=\s*"?([^"]+)"?\s*\Z/i + forwarded_header.split(';').each_with_object({}) do |field, values| + field.split(',').each do |pair| + pair = pair.split('=').map(&:strip).join('=') + return nil unless pair =~ /\A(by|for|host|proto)="?([^"]+)"?\Z/i (values[$1.downcase.to_sym] ||= []) << $2 end end From 4849132bef471adb21131980df745f4bb84de2d9 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 62c2502cc..f91838b37 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -459,6 +459,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 c4f9b27fa..2165c5a6a 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -716,6 +716,10 @@ def initialize(*) end describe Rack::Utils, "get_byte_ranges" 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 + deprecated "pase simple byte ranges from env" do Rack::Utils.byte_ranges({ "HTTP_RANGE" => "bytes=123-456" }, 500).must_equal [(123..456)] end From 6efb2ceea003c4b195815a614e00438cbd543462 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 ff3145deb..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 a4bc5e0f41c750135969ceece8772ab112dc8f17 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 21 Feb 2024 11:23:31 -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 e2deadab2..8817699e1 100644 --- a/lib/rack/version.rb +++ b/lib/rack/version.rb @@ -25,7 +25,7 @@ def self.version VERSION end - RELEASE = "3.0.9" + RELEASE = "3.0.9.1" # Return the Rack release as a dotted string. def self.release