From 30845fdcc8b86ec2743da9827a19547b65e2df0e Mon Sep 17 00:00:00 2001 From: shixuantong Date: Thu, 5 Jun 2025 09:54:11 +0800 Subject: [PATCH] fix CVE-2025-43857 (cherry picked from commit 590849f46d9a4b8421c74ddc3190dae6175b4d13) --- backport-0001-CVE-2025-43857.patch | 120 ++++++++++++++++ backport-0002-CVE-2025-43857.patch | 68 +++++++++ backport-0003-CVE-2025-43857.patch | 64 +++++++++ backport-0004-CVE-2025-43857.patch | 118 ++++++++++++++++ ...ponse_handlers-kwarg-to-Net-IMAP.new.patch | 67 +++++++++ ...ring-literals-with-specific-capacity.patch | 36 +++++ ...eral-regexp-to-the-end-of-the-buffer.patch | 37 +++++ ...ly-throw-eof-for-EOF-in-get_response.patch | 60 ++++++++ ...act-ResponseReader-from-get_response.patch | 130 ++++++++++++++++++ ...ne-and-literal-parts-of-get_response.patch | 56 ++++++++ ...backport-compatibility-with-ruby-2.7.patch | 32 +++++ ...at-get_response-debug-trace-printing.patch | 36 +++++ ...sponseReader-ivars-buff-literal_size.patch | 69 ++++++++++ ...t-Simplify-get_response-loop-further.patch | 36 +++++ ruby.spec | 19 ++- 15 files changed, 947 insertions(+), 1 deletion(-) create mode 100644 backport-0001-CVE-2025-43857.patch create mode 100644 backport-0002-CVE-2025-43857.patch create mode 100644 backport-0003-CVE-2025-43857.patch create mode 100644 backport-0004-CVE-2025-43857.patch create mode 100644 backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch create mode 100644 backport-Allocate-string-literals-with-specific-capacity.patch create mode 100644 backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch create mode 100644 backport-Explicitly-throw-eof-for-EOF-in-get_response.patch create mode 100644 backport-Extract-ResponseReader-from-get_response.patch create mode 100644 backport-Extract-line-and-literal-parts-of-get_response.patch create mode 100644 backport-Fix-backport-compatibility-with-ruby-2.7.patch create mode 100644 backport-Reformat-get_response-debug-trace-printing.patch create mode 100644 backport-Save-ResponseReader-ivars-buff-literal_size.patch create mode 100644 backport-Simplify-get_response-loop-further.patch diff --git a/backport-0001-CVE-2025-43857.patch b/backport-0001-CVE-2025-43857.patch new file mode 100644 index 0000000..4dfc541 --- /dev/null +++ b/backport-0001-CVE-2025-43857.patch @@ -0,0 +1,120 @@ +From 53ceba1e97cbc3ac4d141077732178cc8bc79476 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 19 Apr 2025 22:21:59 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Limit=20max=20response=20size=20to?= + =?UTF-8?q?=20512MiB=20(hard-coded)?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +_Please note:_ this only limits the size per response. It does _not_ +limit how many unhandled responses may be stored on the responses hash. + +Reference:https://github.com/ruby/net-imap/commit/53ceba1e97cbc3ac4d141077732178cc8bc79476 +Conflict:don't chang test file. +--- + .../net-imap-0.3.4/lib/net/imap/errors.rb | 33 +++++++++++++++++++ + .../lib/net/imap/response_reader.rb | 31 +++++++++++++++-- + 2 files changed, 62 insertions(+), 2 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb +index b353756..dcb5091 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb +@@ -11,6 +11,39 @@ module Net + class DataFormatError < Error + end + ++ # Error raised when the socket cannot be read, due to a configured limit. ++ class ResponseReadError < Error ++ end ++ ++ # Error raised when a response is larger than IMAP#max_response_size. ++ class ResponseTooLargeError < ResponseReadError ++ attr_reader :bytes_read, :literal_size ++ attr_reader :max_response_size ++ ++ def initialize(msg = nil, *args, ++ bytes_read: nil, ++ literal_size: nil, ++ max_response_size: nil, ++ **kwargs) ++ @bytes_read = bytes_read ++ @literal_size = literal_size ++ @max_response_size = max_response_size ++ msg ||= [ ++ "Response size", response_size_msg, "exceeds max_response_size", ++ max_response_size && "(#{max_response_size}B)", ++ ].compact.join(" ") ++ super(msg, *args, **kwargs) ++ end ++ ++ private ++ ++ def response_size_msg ++ if bytes_read && literal_size ++ "(#{bytes_read}B read + #{literal_size}B literal)" ++ end ++ end ++ end ++ + # Error raised when a response from the server is non-parseable. + class ResponseParseError < Error + end +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +index 6a608b1..3c33dea 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -28,19 +28,46 @@ module Net + + attr_reader :buff, :literal_size + ++ def bytes_read = buff.bytesize ++ def empty? = buff.empty? ++ def done? = line_done? && !get_literal_size ++ def line_done? = buff.end_with?(CRLF) + def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end + + def read_line +- buff << (@sock.gets(CRLF) or throw :eof) ++ buff << (@sock.gets(CRLF, read_limit) or throw :eof) ++ max_response_remaining! unless line_done? + end + + def read_literal ++ # check before allocating memory for literal ++ max_response_remaining! + literal = String.new(capacity: literal_size) +- buff << (@sock.read(literal_size, literal) or throw :eof) ++ buff << (@sock.read(read_limit(literal_size), literal) or throw :eof) + ensure + @literal_size = nil + end + ++ def read_limit(limit = nil) ++ [limit, max_response_remaining!].compact.min ++ end ++ ++ def max_response_size = 512 << 20 # TODO: Config#max_response_size ++ def max_response_remaining = max_response_size &.- bytes_read ++ def response_too_large? = max_response_size &.< min_response_size ++ def min_response_size = bytes_read + min_response_remaining ++ ++ def min_response_remaining ++ empty? ? 3 : done? ? 0 : (literal_size || 0) + 2 ++ end ++ ++ def max_response_remaining! ++ return max_response_remaining unless response_too_large? ++ raise ResponseTooLargeError.new( ++ max_response_size:, bytes_read:, literal_size:, ++ ) ++ end ++ + end + end + end +-- +2.22.0 + + diff --git a/backport-0002-CVE-2025-43857.patch b/backport-0002-CVE-2025-43857.patch new file mode 100644 index 0000000..b7d6c84 --- /dev/null +++ b/backport-0002-CVE-2025-43857.patch @@ -0,0 +1,68 @@ +From 158cfdff54f3961b0ec628136444e3b0b0bb1736 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sun, 20 Apr 2025 17:38:43 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Make=20max=5Fresponse=5Fsize=20conf?= + =?UTF-8?q?igurable=20[=F0=9F=9A=A7=20partial]?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/158cfdff54f3961b0ec628136444e3b0b0bb1736 +Conflict:(1)No changes were made to the first comment. +(2)don't change test file + +Note that this cherry-picked commit is missing key paits that are +incompatible with net-imap before 0.4. I'm keeping the conflict +resolution here, and the updates for net-imap 0.3 in the next commit. + +------ + +Though it would be useful to also have limits based on response type and +what commands are currently running, that's out of scope for now. + +_Please note:_ this only limits the size per response. It does _not_ +limit how many unhandled responses may be stored on the responses hash. +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 11 +++++++++++ + .../net-imap-0.3.4/lib/net/imap/response_reader.rb | 2 +- + 2 files changed, 12 insertions(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 49fe06b..a369787 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -764,6 +764,17 @@ module Net + alias default_ssl_port default_tls_port + end + ++ ## ++ # :attr_accessor: max_response_size ++ # ++ # The maximum allowed server response size, in bytes. ++ # Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size]. ++ ++ # :stopdoc: ++ def max_response_size; config.max_response_size end ++ def max_response_size=(val) config.max_response_size = val end ++ # :startdoc: ++ + # Disconnects from the server. + # + # Related: #logout +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +index 3c33dea..bcf3601 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -52,7 +52,7 @@ module Net + [limit, max_response_remaining!].compact.min + end + +- def max_response_size = 512 << 20 # TODO: Config#max_response_size ++ def max_response_size = client.max_response_size + def max_response_remaining = max_response_size &.- bytes_read + def response_too_large? = max_response_size &.< min_response_size + def min_response_size = bytes_read + min_response_remaining +-- +2.22.0 + + diff --git a/backport-0003-CVE-2025-43857.patch b/backport-0003-CVE-2025-43857.patch new file mode 100644 index 0000000..8b837c9 --- /dev/null +++ b/backport-0003-CVE-2025-43857.patch @@ -0,0 +1,64 @@ +From ae0fa010bb5e3c95b9beee31af607d4dba619d63 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sun, 20 Apr 2025 21:01:48 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20compatibility=20wi?= + =?UTF-8?q?th=20ruby=202.7?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/ae0fa010bb5e3c95b9beee31af607d4dba619d63 +Conflict:don't chang test file. +--- + .../lib/net/imap/response_reader.rb | 20 ++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +index bcf3601..fd7561f 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -28,10 +28,10 @@ module Net + + attr_reader :buff, :literal_size + +- def bytes_read = buff.bytesize +- def empty? = buff.empty? +- def done? = line_done? && !get_literal_size +- def line_done? = buff.end_with?(CRLF) ++ def bytes_read; buff.bytesize end ++ def empty?; buff.empty? end ++ def done?; line_done? && !get_literal_size end ++ def line_done?; buff.end_with?(CRLF) end + def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end + + def read_line +@@ -52,10 +52,10 @@ module Net + [limit, max_response_remaining!].compact.min + end + +- def max_response_size = client.max_response_size +- def max_response_remaining = max_response_size &.- bytes_read +- def response_too_large? = max_response_size &.< min_response_size +- def min_response_size = bytes_read + min_response_remaining ++ def max_response_size; client.max_response_size end ++ def max_response_remaining; max_response_size &.- bytes_read end ++ def response_too_large?; max_response_size &.< min_response_size end ++ def min_response_size; bytes_read + min_response_remaining end + + def min_response_remaining + empty? ? 3 : done? ? 0 : (literal_size || 0) + 2 +@@ -64,7 +64,9 @@ module Net + def max_response_remaining! + return max_response_remaining unless response_too_large? + raise ResponseTooLargeError.new( +- max_response_size:, bytes_read:, literal_size:, ++ max_response_size: max_response_size, ++ bytes_read: bytes_read, ++ literal_size: literal_size, + ) + end + +-- +2.22.0 + + diff --git a/backport-0004-CVE-2025-43857.patch b/backport-0004-CVE-2025-43857.patch new file mode 100644 index 0000000..9cada1d --- /dev/null +++ b/backport-0004-CVE-2025-43857.patch @@ -0,0 +1,118 @@ +From e0059251e854cb03d5209c682ba3484fcb6953cd Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Wed, 9 Apr 2025 09:54:51 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20to=20not-imap=200.?= + =?UTF-8?q?3=20and=20ruby=202.6?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/e0059251e854cb03d5209c682ba3484fcb6953cd +Conflict:(1)No changes were made to the first comment. +(2)don't change test file. + +For the net-imap v0.3 backport, two major changes were needed: +* the tests needed to be almost completely rewritten because FakeServer + was added for v0.4. +* needed to be on Net::IMAP directly, because Config + was added for v0.4. +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 47 ++++++++++++++----- + .../net-imap-0.3.4/lib/net/imap/errors.rb | 1 + + 2 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index a369787..7c5881a 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -736,6 +736,40 @@ module Net + # Seconds to wait until an IDLE response is received. + attr_reader :idle_response_timeout + ++ # The maximum allowed server response size. When +nil+, there is no limit ++ # on response size. ++ # ++ # The default value is _unlimited_ (after +v0.5.8+, the default is 512 MiB). ++ # A _much_ lower value should be used with untrusted servers (for example, ++ # when connecting to a user-provided hostname). When using a lower limit, ++ # message bodies should be fetched in chunks rather than all at once. ++ # ++ # Please Note: this only limits the size per response. It does ++ # not prevent a flood of individual responses and it does not limit how ++ # many unhandled responses may be stored on the responses hash. See ++ # Net::IMAP@Unbounded+memory+use. ++ # ++ # Socket reads are limited to the maximum remaining bytes for the current ++ # response: max_response_size minus the bytes that have already been read. ++ # When the limit is reached, or reading a +literal+ _would_ go over the ++ # limit, ResponseTooLargeError is raised and the connection is closed. ++ # See also #socket_read_limit. ++ # ++ # Note that changes will not take effect immediately, because the receiver ++ # thread may already be waiting for the next response using the previous ++ # value. Net::IMAP#noop can force a response and enforce the new setting ++ # immediately. ++ # ++ # ==== Versioned Defaults ++ # ++ # Net::IMAP#max_response_size was added in +v0.2.5+ and +v0.3.9+ as an ++ # attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to a config ++ # attribute. ++ # ++ # * original: +nil+ (no limit) ++ # * +0.5+: 512 MiB ++ attr_accessor :max_response_size ++ + attr_accessor :client_thread # :nodoc: + + # Returns the debug mode. +@@ -764,17 +798,6 @@ module Net + alias default_ssl_port default_tls_port + end + +- ## +- # :attr_accessor: max_response_size +- # +- # The maximum allowed server response size, in bytes. +- # Delegates to {config.max_response_size}[rdoc-ref:Config#max_response_size]. +- +- # :stopdoc: +- def max_response_size; config.max_response_size end +- def max_response_size=(val) config.max_response_size = val end +- # :startdoc: +- + # Disconnects from the server. + # + # Related: #logout +@@ -2019,6 +2042,7 @@ module Net + # that the greeting is handled in the current thread, + # but all other responses are handled in the receiver + # thread. ++ # max_response_size:: See #max_response_size. + # + # The most common errors are: + # +@@ -2049,6 +2073,7 @@ module Net + @tagno = 0 + @open_timeout = options[:open_timeout] || 30 + @idle_response_timeout = options[:idle_response_timeout] || 5 ++ @max_response_size = options[:max_response_size] + @parser = ResponseParser.new + @sock = tcp_socket(@host, @port) + @reader = ResponseReader.new(self, @sock) +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb +index dcb5091..52cb936 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/errors.rb +@@ -32,6 +32,7 @@ module Net + "Response size", response_size_msg, "exceeds max_response_size", + max_response_size && "(#{max_response_size}B)", + ].compact.join(" ") ++ return super(msg, *args) if kwargs.empty? # ruby 2.6 compatibility + super(msg, *args, **kwargs) + end + +-- +2.22.0 + + diff --git a/backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch b/backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch new file mode 100644 index 0000000..77ec0ad --- /dev/null +++ b/backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch @@ -0,0 +1,67 @@ +From 624f6da3323f7316cc99a4e4c5f1a3a931546a40 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 22 Mar 2025 14:53:00 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20=20kwarg?= + =?UTF-8?q?=20to=20Net::IMAP.new?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/624f6da3323f7316cc99a4e4c5f1a3a931546a40 +Conflict:(1)No changes were made to the first comment. +(2)don't change test file + +This ensures every server response is handled, including the greeting. +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 21243ca..18bf554 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -1960,6 +1960,11 @@ module Net + # end + # } + # ++ # Response handlers can also be added when the client is created before the ++ # receiver thread is started, by the +response_handlers+ argument to ::new. ++ # This ensures every server response is handled, including the #greeting. ++ # ++ # Related: #remove_response_handler, #response_handlers + def add_response_handler(handler = nil, &block) + raise ArgumentError, "two Procs are passed" if handler && block + @response_handlers.push(block || handler) +@@ -1995,6 +2000,12 @@ module Net + # OpenSSL::SSL::SSLContext#set_params as parameters. + # open_timeout:: Seconds to wait until a connection is opened + # idle_response_timeout:: Seconds to wait until an IDLE response is received ++ # response_handlers:: A list of response handlers to be added before the ++ # receiver thread is started. This ensures every server ++ # response is handled, including the #greeting. Note ++ # that the greeting is handled in the current thread, ++ # but all other responses are handled in the receiver ++ # thread. + # + # The most common errors are: + # +@@ -2037,6 +2048,7 @@ module Net + @responses = Hash.new([].freeze) + @tagged_responses = {} + @response_handlers = [] ++ options[:response_handlers]&.each do |h| add_response_handler(h) end + @tagged_response_arrival = new_cond + @continued_command_tag = nil + @continuation_request_arrival = new_cond +@@ -2053,6 +2065,7 @@ module Net + if @greeting.name == "BYE" + raise ByeResponseError, @greeting + end ++ @response_handlers.each do |handler| handler.call(@greeting) end + + @client_thread = Thread.current + @receiver_thread = Thread.start { +-- +2.22.0 + + diff --git a/backport-Allocate-string-literals-with-specific-capacity.patch b/backport-Allocate-string-literals-with-specific-capacity.patch new file mode 100644 index 0000000..fb9b0c0 --- /dev/null +++ b/backport-Allocate-string-literals-with-specific-capacity.patch @@ -0,0 +1,36 @@ +From f34e410cf00c648ba5e52ba4999cf4803d9e9bec Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 13:44:50 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Allocate=20string=20litera?= + =?UTF-8?q?ls=20with=20specific=20capacity?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We know exactly how much memory we're going to need, so we can allocate +this up-front, and save a few malloc/memcpy calls on larger literals. + +Reference:https://github.com/ruby/net-imap/commit/f34e410cf00c648ba5e52ba4999cf4803d9e9bec +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 6a97088..e72fb1c 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2209,7 +2209,8 @@ module Net + end + + def get_response_literal(buff, literal_size) +- literal = @sock.read(literal_size) or return ++ literal = String.new(capacity: literal_size) ++ @sock.read(literal_size, literal) or return + buff << literal + end + +-- +2.22.0 + + diff --git a/backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch b/backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch new file mode 100644 index 0000000..d1d1e27 --- /dev/null +++ b/backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch @@ -0,0 +1,37 @@ +From 4b64a5da564edaf39b1023adc58dd2322f85b1d5 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 13:08:21 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Anchor=20literal=20regexp?= + =?UTF-8?q?=20to=20the=20end=20of=20the=20buffer?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This also allows us to check against the concatenated buffer, rather +than the smaller line buffer. That distinction doesn't really matter +now, since we always read an entire line at once. But it will matter if +we read partial lines. + +Reference:https://github.com/ruby/net-imap/commit/4b64a5da564edaf39b1023adc58dd2322f85b1d5 +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 3d6c93a..e65bc26 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2194,7 +2194,7 @@ module Net + s = @sock.gets(CRLF) + break unless s + buff.concat(s) +- if /\{(\d+)\}\r\n/n =~ s ++ if /\{(\d+)\}\r\n\z/n =~ buff + s = @sock.read($1.to_i) + buff.concat(s) + else +-- +2.22.0 + + diff --git a/backport-Explicitly-throw-eof-for-EOF-in-get_response.patch b/backport-Explicitly-throw-eof-for-EOF-in-get_response.patch new file mode 100644 index 0000000..f2f69f9 --- /dev/null +++ b/backport-Explicitly-throw-eof-for-EOF-in-get_response.patch @@ -0,0 +1,60 @@ +From 7f23825e36676f83d06b4527b5c091dc1462e8d4 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 14:00:40 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Explicitly=20"throw=20:eof?= + =?UTF-8?q?"=20for=20EOF=20in=20get=5Fresponse?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This feels a lot more self-documenting than returning nil then breaking +when nil is returned. Also, it lets me refactor the return values for +the get_response_line/get_response_literal methods, or throw from even +deeper in the stack. + +Reference:https://github.com/ruby/net-imap/commit/7f23825e36676f83d06b4527b5c091dc1462e8d4 +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 859a541..023ef2f 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2190,10 +2190,12 @@ module Net + + def get_response + buff = String.new +- while true +- get_response_line(buff) or break +- break unless /\{(\d+)\}\r\n\z/n =~ buff +- get_response_literal(buff, $1.to_i) or break ++ catch :eof do ++ while true ++ get_response_line(buff) ++ break unless /\{(\d+)\}\r\n\z/n =~ buff ++ get_response_literal(buff, $1.to_i) ++ end + end + return nil if buff.length == 0 + $stderr.print(buff.gsub(/^/n, "S: ")) if @@debug +@@ -2201,13 +2203,13 @@ module Net + end + + def get_response_line(buff) +- line = @sock.gets(CRLF) or return ++ line = @sock.gets(CRLF) or throw :eof + buff << line + end + + def get_response_literal(buff, literal_size) + literal = String.new(capacity: literal_size) +- @sock.read(literal_size, literal) or return ++ @sock.read(literal_size, literal) or throw :eof + buff << literal + end + +-- +2.27.0 + diff --git a/backport-Extract-ResponseReader-from-get_response.patch b/backport-Extract-ResponseReader-from-get_response.patch new file mode 100644 index 0000000..02276a1 --- /dev/null +++ b/backport-Extract-ResponseReader-from-get_response.patch @@ -0,0 +1,130 @@ +From 8e2e403293287f9a7bf5341094be6206a745754f Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Wed, 2 Apr 2025 20:50:15 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20ResponseReader?= + =?UTF-8?q?=20from=20get=5Fresponse?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/8e2e403293287f9a7bf5341094be6206a745754f +Conflict:don't change test file. + +It's nice to extract a little bit of the complexity from the core + class. But my primary motivation was so that I could +directly test this code quickly and in isolation from needing to +simulate a full IMAP connection. +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 24 +++--------- + .../lib/net/imap/response_reader.rb | 38 +++++++++++++++++++ + 2 files changed, 43 insertions(+), 19 deletions(-) + create mode 100644 .bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 023ef2f..36962c5 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -703,6 +703,8 @@ module Net + class IMAP < Protocol + VERSION = "0.3.4" + ++ autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__) ++ + include MonitorMixin + if defined?(OpenSSL::SSL) + include OpenSSL +@@ -2038,6 +2040,7 @@ module Net + @idle_response_timeout = options[:idle_response_timeout] || 5 + @parser = ResponseParser.new + @sock = tcp_socket(@host, @port) ++ @reader = ResponseReader.new(self, @sock) + begin + if options[:ssl] + start_tls_session(options[:ssl]) +@@ -2189,30 +2192,12 @@ module Net + end + + def get_response +- buff = String.new +- catch :eof do +- while true +- get_response_line(buff) +- break unless /\{(\d+)\}\r\n\z/n =~ buff +- get_response_literal(buff, $1.to_i) +- end +- end ++ buff = @reader.read_response_buffer + return nil if buff.length == 0 + $stderr.print(buff.gsub(/^/n, "S: ")) if @@debug + @parser.parse(buff) + end + +- def get_response_line(buff) +- line = @sock.gets(CRLF) or throw :eof +- buff << line +- end +- +- def get_response_literal(buff, literal_size) +- literal = String.new(capacity: literal_size) +- @sock.read(literal_size, literal) or throw :eof +- buff << literal +- end +- + ############################# + + def record_response(name, data) +@@ -2392,6 +2377,7 @@ module Net + context.verify_callback = VerifyCallbackProc + end + @sock = SSLSocket.new(@sock, context) ++ @reader = ResponseReader.new(self, @sock) + @sock.sync_close = true + @sock.hostname = @host if @sock.respond_to? :hostname= + ssl_socket_connect(@sock, @open_timeout) +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +new file mode 100644 +index 0000000..57770e3 +--- /dev/null ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -0,0 +1,38 @@ ++# frozen_string_literal: true ++ ++module Net ++ class IMAP ++ # See https://www.rfc-editor.org/rfc/rfc9051#section-2.2.2 ++ class ResponseReader # :nodoc: ++ attr_reader :client ++ ++ def initialize(client, sock) ++ @client, @sock = client, sock ++ end ++ ++ def read_response_buffer ++ buff = String.new ++ catch :eof do ++ while true ++ read_line(buff) ++ break unless /\{(\d+)\}\r\n\z/n =~ buff ++ read_literal(buff, $1.to_i) ++ end ++ end ++ buff ++ end ++ ++ private ++ ++ def read_line(buff) ++ buff << (@sock.gets(CRLF) or throw :eof) ++ end ++ ++ def read_literal(buff, literal_size) ++ literal = String.new(capacity: literal_size) ++ buff << (@sock.read(literal_size, literal) or throw :eof) ++ end ++ ++ end ++ end ++end +-- +2.27.0 + diff --git a/backport-Extract-line-and-literal-parts-of-get_response.patch b/backport-Extract-line-and-literal-parts-of-get_response.patch new file mode 100644 index 0000000..126aff5 --- /dev/null +++ b/backport-Extract-line-and-literal-parts-of-get_response.patch @@ -0,0 +1,56 @@ +From 84c3de81092f8261273526de318a1c79c81b7135 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 12:45:23 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20line=20and=20lit?= + =?UTF-8?q?eral=20parts=20of=20get=5Fresponse?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +IMO, this refactoring makes `get_response` much easier to understand. +Which will be useful, because I'm about to complicate it. 😉 +--- + ./.bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 19 ++++++++++++++----- + 1 file changed, 14 insertions(+), 5 deletions(-) + +diff --git a/./.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/./.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 452f86b..c09d174 100644 +--- a/./.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/./.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2227,12 +2227,9 @@ module Net + def get_response + buff = String.new + while true +- s = @sock.gets(CRLF) +- break unless s +- buff.concat(s) ++ get_response_line(buff) or break + if /\{(\d+)\}\r\n\z/n =~ buff +- s = @sock.read($1.to_i) +- buff.concat(s) ++ get_response_literal(buff, $1.to_i) or break + else + break + end +@@ -2242,6 +2239,18 @@ module Net + @parser.parse(buff) + end + ++ def get_response_line(buff) ++ line = @sock.gets(CRLF) or return ++ buff << line ++ end ++ ++ def get_response_literal(buff, literal_size) ++ literal = @sock.read(literal_size) or return ++ buff << literal ++ end ++ ++ ############################# ++ + def record_response(name, data) + unless @responses.has_key?(name) + @responses[name] = [] +-- +2.27.0 + diff --git a/backport-Fix-backport-compatibility-with-ruby-2.7.patch b/backport-Fix-backport-compatibility-with-ruby-2.7.patch new file mode 100644 index 0000000..3878c15 --- /dev/null +++ b/backport-Fix-backport-compatibility-with-ruby-2.7.patch @@ -0,0 +1,32 @@ +From b0a6039e8edf83c3871397cd11acb01236336d8c Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 19 Apr 2025 21:30:58 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20compatibility=20wi?= + =?UTF-8?q?th=20ruby=202.7?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/b0a6039e8edf83c3871397cd11acb01236336d8c +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +index 7d3a536..6a608b1 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -28,7 +28,7 @@ module Net + + attr_reader :buff, :literal_size + +- def get_literal_size = /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i ++ def get_literal_size; /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i end + + def read_line + buff << (@sock.gets(CRLF) or throw :eof) +-- +2.22.0 + + diff --git a/backport-Reformat-get_response-debug-trace-printing.patch b/backport-Reformat-get_response-debug-trace-printing.patch new file mode 100644 index 0000000..98d8bc7 --- /dev/null +++ b/backport-Reformat-get_response-debug-trace-printing.patch @@ -0,0 +1,36 @@ +From 3446447037c76c2335e657b023c60db0ed62ec8a Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Mon, 24 Mar 2025 17:35:10 -0400 +Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Reformat=20get=5Fresponse=20debu?= + =?UTF-8?q?g=20trace=20printing?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/3446447037c76c2335e657b023c60db0ed62ec8a +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index 18bf554..3d6c93a 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2202,10 +2202,8 @@ module Net + end + end + return nil if buff.length == 0 +- if @@debug +- $stderr.print(buff.gsub(/^/n, "S: ")) +- end +- return @parser.parse(buff) ++ $stderr.print(buff.gsub(/^/n, "S: ")) if @@debug ++ @parser.parse(buff) + end + + def record_response(name, data) +-- +2.22.0 + + diff --git a/backport-Save-ResponseReader-ivars-buff-literal_size.patch b/backport-Save-ResponseReader-ivars-buff-literal_size.patch new file mode 100644 index 0000000..fc32d85 --- /dev/null +++ b/backport-Save-ResponseReader-ivars-buff-literal_size.patch @@ -0,0 +1,69 @@ +From 829532458a0ed25ec9c96fe8be50e3a73ce45ac9 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Mon, 14 Apr 2025 09:23:58 -0400 +Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Save=20ResponseReader=20iv?= + =?UTF-8?q?ars:=20@buff=20&=20@literal=5Fsize?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This avoids the need to pass these to every method that uses them. +That's not a big deal now, but it simplifies the next few changes. + +Also added a missing test for empty literals: "{0}\r\n" + +Reference:https://github.com/ruby/net-imap/commit/829532458a0ed25ec9c96fe8be50e3a73ce45ac9 +Conflict:don't change test file. +--- + .../lib/net/imap/response_reader.rb | 20 +++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +index 57770e3..7d3a536 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap/response_reader.rb +@@ -11,26 +11,34 @@ module Net + end + + def read_response_buffer +- buff = String.new ++ @buff = String.new + catch :eof do + while true +- read_line(buff) +- break unless /\{(\d+)\}\r\n\z/n =~ buff +- read_literal(buff, $1.to_i) ++ read_line ++ break unless (@literal_size = get_literal_size) ++ read_literal + end + end + buff ++ ensure ++ @buff = nil + end + + private + +- def read_line(buff) ++ attr_reader :buff, :literal_size ++ ++ def get_literal_size = /\{(\d+)\}\r\n\z/n =~ buff && $1.to_i ++ ++ def read_line + buff << (@sock.gets(CRLF) or throw :eof) + end + +- def read_literal(buff, literal_size) ++ def read_literal + literal = String.new(capacity: literal_size) + buff << (@sock.read(literal_size, literal) or throw :eof) ++ ensure ++ @literal_size = nil + end + + end +-- +2.22.0 + + diff --git a/backport-Simplify-get_response-loop-further.patch b/backport-Simplify-get_response-loop-further.patch new file mode 100644 index 0000000..0652a89 --- /dev/null +++ b/backport-Simplify-get_response-loop-further.patch @@ -0,0 +1,36 @@ +From 17aa0c53f49efed10891139e5fd802581dffb54b Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 12:46:59 -0400 +Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Simplify=20get=5Fresponse=20loop?= + =?UTF-8?q?=20further?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reference:https://github.com/ruby/net-imap/commit/17aa0c53f49efed10891139e5fd802581dffb54b +Conflict:NA +--- + .bundle/gems/net-imap-0.3.4/lib/net/imap.rb | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +index bd6ee46..859a541 100644 +--- a/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.4/lib/net/imap.rb +@@ -2192,11 +2192,8 @@ module Net + buff = String.new + while true + get_response_line(buff) or break +- if /\{(\d+)\}\r\n\z/n =~ buff +- get_response_literal(buff, $1.to_i) or break +- else +- break +- end ++ break unless /\{(\d+)\}\r\n\z/n =~ buff ++ get_response_literal(buff, $1.to_i) or break + end + return nil if buff.length == 0 + $stderr.print(buff.gsub(/^/n, "S: ")) if @@debug +-- +2.27.0 + diff --git a/ruby.spec b/ruby.spec index 413da36..36a033c 100644 --- a/ruby.spec +++ b/ruby.spec @@ -33,7 +33,7 @@ Name: ruby Version: %{ruby_version} -Release: 149 +Release: 150 Summary: Object-oriented scripting language interpreter License: (Ruby or BSD) and Public Domain and MIT and CC0 and zlib and UCD URL: https://www.ruby-lang.org/en/ @@ -108,6 +108,20 @@ Patch6031: backport-CVE-2025-27219.patch Patch6032: backport-CVE-2025-27220.patch Patch6033: backport-0001-CVE-2025-27221.patch Patch6034: backport-0002-CVE-2025-27221.patch +Patch6035: backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch +Patch6036: backport-Reformat-get_response-debug-trace-printing.patch +Patch6037: backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch +Patch6038: backport-Extract-line-and-literal-parts-of-get_response.patch +Patch6039: backport-Allocate-string-literals-with-specific-capacity.patch +Patch6040: backport-Simplify-get_response-loop-further.patch +Patch6041: backport-Explicitly-throw-eof-for-EOF-in-get_response.patch +Patch6042: backport-Extract-ResponseReader-from-get_response.patch +Patch6043: backport-Save-ResponseReader-ivars-buff-literal_size.patch +Patch6044: backport-Fix-backport-compatibility-with-ruby-2.7.patch +Patch6045: backport-0001-CVE-2025-43857.patch +Patch6046: backport-0002-CVE-2025-43857.patch +Patch6047: backport-0003-CVE-2025-43857.patch +Patch6048: backport-0004-CVE-2025-43857.patch Provides: %{name}-libs = %{version}-%{release} Obsoletes: %{name}-libs < %{version}-%{release} @@ -893,6 +907,9 @@ make runruby TESTRUN_SCRIPT=%{SOURCE13} %{gem_dir}/specifications/matrix-%{matrix_version}.gemspec %changelog +* Thu Jun 05 2025 shixuantong - 3.2.2-150 +- fix CVE-2025-43857 + * Fri Feb 28 2025 shixuantong - 3.2.2-149 - fix CVE-2025-27219 CVE-2025-27220 CVE-2025-27221 -- Gitee