Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-puma for openSUSE:Factory checked in at 2022-04-01 21:35:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-puma (Old) and /work/SRC/openSUSE:Factory/.rubygem-puma.new.1900 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-puma" Fri Apr 1 21:35:55 2022 rev:50 rq:966342 version:5.6.4 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-puma/rubygem-puma.changes 2022-02-14 22:37:05.153556713 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-puma.new.1900/rubygem-puma.changes 2022-04-01 21:37:09.372805930 +0200 @@ -1,0 +2,7 @@ +Wed Mar 30 22:24:21 UTC 2022 - Marcus Rueckert <[email protected]> + +- Update to version 5.6.4 + https://github.com/puma/puma/security/advisories/GHSA-h99w-9q5r-gjq9 + https://github.com/puma/puma/releases/tag/v5.6.4 + +------------------------------------------------------------------- Old: ---- puma-5.6.2.gem New: ---- puma-5.6.4.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-puma.spec ++++++ --- /var/tmp/diff_new_pack.GkRzmd/_old 2022-04-01 21:37:09.888800271 +0200 +++ /var/tmp/diff_new_pack.GkRzmd/_new 2022-04-01 21:37:09.888800271 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-puma -Version: 5.6.2 +Version: 5.6.4 Release: 0 %define mod_name puma %define mod_full_name %{mod_name}-%{version} ++++++ puma-5.6.2.gem -> puma-5.6.4.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/History.md new/History.md --- old/History.md 1980-01-01 01:00:00.000000000 +0100 +++ new/History.md 1980-01-01 01:00:00.000000000 +0100 @@ -1,3 +1,8 @@ +## 5.6.4 / 2022-03-30 + +* Security + * Close several HTTP Request Smuggling exploits (CVE-2022-24790) + ## 5.6.2 / 2022-02-11 * Bugfix/Security Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/client.rb new/lib/puma/client.rb --- old/lib/puma/client.rb 1980-01-01 01:00:00.000000000 +0100 +++ new/lib/puma/client.rb 1980-01-01 01:00:00.000000000 +0100 @@ -23,6 +23,8 @@ class ConnectionError < RuntimeError; end + class HttpParserError501 < IOError; end + # An instance of this class represents a unique request from a client. # For example, this could be a web request from a browser or from CURL. # @@ -35,7 +37,21 @@ # Instances of this class are responsible for knowing if # the header and body are fully buffered via the `try_to_finish` method. # They can be used to "time out" a response via the `timeout_at` reader. + # class Client + + # this tests all values but the last, which must be chunked + ALLOWED_TRANSFER_ENCODING = %w[compress deflate gzip].freeze + + # chunked body validation + CHUNK_SIZE_INVALID = /[^\h]/.freeze + CHUNK_VALID_ENDING = "\r\n".freeze + + # Content-Length header value validation + CONTENT_LENGTH_VALUE_INVALID = /[^\d]/.freeze + + TE_ERR_MSG = 'Invalid Transfer-Encoding' + # The object used for a request with no body. All requests with # no body share this one object since it has no state. EmptyBody = NullIO.new @@ -302,16 +318,27 @@ body = @parser.body te = @env[TRANSFER_ENCODING2] - if te - if te.include?(",") - te.split(",").each do |part| - if CHUNKED.casecmp(part.strip) == 0 - return setup_chunked_body(body) - end + te_lwr = te.downcase + if te.include? ',' + te_ary = te_lwr.split ',' + te_count = te_ary.count CHUNKED + te_valid = te_ary[0..-2].all? { |e| ALLOWED_TRANSFER_ENCODING.include? e } + if te_ary.last == CHUNKED && te_count == 1 && te_valid + @env.delete TRANSFER_ENCODING2 + return setup_chunked_body body + elsif te_count >= 1 + raise HttpParserError , "#{TE_ERR_MSG}, multiple chunked: '#{te}'" + elsif !te_valid + raise HttpParserError501, "#{TE_ERR_MSG}, unknown value: '#{te}'" end - elsif CHUNKED.casecmp(te) == 0 - return setup_chunked_body(body) + elsif te_lwr == CHUNKED + @env.delete TRANSFER_ENCODING2 + return setup_chunked_body body + elsif ALLOWED_TRANSFER_ENCODING.include? te_lwr + raise HttpParserError , "#{TE_ERR_MSG}, single value must be chunked: '#{te}'" + else + raise HttpParserError501 , "#{TE_ERR_MSG}, unknown value: '#{te}'" end end @@ -319,7 +346,12 @@ cl = @env[CONTENT_LENGTH] - unless cl + if cl + # cannot contain characters that are not \d + if cl =~ CONTENT_LENGTH_VALUE_INVALID + raise HttpParserError, "Invalid Content-Length: #{cl.inspect}" + end + else @buffer = body.empty? ? nil : body @body = EmptyBody set_ready @@ -478,7 +510,13 @@ while !io.eof? line = io.gets if line.end_with?("\r\n") - len = line.strip.to_i(16) + # Puma doesn't process chunk extensions, but should parse if they're + # present, which is the reason for the semicolon regex + chunk_hex = line.strip[/\A[^;]+/] + if chunk_hex =~ CHUNK_SIZE_INVALID + raise HttpParserError, "Invalid chunk size: '#{chunk_hex}'" + end + len = chunk_hex.to_i(16) if len == 0 @in_last_chunk = true @body.rewind @@ -509,7 +547,12 @@ case when got == len - write_chunk(part[0..-3]) # to skip the ending \r\n + # proper chunked segment must end with "\r\n" + if part.end_with? CHUNK_VALID_ENDING + write_chunk(part[0..-3]) # to skip the ending \r\n + else + raise HttpParserError, "Chunk size mismatch" + end when got <= len - 2 write_chunk(part) @partial_part_left = len - part.size diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/const.rb new/lib/puma/const.rb --- old/lib/puma/const.rb 1980-01-01 01:00:00.000000000 +0100 +++ new/lib/puma/const.rb 1980-01-01 01:00:00.000000000 +0100 @@ -76,7 +76,7 @@ 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required' - } + }.freeze # For some HTTP status codes the client only expects headers. # @@ -85,7 +85,7 @@ 204 => true, 205 => true, 304 => true - } + }.freeze # Frequently used constants when constructing requests or responses. Many times # the constant just refers to a string with the same contents. Using these constants @@ -100,7 +100,7 @@ # too taxing on performance. module Const - PUMA_VERSION = VERSION = "5.6.2".freeze + PUMA_VERSION = VERSION = "5.6.4".freeze CODE_NAME = "Birdie's Version".freeze PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze @@ -145,9 +145,11 @@ 408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze, # Indicate that there was an internal error, obviously. 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze, + # Incorrect or invalid header value + 501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze, # A common header for indicating the server is too busy. Not used yet. 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze - } + }.freeze # The basic max request size we'll try to read. CHUNK_SIZE = 16 * 1024 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/puma/server.rb new/lib/puma/server.rb --- old/lib/puma/server.rb 1980-01-01 01:00:00.000000000 +0100 +++ new/lib/puma/server.rb 1980-01-01 01:00:00.000000000 +0100 @@ -515,6 +515,9 @@ when HttpParserError client.write_error(400) @events.parse_error e, client + when HttpParserError501 + client.write_error(501) + @events.parse_error e, client else client.write_error(500) @events.unknown_error e, nil, "Read" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 1980-01-01 01:00:00.000000000 +0100 +++ new/metadata 1980-01-01 01:00:00.000000000 +0100 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: puma version: !ruby/object:Gem::Version - version: 5.6.2 + version: 5.6.4 platform: ruby authors: - Evan Phoenix
