diff --git a/backport-0001-CVE-2025-27221.patch b/backport-0001-CVE-2025-27221.patch deleted file mode 100644 index d1c20a28b25fc026e86b7ca76c86a4a3e4b17854..0000000000000000000000000000000000000000 --- a/backport-0001-CVE-2025-27221.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 4263c0d15a582b46d75aac57cd26a47d33941a53 Mon Sep 17 00:00:00 2001 -From: Hiroshi SHIBATA -Date: Fri, 21 Feb 2025 16:29:36 +0900 -Subject: [PATCH] Truncate userinfo with URI#join, URI#merge and URI#+ - ---- - lib/uri/generic.rb | 6 +++++- - test/uri/test_generic.rb | 11 +++++++++++ - 2 files changed, 16 insertions(+), 1 deletion(-) - -diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb -index 69698c4..7d0b889 100644 ---- a/lib/uri/generic.rb -+++ b/lib/uri/generic.rb -@@ -1141,7 +1141,11 @@ module URI - end - - # RFC2396, Section 5.2, 7) -- base.set_userinfo(rel.userinfo) if rel.userinfo -+ if rel.userinfo -+ base.set_userinfo(rel.userinfo) -+ else -+ base.set_userinfo(nil) -+ end - base.set_host(rel.host) if rel.host - base.set_port(rel.port) if rel.port - base.query = rel.query if rel.query -diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb -index 3897c3d..30f9cbf 100644 ---- a/test/uri/test_generic.rb -+++ b/test/uri/test_generic.rb -@@ -164,6 +164,17 @@ class URI::TestGeneric < Test::Unit::TestCase - # must be empty string to identify as path-abempty, not path-absolute - assert_equal('', url.host) - assert_equal('http:////example.com', url.to_s) -+ -+ # sec-2957667 -+ url = URI.parse('http://user:pass@example.com').merge('//example.net') -+ assert_equal('http://example.net', url.to_s) -+ assert_nil(url.userinfo) -+ url = URI.join('http://user:pass@example.com', '//example.net') -+ assert_equal('http://example.net', url.to_s) -+ assert_nil(url.userinfo) -+ url = URI.parse('http://user:pass@example.com') + '//example.net' -+ assert_equal('http://example.net', url.to_s) -+ assert_nil(url.userinfo) - end - - def test_parse_scheme_with_symbols --- -2.33.0 - - diff --git a/backport-0001-CVE-2025-43857.patch b/backport-0001-CVE-2025-43857.patch new file mode 100644 index 0000000000000000000000000000000000000000..08eff9639f8874f58a35cbcc59a237a2d3879075 --- /dev/null +++ b/backport-0001-CVE-2025-43857.patch @@ -0,0 +1,116 @@ +From 53ceba1e97cbc3ac4d141077732178cc8bc79476 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 19 Apr 2025 22:21:59 -0400 +Subject: [PATCH 20/23] =?UTF-8?q?=E2=9C=A8=20Limit=20max=20response=20size?= + =?UTF-8?q?=20to=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. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb | 33 ++++++++++++++++++++++ + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 31 +++++++++++++++++++-- + 2 files changed, 62 insertions(+), 2 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb +index b353756..dcb5091 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.8/lib/net/imap/response_reader.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb +index 6a608b1..3c33dea 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.0 + diff --git a/backport-0002-CVE-2025-27221.patch b/backport-0002-CVE-2025-27221.patch deleted file mode 100644 index 84996c8f5ecbe9858b288f482edb7a4d4a2b08e1..0000000000000000000000000000000000000000 --- a/backport-0002-CVE-2025-27221.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 58adef476ef4b5e6deefaf92e7594ab29396c624 Mon Sep 17 00:00:00 2001 -From: Hiroshi SHIBATA -Date: Fri, 21 Feb 2025 18:16:28 +0900 -Subject: [PATCH] Fix merger of URI with authority component - -https://hackerone.com/reports/2957667 - -Co-authored-by: Nobuyoshi Nakada ---- - lib/uri/generic.rb | 19 +++++++------------ - test/uri/test_generic.rb | 7 +++++++ - 2 files changed, 14 insertions(+), 12 deletions(-) - -diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb -index 7d0b889..f7eed57 100644 ---- a/lib/uri/generic.rb -+++ b/lib/uri/generic.rb -@@ -1133,21 +1133,16 @@ module URI - base.fragment=(nil) - - # RFC2396, Section 5.2, 4) -- if !authority -- base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path -- else -- # RFC2396, Section 5.2, 4) -- base.set_path(rel.path) if rel.path -+ if authority -+ base.set_userinfo(rel.userinfo) -+ base.set_host(rel.host) -+ base.set_port(rel.port || base.default_port) -+ base.set_path(rel.path) -+ elsif base.path && rel.path -+ base.set_path(merge_path(base.path, rel.path)) - end - - # RFC2396, Section 5.2, 7) -- if rel.userinfo -- base.set_userinfo(rel.userinfo) -- else -- base.set_userinfo(nil) -- end -- base.set_host(rel.host) if rel.host -- base.set_port(rel.port) if rel.port - base.query = rel.query if rel.query - base.fragment=(rel.fragment) if rel.fragment - -diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb -index 30f9cbf..4b5e12c 100644 ---- a/test/uri/test_generic.rb -+++ b/test/uri/test_generic.rb -@@ -267,6 +267,13 @@ class URI::TestGeneric < Test::Unit::TestCase - assert_equal(u0, u1) - end - -+ def test_merge_authority -+ u = URI.parse('http://user:pass@example.com:8080') -+ u0 = URI.parse('http://new.example.org/path') -+ u1 = u.merge('//new.example.org/path') -+ assert_equal(u0, u1) -+ end -+ - def test_route - url = URI.parse('http://hoge/a.html').route_to('http://hoge/b.html') - assert_equal('b.html', url.to_s) --- -2.33.0 - - diff --git a/backport-0002-CVE-2025-43857.patch b/backport-0002-CVE-2025-43857.patch new file mode 100644 index 0000000000000000000000000000000000000000..e36ad07fe90ff5c53f0de31448f2bbe846758e2c --- /dev/null +++ b/backport-0002-CVE-2025-43857.patch @@ -0,0 +1,74 @@ +From 158cfdff54f3961b0ec628136444e3b0b0bb1736 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sun, 20 Apr 2025 17:38:43 -0400 +Subject: [PATCH 21/23] =?UTF-8?q?=E2=9C=A8=20Make=20max=5Fresponse=5Fsize?= + =?UTF-8?q?=20configurable=20[=F0=9F=9A=A7=20partial]?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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.8/lib/net/imap.rb | 15 +++++ + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 2 +- + 2 files changed, 16 insertions(+), 1 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 9560d07..954dffa 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -159,6 +159,10 @@ module Net + # + # Use paginated or limited versions of commands whenever possible. + # ++ # Use Config#max_response_size to impose a limit on incoming server responses ++ # as they are being read. This is especially important for untrusted ++ # servers. ++ # + # Use #add_response_handler to handle responses after each one is received. + # Use the +response_handlers+ argument to ::new to assign response handlers + # before the receiver thread is started. +@@ -800,6 +804,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.8/lib/net/imap/response_reader.rb b/lib/net/imap/response_reader.rb +index 3c33dea..bcf3601 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.0 + diff --git a/backport-0003-CVE-2025-43857.patch b/backport-0003-CVE-2025-43857.patch new file mode 100644 index 0000000000000000000000000000000000000000..80b03d7b8171455b690d4e2e241624208856b9c6 --- /dev/null +++ b/backport-0003-CVE-2025-43857.patch @@ -0,0 +1,61 @@ +From ae0fa010bb5e3c95b9beee31af607d4dba619d63 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sun, 20 Apr 2025 21:01:48 -0400 +Subject: [PATCH 22/23] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20compatibilit?= + =?UTF-8?q?y=20with=20ruby=202.7?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 20 +++++++++++--------- + 1 files changed, 11 insertions(+), 9 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb b/lib/net/imap/response_reader.rb +index bcf3601..fd7561f 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.0 + diff --git a/backport-0004-CVE-2025-43857.patch b/backport-0004-CVE-2025-43857.patch new file mode 100644 index 0000000000000000000000000000000000000000..e244db1df4734a840f7da8c9af681750960d48de --- /dev/null +++ b/backport-0004-CVE-2025-43857.patch @@ -0,0 +1,122 @@ +From e0059251e854cb03d5209c682ba3484fcb6953cd Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Wed, 9 Apr 2025 09:54:51 -0400 +Subject: [PATCH 23/23] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20to=20not-ima?= + =?UTF-8?q?p=200.3=20and=20ruby=202.6?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. +* `max_response_size` needed to be on Net::IMAP directly, because Config + was added for v0.4. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 49 +++++++-- + .bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb | 1 + + 2 files changed, 38 insertions(+), 12 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 954dffa..283fee2 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -159,7 +159,7 @@ module Net + # + # Use paginated or limited versions of commands whenever possible. + # +- # Use Config#max_response_size to impose a limit on incoming server responses ++ # Use #max_response_size to impose a limit on incoming server responses + # as they are being read. This is especially important for untrusted + # servers. + # +@@ -776,6 +776,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. +@@ -804,17 +838,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 +@@ -2059,6 +2082,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: + # +@@ -2089,6 +2113,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.8/lib/net/imap/errors.rb b/lib/net/imap/errors.rb +index dcb5091..52cb936 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/errors.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.0 + diff --git a/backport-Add-docs-for-receiver-thread-server-responses.patch b/backport-Add-docs-for-receiver-thread-server-responses.patch new file mode 100644 index 0000000000000000000000000000000000000000..bad13c2b14eab947b156744b7c5c3c0879857485 --- /dev/null +++ b/backport-Add-docs-for-receiver-thread-server-responses.patch @@ -0,0 +1,67 @@ +From 898ef8ab7c4daaf2fff471f8c88a94476f5252fa Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 22 Mar 2025 14:31:32 -0400 +Subject: [PATCH 03/23] =?UTF-8?q?=F0=9F=93=9A=20Add=20docs=20for=20receive?= + =?UTF-8?q?r=20thread=20&=20server=20responses?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Most importantly, this documents the scenarios that need extra care to +avoid memory leaks: +* Commands such as #list or #fetch can have an enormous number of + responses. +* Commands such as #fetch can result in an enormous size per response. +* Long-lived connections will gradually accumulate unsolicited server + responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses. +* A buggy or untrusted server could send inappropriate responses, which + could be very numerous, very large, and very rapid. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +index a66049e..53bbb3f 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -132,6 +132,37 @@ module Net + # + # This script invokes the FETCH command and the SEARCH command concurrently. + # ++ # When running multiple commands, care must be taken to avoid ambiguity. For ++ # example, SEARCH responses are ambiguous about which command they are ++ # responding to, so search commands should not run simultaneously, unless the ++ # server supports +ESEARCH+ {[RFC4731]}[https://rfc-editor.org/rfc/rfc4731] or ++ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051]. See {RFC9051 ++ # ยง5.5}[https://www.rfc-editor.org/rfc/rfc9051.html#section-5.5] for ++ # other examples of command sequences which should not be pipelined. ++ # ++ # == Unbounded memory use ++ # ++ # Net::IMAP reads server responses in a separate receiver thread per client. ++ # Unhandled response data is saved to #responses, and response_handlers run ++ # inside the receiver thread. See the list of methods for {handling server ++ # responses}[rdoc-ref:Net::IMAP@Handling+server+responses], below. ++ # ++ # Because the receiver thread continuously reads and saves new responses, some ++ # scenarios must be careful to avoid unbounded memory use: ++ # ++ # * Commands such as #list or #fetch can have an enormous number of responses. ++ # * Commands such as #fetch can result in an enormous size per response. ++ # * Long-lived connections will gradually accumulate unsolicited server ++ # responses, especially +EXISTS+, +FETCH+, and +EXPUNGE+ responses. ++ # * A buggy or untrusted server could send inappropriate responses, which ++ # could be very numerous, very large, and very rapid. ++ # ++ # Use paginated or limited versions of commands whenever possible. ++ # ++ # Use #add_response_handler to handle responses after each one is received. ++ # Use #extract_responses, #clear_responses, or #responses (with a block) to ++ # prune responses. ++ # + # == Errors + # + # An \IMAP server can send three different types of responses to indicate +-- +2.27.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 0000000000000000000000000000000000000000..e5c43e23ae1a9fe0d439f958e49ecb21d05c952a --- /dev/null +++ b/backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch @@ -0,0 +1,71 @@ +From 624f6da3323f7316cc99a4e4c5f1a3a931546a40 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 22 Mar 2025 14:53:00 -0400 +Subject: [PATCH 06/23] =?UTF-8?q?=E2=9C=A8=20Add=20`response=5Fhandlers`?= + =?UTF-8?q?=20kwarg=20to=20Net::IMAP.new?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This ensures every server response is handled, including the greeting. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 15 ++++++++ + 1 files changed, 15 insertions(+) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +index 0fa91c8..96eef1e 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -160,6 +160,8 @@ module Net + # Use paginated or limited versions of commands whenever possible. + # + # Use #add_response_handler to handle responses after each one is received. ++ # Use the +response_handlers+ argument to ::new to assign response handlers ++ # before the receiver thread is started. + # + # == Errors + # +@@ -1994,6 +1996,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) +@@ -2029,6 +2036,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: + # +@@ -2071,6 +2084,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 +@@ -2087,6 +2101,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.27.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 0000000000000000000000000000000000000000..8bf3bea2f2b6ebb4356a5710b440db4ff653a8c7 --- /dev/null +++ b/backport-Allocate-string-literals-with-specific-capacity.patch @@ -0,0 +1,32 @@ +From f34e410cf00c648ba5e52ba4999cf4803d9e9bec Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 13:44:50 -0400 +Subject: [PATCH 10/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Allocate=20string=20?= + =?UTF-8?q?literals=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. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index c09d174..77917af 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -2245,7 +2245,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.27.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 0000000000000000000000000000000000000000..098bd92d6f2e9b54743f99f5f6c6162c23c39bed --- /dev/null +++ b/backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch @@ -0,0 +1,33 @@ +From 4b64a5da564edaf39b1023adc58dd2322f85b1d5 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 13:08:21 -0400 +Subject: [PATCH 08/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Anchor=20literal=20r?= + =?UTF-8?q?egexp=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. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index a829315..452f86b 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -2230,7 +2230,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.27.0 + diff --git a/backport-CVE-2025-27219.patch b/backport-CVE-2025-27219.patch deleted file mode 100644 index 4a9aa70233676f226a9529e001f9bbf52cbb73fa..0000000000000000000000000000000000000000 --- a/backport-CVE-2025-27219.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 2c2d89e7cce0c81d9e63bb29c0e65b0436885af1 Mon Sep 17 00:00:00 2001 -From: Hiroshi SHIBATA -Date: Fri, 21 Feb 2025 16:01:17 +0900 -Subject: [PATCH 1/2] Use String#concat instead of String#+ for reducing cpu - usage - -Co-authored-by: "Yusuke Endoh" ---- - lib/cgi/cookie.rb | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb -index 9498e2f..1c4ef6a 100644 ---- a/lib/cgi/cookie.rb -+++ b/lib/cgi/cookie.rb -@@ -190,9 +190,10 @@ class CGI - values ||= "" - values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) } - if cookies.has_key?(name) -- values = cookies[name].value + values -+ cookies[name].concat(values) -+ else -+ cookies[name] = Cookie.new(name, *values) - end -- cookies[name] = Cookie.new(name, *values) - end - - cookies --- -2.33.0 - - diff --git a/backport-CVE-2025-27220.patch b/backport-CVE-2025-27220.patch deleted file mode 100644 index 0a7e6822a96f91c296fb6b5e1ac84736ff5c78a4..0000000000000000000000000000000000000000 --- a/backport-CVE-2025-27220.patch +++ /dev/null @@ -1,73 +0,0 @@ -From da7aadf928d85ffdf594d7e77aed4a441f7c3ebb Mon Sep 17 00:00:00 2001 -From: Hiroshi SHIBATA -Date: Fri, 21 Feb 2025 15:53:31 +0900 -Subject: [PATCH 2/2] Escape/unescape unclosed tags as well - -Co-authored-by: Nobuyoshi Nakada ---- - lib/cgi/util.rb | 4 ++-- - test/cgi/test_cgi_util.rb | 18 ++++++++++++++++++ - 2 files changed, 20 insertions(+), 2 deletions(-) - -diff --git a/lib/cgi/util.rb b/lib/cgi/util.rb -index 5a5c77a..ce77a0c 100644 ---- a/lib/cgi/util.rb -+++ b/lib/cgi/util.rb -@@ -178,7 +178,7 @@ module CGI::Util - def escapeElement(string, *elements) - elements = elements[0] if elements[0].kind_of?(Array) - unless elements.empty? -- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do -+ string.gsub(/<\/?(?:#{elements.join("|")})\b[^<>]*+>?/im) do - CGI.escapeHTML($&) - end - else -@@ -198,7 +198,7 @@ module CGI::Util - def unescapeElement(string, *elements) - elements = elements[0] if elements[0].kind_of?(Array) - unless elements.empty? -- string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do -+ string.gsub(/<\/?(?:#{elements.join("|")})\b(?>[^&]+|&(?![gl]t;)\w+;)*(?:>)?/im) do - unescapeHTML($&) - end - else -diff --git a/test/cgi/test_cgi_util.rb b/test/cgi/test_cgi_util.rb -index a3be193..d058ccc 100644 ---- a/test/cgi/test_cgi_util.rb -+++ b/test/cgi/test_cgi_util.rb -@@ -244,6 +244,14 @@ class CGIUtilTest < Test::Unit::TestCase - assert_equal("
<A HREF="url"></A>", escapeElement('
', ["A", "IMG"])) - assert_equal("
<A HREF="url"></A>", escape_element('
', "A", "IMG")) - assert_equal("
<A HREF="url"></A>", escape_element('
', ["A", "IMG"])) -+ -+ assert_equal("<A <A HREF="url"></A>", escapeElement('', "A", "IMG")) -+ assert_equal("<A <A HREF="url"></A>", escapeElement('', ["A", "IMG"])) -+ assert_equal("<A <A HREF="url"></A>", escape_element('', "A", "IMG")) -+ assert_equal("<A <A HREF="url"></A>", escape_element('', ["A", "IMG"])) -+ -+ assert_equal("<A <A ", escapeElement('', unescapeElement(escapeHTML('
'), ["A", "IMG"])) - assert_equal('<BR>', unescape_element(escapeHTML('
'), "A", "IMG")) - assert_equal('<BR>', unescape_element(escapeHTML('
'), ["A", "IMG"])) -+ -+ assert_equal('', unescapeElement(escapeHTML(''), "A", "IMG")) -+ assert_equal('', unescapeElement(escapeHTML(''), ["A", "IMG"])) -+ assert_equal('', unescape_element(escapeHTML(''), "A", "IMG")) -+ assert_equal('', unescape_element(escapeHTML(''), ["A", "IMG"])) -+ -+ assert_equal(' +Date: Sun, 20 Apr 2025 18:18:23 -0400 +Subject: [PATCH 04/23] =?UTF-8?q?=F0=9F=93=9A=20Delete=20backported=20docs?= + =?UTF-8?q?=20that=20are=20only=20for=20v0.4+?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +index 53bbb3f..0fa91c8 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -160,8 +160,6 @@ module Net + # Use paginated or limited versions of commands whenever possible. + # + # Use #add_response_handler to handle responses after each one is received. +- # Use #extract_responses, #clear_responses, or #responses (with a block) to +- # prune responses. + # + # == Errors + # +-- +2.27.0 + diff --git a/backport-Document-connection-state-more-consistently.patch b/backport-Document-connection-state-more-consistently.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9546a765b0cebb1aef124c5323d8bbf2da6cf05 --- /dev/null +++ b/backport-Document-connection-state-more-consistently.patch @@ -0,0 +1,127 @@ +From a9d117342265f6e962c931eee7264b355bb7b6d0 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Fri, 7 Mar 2025 10:41:42 -0500 +Subject: [PATCH 02/23] =?UTF-8?q?=F0=9F=93=9A=20Document=20connection=20st?= + =?UTF-8?q?ate=20more=20consistently?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Because a `#connection_state` attribute will be added, I'd like to +consistently name the connection states everywhere they are used. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 43 ++++++++++++++++++++++++------------------- + 1 file changed, 24 insertions(+), 19 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +index 2abf840..a66049e 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -45,10 +45,16 @@ module Net + # To work on the messages within a mailbox, the client must + # first select that mailbox, using either #select or #examine + # (for read-only access). Once the client has successfully +- # selected a mailbox, they enter the "_selected_" state, and that ++ # selected a mailbox, they enter the +selected+ state, and that + # mailbox becomes the _current_ mailbox, on which mail-item + # related commands implicitly operate. + # ++ # === Connection state ++ # ++ # Once an IMAP connection is established, the connection is in one of four ++ # states: not authenticated, +authenticated+, +selected+, and ++ # +logout+. Most commands are valid only in certain states. ++ # + # === Sequence numbers and UIDs + # + # Messages have two sorts of identifiers: message sequence +@@ -187,7 +193,7 @@ module Net + # - Net::IMAP.new: A new client connects immediately and waits for a + # successful server greeting before returning the new client object. + # - #starttls: Asks the server to upgrade a clear-text connection to use TLS. +- # - #logout: Tells the server to end the session. Enters the "_logout_" state. ++ # - #logout: Tells the server to end the session. Enters the +logout+ state. + # - #disconnect: Disconnects the connection (without sending #logout first). + # - #disconnected?: True if the connection has been closed. + # +@@ -230,40 +236,39 @@ module Net + # Capabilities may change after #starttls, #authenticate, or #login + # and cached capabilities must be reloaded. + # - #noop: Allows the server to send unsolicited untagged #responses. +- # - #logout: Tells the server to end the session. Enters the "_logout_" state. ++ # - #logout: Tells the server to end the session. Enters the +logout+ state. + # + # ==== \IMAP commands for the "Not Authenticated" state + # +- # In addition to the universal commands, the following commands are valid in +- # the "not authenticated" state: ++ # In addition to the commands for any state, the following commands are valid ++ # in the +not_authenticated+ state: + # + # - #starttls: Upgrades a clear-text connection to use TLS. + # + # Requires the +STARTTLS+ capability. +- # - #authenticate: Identifies the client to the server using a {SASL +- # mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]. +- # Enters the "_authenticated_" state. ++ # - #authenticate: Identifies the client to the server using the given {SASL ++ # mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml] ++ # and credentials. Enters the +authenticated+ state. + # + # Requires the AUTH=#{mechanism} capability for the chosen + # mechanism. + # - #login: Identifies the client to the server using a plain text password. +- # Using #authenticate is generally preferred. Enters the "_authenticated_" +- # state. ++ # Using #authenticate is preferred. Enters the +authenticated+ state. + # + # The +LOGINDISABLED+ capability must NOT be listed. + # + # ==== \IMAP commands for the "Authenticated" state + # +- # In addition to the universal commands, the following commands are valid in +- # the "_authenticated_" state: ++ # In addition to the commands for any state, the following commands are valid ++ # in the +authenticated+ state: + # + #-- + # - #enable: Not implemented by Net::IMAP, yet. + # + # Requires the +ENABLE+ capability. + #++ +- # - #select: Open a mailbox and enter the "_selected_" state. +- # - #examine: Open a mailbox read-only, and enter the "_selected_" state. ++ # - #select: Open a mailbox and enter the +selected+ state. ++ # - #examine: Open a mailbox read-only, and enter the +selected+ state. + # - #create: Creates a new mailbox. + # - #delete: Permanently remove a mailbox. + # - #rename: Change the name of a mailbox. +@@ -289,12 +294,12 @@ module Net + # + # ==== \IMAP commands for the "Selected" state + # +- # In addition to the universal commands and the "authenticated" commands, the +- # following commands are valid in the "_selected_" state: ++ # In addition to the commands for any state and the +authenticated+ ++ # commands, the following commands are valid in the +selected+ state: + # +- # - #close: Closes the mailbox and returns to the "_authenticated_" state, ++ # - #close: Closes the mailbox and returns to the +authenticated+ state, + # expunging deleted messages, unless the mailbox was opened as read-only. +- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state, ++ # - #unselect: Closes the mailbox and returns to the +authenticated+ state, + # without expunging any messages. + # + # Requires the +UNSELECT+ capability. +@@ -384,7 +389,7 @@ module Net + # ==== RFC3691: +UNSELECT+ + # Folded into IMAP4rev2[https://tools.ietf.org/html/rfc9051], so it is also + # listed with {Core IMAP commands}[rdoc-ref:Net::IMAP@Core+IMAP+commands]. +- # - #unselect: Closes the mailbox and returns to the "_authenticated_" state, ++ # - #unselect: Closes the mailbox and returns to the +authenticated+ state, + # without expunging any messages. + # + # ==== RFC4314: +ACL+ +-- +2.27.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 0000000000000000000000000000000000000000..e8f66501b64e4631a7b32cf2871a7ce1cb7a8016 --- /dev/null +++ b/backport-Explicitly-throw-eof-for-EOF-in-get_response.patch @@ -0,0 +1,57 @@ +From 7f23825e36676f83d06b4527b5c091dc1462e8d4 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 14:00:40 -0400 +Subject: [PATCH 12/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Explicitly=20"throw?= + =?UTF-8?q?=20:eof"=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. +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 9a432f6..1370317 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -2226,10 +2226,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 +@@ -2237,13 +2239,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 0000000000000000000000000000000000000000..752f463fb8fe578c51c91afaa3a0a014cb956c09 --- /dev/null +++ b/backport-Extract-ResponseReader-from-get_response.patch @@ -0,0 +1,127 @@ +From 8e2e403293287f9a7bf5341094be6206a745754f Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Wed, 2 Apr 2025 20:50:15 -0400 +Subject: [PATCH 13/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20ResponseRe?= + =?UTF-8?q?ader=20from=20get=5Fresponse?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's nice to extract a little bit of the complexity from the core +`Net::IMAP` 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.8/lib/net/imap.rb | 24 +++---------- + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 38 +++++++++++++++++++++ + 2 files changed, 43 insertions(+), 19 deletions(-) + create mode 100644 .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 1370317..9560d07 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -737,6 +737,8 @@ module Net + class IMAP < Protocol + VERSION = "0.3.8" + ++ autoload :ResponseReader, File.expand_path("imap/response_reader", __dir__) ++ + include MonitorMixin + if defined?(OpenSSL::SSL) + include OpenSSL +@@ -2074,6 +2076,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]) +@@ -2225,30 +2228,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) +@@ -2428,6 +2413,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.8/lib/net/imap/response_reader.rb b/lib/net/imap/response_reader.rb +new file mode 100644 +index 0000000..57770e3 +--- /dev/null ++++ b/.bundle/gems/net-imap-0.3.8/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 0000000000000000000000000000000000000000..12ac6d897d9d6eaa2d4d372a706fd7ce9dc03d27 --- /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 09/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Extract=20line=20and?= + =?UTF-8?q?=20literal=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.8/lib/net/imap.rb | 19 ++++++++++++++----- + 1 file changed, 14 insertions(+), 5 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 452f86b..c09d174 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/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 0000000000000000000000000000000000000000..2de19b5069b734727fbc80ca527df7b78930ae8c --- /dev/null +++ b/backport-Fix-backport-compatibility-with-ruby-2.7.patch @@ -0,0 +1,29 @@ +From b0a6039e8edf83c3871397cd11acb01236336d8c Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Sat, 19 Apr 2025 21:30:58 -0400 +Subject: [PATCH 19/23] =?UTF-8?q?=E2=9C=85=20Fix=20backport=20compatibilit?= + =?UTF-8?q?y=20with=20ruby=202.7?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb b/lib/net/imap/response_reader.rb +index 7d3a536..6a608b1 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.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 0000000000000000000000000000000000000000..8e3a9412fe090d0f04fa11487ca42e7b418ea117 --- /dev/null +++ b/backport-Reformat-get_response-debug-trace-printing.patch @@ -0,0 +1,33 @@ +From 3446447037c76c2335e657b023c60db0ed62ec8a Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Mon, 24 Mar 2025 17:35:10 -0400 +Subject: [PATCH 07/23] =?UTF-8?q?=F0=9F=8E=A8=20Reformat=20get=5Fresponse?= + =?UTF-8?q?=20debug=20trace=20printing?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 96eef1e..a829315 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -2238,10 +2238,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.27.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 0000000000000000000000000000000000000000..89d7a73b241d7ee657bc2b57ab512df368a42219 --- /dev/null +++ b/backport-Save-ResponseReader-ivars-buff-literal_size.patch @@ -0,0 +1,65 @@ +From 829532458a0ed25ec9c96fe8be50e3a73ce45ac9 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Mon, 14 Apr 2025 09:23:58 -0400 +Subject: [PATCH 17/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Save=20ResponseReade?= + =?UTF-8?q?r=20ivars:=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" +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb | 20 ++++++++++++++------ + 1 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb b/lib/net/imap/response_reader.rb +index 57770e3..7d3a536 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_reader.rb ++++ b/.bundle/gems/net-imap-0.3.8/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.27.0 + diff --git a/backport-Simplify-get_response-loop-further.patch b/backport-Simplify-get_response-loop-further.patch new file mode 100644 index 0000000000000000000000000000000000000000..89ccefebc7e3e5b6a0eaa1bb13b0d40e997cdcb5 --- /dev/null +++ b/backport-Simplify-get_response-loop-further.patch @@ -0,0 +1,34 @@ +From 17aa0c53f49efed10891139e5fd802581dffb54b Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Tue, 25 Mar 2025 12:46:59 -0400 +Subject: [PATCH 11/23] =?UTF-8?q?=F0=9F=8E=A8=20Simplify=20get=5Fresponse?= + =?UTF-8?q?=20loop=20further?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap.rb | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb b/lib/net/imap.rb +index 77917af..9a432f6 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap.rb +@@ -2228,11 +2228,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/backport-Use-Range-size-vs-Range-count-for-uid-set-limit.patch b/backport-Use-Range-size-vs-Range-count-for-uid-set-limit.patch new file mode 100644 index 0000000000000000000000000000000000000000..68cff328316dc1d3a81791e7fe3bd788879026b6 --- /dev/null +++ b/backport-Use-Range-size-vs-Range-count-for-uid-set-limit.patch @@ -0,0 +1,35 @@ +From c49ff9ee09bb96afc483315ea7f1913879f44538 Mon Sep 17 00:00:00 2001 +From: nick evans +Date: Wed, 26 Feb 2025 15:10:09 -0500 +Subject: [PATCH 01/23] =?UTF-8?q?=F0=9F=90=9B=20Use=20Range#size=20vs=20Ra?= + =?UTF-8?q?nge#count=20for=20uid-set=20limit?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Prior to ruby 3.3, `Range#count` is handled by `Enumerable#count`, even +for numeric ranges. For large ranges, `Range#size` is _significantly_ +faster. On my system, ruby 3.2 took 54 seconds (at 100% CPU) to run +`(1...2**32).count`. + +Thanks to @xiaoge1001 for reporting this issue (#410). +--- + .bundle/gems/net-imap-0.3.8/lib/net/imap/response_parser.rb | 2 +- + 1 files changed, 1 insertions(+), 1 deletion(-) + +diff --git a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_parser.rb b/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_parser.rb +index 0341356..5317bfc 100644 +--- a/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_parser.rb ++++ b/.bundle/gems/net-imap-0.3.8/lib/net/imap/response_parser.rb +@@ -1382,7 +1382,7 @@ module Net + when T_NUMBER then [Integer(token.value)] + when T_ATOM + entries = uid_set__ranges(token.value) +- if (count = entries.sum(&:count)) > MAX_UID_SET_SIZE ++ if (count = entries.sum(&:size)) > MAX_UID_SET_SIZE + parse_error("uid-set is too large: %d > 10k", count) + end + entries.flat_map(&:to_a) +-- +2.27.0 + diff --git a/net-imap-0.3.8.gem b/net-imap-0.3.8.gem deleted file mode 100644 index 5567bb060cc5252cfe1f3a9ca26043b68e4cf54b..0000000000000000000000000000000000000000 Binary files a/net-imap-0.3.8.gem and /dev/null differ diff --git a/ruby-3.2.7.tar.xz b/ruby-3.2.7.tar.xz deleted file mode 100644 index 6fa41385c10d5197e3d609123b520951521fcd01..0000000000000000000000000000000000000000 --- a/ruby-3.2.7.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc159b0d4a8ce412948fb69e61493839a0b3e1d5c919180f27036f1c948cfbe2 -size 15128228 diff --git a/ruby-3.2.8.tar.xz b/ruby-3.2.8.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..7733d1f3336a66b8b4939fef8e08b4f1cb364674 Binary files /dev/null and b/ruby-3.2.8.tar.xz differ diff --git a/ruby.spec b/ruby.spec index fe954bad838bd152eb998e2fb13450adb7379d23..3e530dd0baa469d973c9b60b59f77b979eed106c 100644 --- a/ruby.spec +++ b/ruby.spec @@ -2,7 +2,7 @@ # https://bugzilla.redhat.com/show_bug.cgi?id=2043092 %undefine _package_note_flags -%global ruby_version 3.2.7 +%global ruby_version 3.2.8 # Bundled libraries versions %global rubygems_version 3.4.19 @@ -38,12 +38,12 @@ Name: ruby Version: %{ruby_version} -Release: 152 +Release: 153 Summary: Object-oriented scripting language interpreter License: (Ruby OR BSD-2-Clause) AND (Ruby OR BSD-2-Clause OR GPL-1.0-or-later) AND BSD-3-Clause AND (GPL-3.0-or-later WITH Bison-exception-2.2) AND ISC AND Public Domain AND MIT AND CC0 AND zlib AND Unicode-DFS-2015 URL: https://www.ruby-lang.org/en/ -Source0: https://cache.ruby-lang.org/pub/ruby/3.1/%{name}-%{version}.tar.xz +Source0: https://cache.ruby-lang.org/pub/ruby/3.2/%{name}-%{version}.tar.xz Source1: operating_system.rb Source2: libruby.stp Source3: ruby-exercise.stp @@ -60,9 +60,6 @@ Source13: test_systemtap.rb %{load:%{SOURCE4}} %{load:%{SOURCE5}} -# Separated source for security updates -Source6001: https://rubygems.org/downloads/net-imap-%{net_imap_version}.gem - # Fix ruby_version abuse. # https://bugs.ruby-lang.org/issues/11002 Patch0: ruby-2.3.0-ruby_version.patch @@ -91,22 +88,36 @@ Patch6: ruby-2.7.0-Initialize-ABRT-hook.patch # https://bugs.ruby-lang.org/issues/16492 Patch19: ruby-2.7.1-Timeout-the-test_bug_reporter_add-witout-raising-err.patch -Patch6003: backport-CVE-2019-19204.patch -Patch6004: backport-CVE-2019-19246.patch -Patch6005: backport-CVE-2019-16161.patch -Patch6006: backport-CVE-2019-16162.patch -Patch6007: backport-CVE-2019-16163.patch -Patch6018: backport-rubygems-rubygems-Drop-to-support-Psych-3.0-bundled-.patch -Patch6019: backport-0001-CVE-2024-35221.patch -Patch6020: backport-0002-CVE-2024-35221.patch -Patch6021: backport-0003-CVE-2024-35221.patch -Patch6022: backport-0004-CVE-2024-35221.patch -Patch6023: backport-0005-CVE-2024-35221.patch -Patch6028: backport-CVE-2024-47220.patch -Patch6029: backport-CVE-2025-27219.patch -Patch6030: backport-CVE-2025-27220.patch -Patch6031: backport-0001-CVE-2025-27221.patch -Patch6032: backport-0002-CVE-2025-27221.patch +Patch6000: backport-CVE-2019-19204.patch +Patch6001: backport-CVE-2019-19246.patch +Patch6002: backport-CVE-2019-16161.patch +Patch6003: backport-CVE-2019-16162.patch +Patch6004: backport-CVE-2019-16163.patch +Patch6005: backport-rubygems-rubygems-Drop-to-support-Psych-3.0-bundled-.patch +Patch6006: backport-0001-CVE-2024-35221.patch +Patch6007: backport-0002-CVE-2024-35221.patch +Patch6008: backport-0003-CVE-2024-35221.patch +Patch6009: backport-0004-CVE-2024-35221.patch +Patch6010: backport-0005-CVE-2024-35221.patch +Patch6011: backport-CVE-2024-47220.patch +Patch6012: backport-Use-Range-size-vs-Range-count-for-uid-set-limit.patch +Patch6013: backport-Document-connection-state-more-consistently.patch +Patch6014: backport-Add-docs-for-receiver-thread-server-responses.patch +Patch6015: backport-Delete-backported-docs-that-are-only-for-v0.4.patch +Patch6016: backport-Add-response_handlers-kwarg-to-Net-IMAP.new.patch +Patch6017: backport-Reformat-get_response-debug-trace-printing.patch +Patch6018: backport-Anchor-literal-regexp-to-the-end-of-the-buffer.patch +Patch6019: backport-Extract-line-and-literal-parts-of-get_response.patch +Patch6020: backport-Allocate-string-literals-with-specific-capacity.patch +Patch6021: backport-Simplify-get_response-loop-further.patch +Patch6022: backport-Explicitly-throw-eof-for-EOF-in-get_response.patch +Patch6023: backport-Extract-ResponseReader-from-get_response.patch +Patch6024: backport-Save-ResponseReader-ivars-buff-literal_size.patch +Patch6025: backport-Fix-backport-compatibility-with-ruby-2.7.patch +Patch6026: backport-0001-CVE-2025-43857.patch +Patch6027: backport-0002-CVE-2025-43857.patch +Patch6028: backport-0003-CVE-2025-43857.patch +Patch6029: backport-0004-CVE-2025-43857.patch Provides: %{name}-libs = %{version}-%{release} Obsoletes: %{name}-libs < %{version}-%{release} @@ -380,18 +391,13 @@ needs to be listed in Gemfile to be used by Bundler. %prep %autosetup -n ruby-%{ruby_version} -p1 +find . -name net-imap.gemspec | xargs sed -i 's/spec.version = version/spec.version = "%{net_imap_version}"/' + rm -rf ext/psych/yaml rm -rf ext/fiddle/libffi* cp -a %{SOURCE3} . -# Update gems by replace it with downloaded gem -( -rm -f gems/net-imap*.gem -cp %{S:6001} gems/net-imap-%{net_imap_version}.gem -sed -i -e 's,net-imap 0.3.4.1,net-imap %{net_imap_version},' gems/bundled_gems -) - # test gems version earlier [ -f gems/debug-%{debug_version}.gem ] [ -f gems/matrix-%{matrix_version}.gem ] @@ -919,6 +925,10 @@ make runruby TESTRUN_SCRIPT=%{SOURCE13} %{gem_dir}/specifications/matrix-%{matrix_version}.gemspec %changelog +* Fri Jun 06 2025 shixuantong - 3.2.8-153 +- update ruby to 3.2.8 +- fix CVE-2025-43857 + * Sat Mar 29 2025 Funda Wang - 3.2.7-152 - disable package notes