diff --git a/CVE-2024-45614.patch b/CVE-2024-45614.patch new file mode 100644 index 0000000000000000000000000000000000000000..136a4a831df0e51186e79ab78a583c77037c0d68 --- /dev/null +++ b/CVE-2024-45614.patch @@ -0,0 +1,210 @@ +From f196b23be24712fb8fb16051cc124798cc84f70e Mon Sep 17 00:00:00 2001 +From: Evan Phoenix +Date: Wed, 18 Sep 2024 21:56:07 -0700 +Subject: [PATCH] Merge commit from fork + +Origin: https://github.com/puma/puma/commit/f196b23be24712fb8fb16051cc124798cc84f70e + +* Prevent underscores from clobbering hyphen headers + +* Special case encoding headers to prevent app confusion + +* Handle _ as , in jruby as well + +* Silence RuboCop offense + +--------- + +Co-authored-by: Patrik Ragnarsson +--- + ext/puma_http11/org/jruby/puma/Http11.java | 2 + + lib/puma/const.rb | 8 +++ + lib/puma/request.rb | 19 ++++++-- + test/test_normalize.rb | 57 ++++++++++++++++++++++ + test/test_request_invalid.rb | 28 +++++++++++ + 5 files changed, 111 insertions(+), 3 deletions(-) + create mode 100644 test/test_normalize.rb + +diff --git a/ext/puma_http11/org/jruby/puma/Http11.java b/ext/puma_http11/org/jruby/puma/Http11.java +index cd7a5d3bb0..0c4f79eee7 100644 +--- a/ext/puma_http11/org/jruby/puma/Http11.java ++++ b/ext/puma_http11/org/jruby/puma/Http11.java +@@ -99,6 +99,8 @@ public static void http_field(Ruby runtime, RubyHash req, ByteList buffer, int f + int bite = b.get(i) & 0xFF; + if(bite == '-') { + b.set(i, (byte)'_'); ++ } else if(bite == '_') { ++ b.set(i, (byte)','); + } else { + b.set(i, (byte)Character.toUpperCase(bite)); + } +diff --git a/lib/puma/const.rb b/lib/puma/const.rb +index c4968f4ae8..451105e648 100644 +--- a/lib/puma/const.rb ++++ b/lib/puma/const.rb +@@ -244,6 +244,14 @@ module Const + # header values can contain HTAB? + ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze + ++ # The keys of headers that should not be convert to underscore ++ # normalized versions. These headers are ignored at the request reading layer, ++ # but if we normalize them after reading, it's just confusing for the application. ++ UNMASKABLE_HEADERS = { ++ "HTTP_TRANSFER,ENCODING" => true, ++ "HTTP_CONTENT,LENGTH" => true, ++ } ++ + # Banned keys of response header + BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze + +diff --git a/lib/puma/request.rb b/lib/puma/request.rb +index 8c7b008ee8..86e2d467e4 100644 +--- a/lib/puma/request.rb ++++ b/lib/puma/request.rb +@@ -318,6 +318,11 @@ def illegal_header_value?(header_value) + # compatibility, we'll convert them back. This code is written to + # avoid allocation in the common case (ie there are no headers + # with `,` in their names), that's why it has the extra conditionals. ++ # ++ # @note If a normalized version of a `,` header already exists, we ignore ++ # the `,` version. This prevents clobbering headers managed by proxies ++ # but not by clients (Like X-Forwarded-For). ++ # + # @param env [Hash] see Puma::Client#env, from request, modifies in place + # @version 5.0.3 + # +@@ -326,23 +331,31 @@ def req_env_post_parse(env) + to_add = nil + + env.each do |k,v| +- if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING" ++ if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k) + if to_delete + to_delete << k + else + to_delete = [k] + end + ++ new_k = k.tr(",", "_") ++ if env.key?(new_k) ++ next ++ end ++ + unless to_add + to_add = {} + end + +- to_add[k.tr(",", "_")] = v ++ to_add[new_k] = v + end + end + +- if to_delete ++ if to_delete # rubocop:disable Style/SafeNavigation + to_delete.each { |k| env.delete(k) } ++ end ++ ++ if to_add + env.merge! to_add + end + end +diff --git a/test/test_normalize.rb b/test/test_normalize.rb +new file mode 100644 +index 0000000000..60e61c3dde +--- /dev/null ++++ b/test/test_normalize.rb +@@ -0,0 +1,57 @@ ++# frozen_string_literal: true ++ ++require_relative "helper" ++ ++require "puma/request" ++ ++class TestNormalize < Minitest::Test ++ parallelize_me! ++ ++ include Puma::Request ++ ++ def test_comma_headers ++ env = { ++ "HTTP_X_FORWARDED_FOR" => "1.1.1.1", ++ "HTTP_X_FORWARDED,FOR" => "2.2.2.2", ++ } ++ ++ req_env_post_parse env ++ ++ expected = { ++ "HTTP_X_FORWARDED_FOR" => "1.1.1.1", ++ } ++ ++ assert_equal expected, env ++ ++ # Test that the iteration order doesn't matter ++ ++ env = { ++ "HTTP_X_FORWARDED,FOR" => "2.2.2.2", ++ "HTTP_X_FORWARDED_FOR" => "1.1.1.1", ++ } ++ ++ req_env_post_parse env ++ ++ expected = { ++ "HTTP_X_FORWARDED_FOR" => "1.1.1.1", ++ } ++ ++ assert_equal expected, env ++ end ++ ++ def test_unmaskable_headers ++ env = { ++ "HTTP_CONTENT,LENGTH" => "100000", ++ "HTTP_TRANSFER,ENCODING" => "chunky" ++ } ++ ++ req_env_post_parse env ++ ++ expected = { ++ "HTTP_CONTENT,LENGTH" => "100000", ++ "HTTP_TRANSFER,ENCODING" => "chunky" ++ } ++ ++ assert_equal expected, env ++ end ++end +diff --git a/test/test_request_invalid.rb b/test/test_request_invalid.rb +index 8e9295b592..c6aa91ab05 100644 +--- a/test/test_request_invalid.rb ++++ b/test/test_request_invalid.rb +@@ -216,4 +216,32 @@ def test_chunked_size_mismatch_2 + + assert_status data + end ++ ++ def test_underscore_header_1 ++ hdrs = [ ++ "X-FORWARDED-FOR: 1.1.1.1", # proper ++ "X-FORWARDED-FOR: 2.2.2.2", # proper ++ "X_FORWARDED-FOR: 3.3.3.3", # invalid, contains underscore ++ "Content-Length: 5", ++ ].join "\r\n" ++ ++ response = send_http_and_read "#{GET_PREFIX}#{hdrs}\r\n\r\nHello\r\n\r\n" ++ ++ assert_includes response, "HTTP_X_FORWARDED_FOR = 1.1.1.1, 2.2.2.2" ++ refute_includes response, "3.3.3.3" ++ end ++ ++ def test_underscore_header_2 ++ hdrs = [ ++ "X_FORWARDED-FOR: 3.3.3.3", # invalid, contains underscore ++ "X-FORWARDED-FOR: 2.2.2.2", # proper ++ "X-FORWARDED-FOR: 1.1.1.1", # proper ++ "Content-Length: 5", ++ ].join "\r\n" ++ ++ response = send_http_and_read "#{GET_PREFIX}#{hdrs}\r\n\r\nHello\r\n\r\n" ++ ++ assert_includes response, "HTTP_X_FORWARDED_FOR = 2.2.2.2, 1.1.1.1" ++ refute_includes response, "3.3.3.3" ++ end + end diff --git a/rubygem-puma.spec b/rubygem-puma.spec index e96a2e43f41e76be44b1a232da32430f32230fd7..1558cc6498fe4f6304bbc51101bdad58fe4130ff 100644 --- a/rubygem-puma.spec +++ b/rubygem-puma.spec @@ -2,7 +2,7 @@ %bcond_with ragel Name: rubygem-%{gem_name} Version: 5.6.5 -Release: 2 +Release: 3 Summary: A simple, fast, threaded, and highly concurrent HTTP 1.1 server License: BSD-3-Clause URL: http://puma.io @@ -13,6 +13,7 @@ Source1: https://github.com/puma/%{gem_name}/archive/refs/tags/v%{ve Patch0: rubygem-puma-3.6.0-fedora-crypto-policy-cipher-list.patch Patch1: CVE-2023-40175.patch Patch2: CVE-2024-21647.patch +Patch3: CVE-2024-45614.patch BuildRequires: openssl-devel ruby(release) rubygems-devel ruby-devel rubygem(rack) BuildRequires: rubygem(minitest) rubygem(sd_notify) @@ -37,6 +38,7 @@ Documentation for %{name}. %patch0 -p1 %patch1 -p1 %patch2 -p1 +%patch3 -p1 rm -rf test/test_thread_pool.rb %if %{with ragel} @@ -146,6 +148,9 @@ ruby -e 'Dir.glob "./test/**/test_*.rb", &method(:require)' - -v %{gem_instdir}/tools %changelog +* Fri Sep 27 2024 wangkai <13474090681@163.com> - 5.6.5-3 +- Fix CVE-2024-45614 + * Wed Aug 07 2024 Ge Wang - 5.6.5-2 - OpenSSL certificate has expired, skip relevant test case