Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package rubygem-rack-2.2 for
openSUSE:Factory checked in at 2026-04-08 17:17:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-rack-2.2 (Old)
and /work/SRC/openSUSE:Factory/.rubygem-rack-2.2.new.21863 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-rack-2.2"
Wed Apr 8 17:17:46 2026 rev:21 rq:1345170 version:2.2.23
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-rack-2.2/rubygem-rack-2.2.changes
2026-03-04 21:11:50.056833516 +0100
+++
/work/SRC/openSUSE:Factory/.rubygem-rack-2.2.new.21863/rubygem-rack-2.2.changes
2026-04-08 17:17:56.625382036 +0200
@@ -1,0 +2,15 @@
+Wed Apr 8 11:26:29 UTC 2026 - Saray Cabrera Padron <[email protected]>
+
+- Update to version 2.2.23
+
+ * [CVE-2026-34763] Root directory disclosure via unescaped regex
interpolation in Rack::Directory.
+ * [CVE-2026-34230] Avoid O(n^2) algorithm in
Rack::Utils.select_best_encoding which could lead to denial of service.
+ * [CVE-2026-26961] Raise error for multipart requests with multiple boundary
parameters.
+ * [CVE-2026-34786] Rack::Static header_rules bypass via URL-encoded path
mismatch.
+ * [CVE-2026-34831] Content-Length mismatch in Rack::Files error responses.
+ * [CVE-2026-34826] Multipart byte range processing allows denial of service
via excessive overlapping ranges.
+ * [CVE-2026-34830] Rack::Sendfile header-based X-Accel-Mapping regex
injection enables unauthorized X-Accel-Redirect.
+ * [CVE-2026-34785] Rack::Static prefix matching can expose unintended files
under the static root.
+ * [CVE-2026-34829] Multipart parsing without Content-Length header allows
unbounded chunked file uploads.
+
+-------------------------------------------------------------------
Old:
----
rack-2.2.22.gem
New:
----
rack-2.2.23.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-rack-2.2.spec ++++++
--- /var/tmp/diff_new_pack.2lJO5y/_old 2026-04-08 17:17:57.509418438 +0200
+++ /var/tmp/diff_new_pack.2lJO5y/_new 2026-04-08 17:17:57.513418602 +0200
@@ -1,7 +1,7 @@
#
# spec file for package rubygem-rack-2.2
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -24,7 +24,7 @@
#
Name: rubygem-rack-2.2
-Version: 2.2.22
+Version: 2.2.23
Release: 0
%define mod_name rack
%define mod_full_name %{mod_name}-%{version}
@@ -56,6 +56,7 @@
%install
%gem_install \
+ --no-rdoc --no-ri \
--symlink-binaries \
--doc-files="CHANGELOG.md CONTRIBUTING.md MIT-LICENSE README.rdoc" \
-f
++++++ rack-2.2.22.gem -> rack-2.2.23.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/CHANGELOG.md new/CHANGELOG.md
--- old/CHANGELOG.md 1980-01-02 01:00:00.000000000 +0100
+++ new/CHANGELOG.md 1980-01-02 01:00:00.000000000 +0100
@@ -2,7 +2,21 @@
All notable changes to this project will be documented in this file. For info
on how to format all future additions to this file please reference [Keep A
Changelog](https://keepachangelog.com/en/1.0.0/).
-## Unreleased
+## [2.2.23] - 2026-04-01
+
+### Security
+
+- [CVE-2026-34763](https://github.com/advisories/GHSA-7mqq-6cf9-v2qp) Root
directory disclosure via unescaped regex interpolation in `Rack::Directory`.
+- [CVE-2026-34230](https://github.com/advisories/GHSA-v569-hp3g-36wr) Avoid
O(n^2) algorithm in `Rack::Utils.select_best_encoding` which could lead to
denial of service.
+- [CVE-2026-26961](https://github.com/advisories/GHSA-vgpv-f759-9wx3) Raise
error for multipart requests with multiple boundary parameters.
+- [CVE-2026-34786](https://github.com/advisories/GHSA-q4qf-9j86-f5mh)
`Rack::Static` `header_rules` bypass via URL-encoded path mismatch.
+- [CVE-2026-34831](https://github.com/advisories/GHSA-q2ww-5357-x388)
`Content-Length` mismatch in `Rack::Files` error responses.
+- [CVE-2026-34826](https://github.com/advisories/GHSA-x8cg-fq8g-mxfx)
Multipart byte range processing allows denial of service via excessive
overlapping ranges.
+- [CVE-2026-34830](https://github.com/advisories/GHSA-qv7j-4883-hwh7)
`Rack::Sendfile` header-based `X-Accel-Mapping` regex injection enables
unauthorized `X-Accel-Redirect`.
+- [CVE-2026-34785](https://github.com/advisories/GHSA-h2jq-g4cq-5ppq)
`Rack::Static` prefix matching can expose unintended files under the static
root.
+- [CVE-2026-34829](https://github.com/advisories/GHSA-8vqr-qjwx-82mw)
Multipart parsing without `Content-Length` header allows unbounded chunked file
uploads.
+
+## [2.2.22] - 2026-02-16
### Security
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/directory.rb new/lib/rack/directory.rb
--- old/lib/rack/directory.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/directory.rb 1980-01-02 01:00:00.000000000 +0100
@@ -45,7 +45,7 @@
class DirectoryBody < Struct.new(:root, :path, :files)
# Yield strings for each part of the directory entry
def each
- show_path = Utils.escape_html(path.sub(/^#{root}/, ''))
+ show_path = Utils.escape_html(path.sub(/\A#{Regexp.escape(root)}/, ''))
yield(DIR_PAGE_HEADER % [ show_path, show_path ])
unless path.chomp('/') == root
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/files.rb new/lib/rack/files.rb
--- old/lib/rack/files.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/files.rb 1980-01-02 01:00:00.000000000 +0100
@@ -196,7 +196,7 @@
status,
{
CONTENT_TYPE => "text/plain",
- CONTENT_LENGTH => body.size.to_s,
+ CONTENT_LENGTH => body.bytesize.to_s,
"X-Cascade" => "pass"
}.merge!(headers),
[body]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/multipart/parser.rb
new/lib/rack/multipart/parser.rb
--- old/lib/rack/multipart/parser.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/multipart/parser.rb 1980-01-02 01:00:00.000000000 +0100
@@ -41,6 +41,10 @@
BUFFERED_UPLOAD_BYTESIZE_LIMIT =
env_int.call("RACK_MULTIPART_BUFFERED_UPLOAD_BYTESIZE_LIMIT", 16 * 1024 * 1024)
private_constant :BUFFERED_UPLOAD_BYTESIZE_LIMIT
+ bytesize_limit = env_int.call("RACK_MULTIPART_PARSER_BYTESIZE_LIMIT", 10
* 1024 * 1024 * 1024)
+ PARSER_BYTESIZE_LIMIT = bytesize_limit > 0 ? bytesize_limit : nil
+ private_constant :PARSER_BYTESIZE_LIMIT
+
class BoundedIO # :nodoc:
def initialize(io, content_length)
@io = io
@@ -81,7 +85,15 @@
return unless content_type
data = content_type.match(MULTIPART)
return unless data
- data[1]
+
+ unless data[1].empty?
+ raise EOFError, "whitespace between boundary parameter name and
equal sign"
+ end
+ if data.post_match =~ /boundary\s*=/i
+ raise EOFError, "multiple boundary parameters found in multipart
content type"
+ end
+
+ data[2]
end
def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)
@@ -90,6 +102,10 @@
boundary = parse_boundary content_type
return EMPTY unless boundary
+ if PARSER_BYTESIZE_LIMIT && content_length && content_length >
PARSER_BYTESIZE_LIMIT
+ raise EOFError, "multipart Content-Length #{content_length} exceeds
limit of #{PARSER_BYTESIZE_LIMIT} bytes"
+ end
+
io = BoundedIO.new(io, content_length) if content_length
outbuf = String.new
@@ -210,6 +226,7 @@
@mime_index = 0
@body_retained = nil
@retained_size = 0
+ @total_bytes_read = (0 if PARSER_BYTESIZE_LIMIT)
@collector = Collector.new tempfile
@sbuf = StringScanner.new("".dup)
@@ -221,6 +238,12 @@
def on_read(content)
handle_empty_content!(content)
+ if @total_bytes_read
+ @total_bytes_read += content.bytesize
+ if @total_bytes_read > PARSER_BYTESIZE_LIMIT
+ raise EOFError, "multipart upload exceeds limit of
#{PARSER_BYTESIZE_LIMIT} bytes"
+ end
+ end
@sbuf.concat content
run_parser
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/multipart.rb new/lib/rack/multipart.rb
--- old/lib/rack/multipart.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/multipart.rb 1980-01-02 01:00:00.000000000 +0100
@@ -12,7 +12,7 @@
EOL = "\r\n"
MULTIPART_BOUNDARY = "AaB03x"
- MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|ni
+ MULTIPART = %r|\Amultipart/.*?boundary(\s*)=\"?([^\";,]+)\"?|ni
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/sendfile.rb new/lib/rack/sendfile.rb
--- old/lib/rack/sendfile.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/sendfile.rb 1980-01-02 01:00:00.000000000 +0100
@@ -47,7 +47,7 @@
#
# The X-Accel-Mapping header should specify the location on the file system,
# followed by an equals sign (=), followed name of the private URL pattern
- # that it maps to. The middleware performs a simple substitution on the
+ # that it maps to. The middleware performs a case-insensitive substitution
on the
# resulting path.
#
# To enable X-Accel-Redirect, you must configure the middleware explicitly:
@@ -181,7 +181,7 @@
# Safe to use header: explicit config + no app mappings:
mapping.split(',').map(&:strip).each do |m|
internal, external = m.split('=', 2).map(&:strip)
- new_path = path.sub(/\A#{internal}/i, external)
+ new_path = path.sub(/\A#{Regexp.escape(internal)}/i, external)
return new_path unless path == new_path
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/static.rb new/lib/rack/static.rb
--- old/lib/rack/static.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/static.rb 1980-01-02 01:00:00.000000000 +0100
@@ -91,6 +91,9 @@
def initialize(app, options = {})
@app = app
@urls = options[:urls] || ["/favicon.ico"]
+ if @urls.kind_of?(Array)
+ @urls = @urls.map { |url| [url, url.end_with?('/') ? url :
"#{url}/".freeze].freeze }.freeze
+ end
@index = options[:index]
@gzip = options[:gzip]
@cascade = options[:cascade]
@@ -113,7 +116,7 @@
end
def route_file(path)
- @urls.kind_of?(Array) && @urls.any? { |url| path.index(url) == 0 }
+ @urls.kind_of?(Array) && @urls.any? { |url, url_slash| path == url ||
path.start_with?(url_slash) }
end
def can_serve(path)
@@ -165,6 +168,8 @@
# Convert HTTP header rules to HTTP headers
def applicable_rules(path)
+ path = ::Rack::Utils.unescape_path(path)
+
@header_rules.find_all do |rule, new_headers|
case rule
when :all
@@ -172,10 +177,9 @@
when :fonts
/\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
when String
- path = ::Rack::Utils.unescape(path)
path.start_with?(rule) || path.start_with?('/' + rule)
when Array
- /\.(#{rule.join('|')})\z/.match?(path)
+ /\.#{Regexp.union(rule)}\z/.match?(path)
when Regexp
rule.match?(path)
else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/utils.rb new/lib/rack/utils.rb
--- old/lib/rack/utils.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/utils.rb 1980-01-02 01:00:00.000000000 +0100
@@ -186,17 +186,41 @@
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
end
+ # Given an array of available encoding strings, and an array of
+ # acceptable encodings for a request, where each element of the
+ # acceptable encodings array is an array where the first element
+ # is an encoding name and the second element is the numeric
+ # priority for the encoding, return the available encoding with
+ # the highest priority.
+ #
+ # The accept_encoding argument is typically generated by calling
+ # Request#accept_encoding.
+ #
+ # Example:
+ #
+ # select_best_encoding(%w(compress gzip identity),
+ # [["compress", 0.5], ["gzip", 1.0]])
+ # # => "gzip"
+ #
+ # To reduce denial of service potential, only the first 16
+ # acceptable encodings are considered.
def select_best_encoding(available_encodings, accept_encoding)
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+ # Only process the first 16 encodings
+ accept_encoding = accept_encoding[0...16]
expanded_accept_encoding = []
+ wildcard_seen = false
accept_encoding.each do |m, q|
preference = available_encodings.index(m) || available_encodings.size
if m == "*"
- (available_encodings - accept_encoding.map(&:first)).each do |m2|
- expanded_accept_encoding << [m2, q, preference]
+ unless wildcard_seen
+ (available_encodings - accept_encoding.map(&:first)).each do |m2|
+ expanded_accept_encoding << [m2, q, preference]
+ end
+ wildcard_seen = true
end
else
expanded_accept_encoding << [m, q, preference]
@@ -204,7 +228,13 @@
end
encoding_candidates = expanded_accept_encoding
- .sort_by { |_, q, p| [-q, p] }
+ .sort do |(_, q1, p1), (_, q2, p2)|
+ if r = (q1 <=> q2).nonzero?
+ -r
+ else
+ (p1 <=> p2).nonzero? || 0
+ end
+ end
.map!(&:first)
unless encoding_candidates.include?("identity")
@@ -350,16 +380,17 @@
# Parses the "Range:" header, if present, into an array of Range objects.
# Returns nil if the header is missing or syntactically invalid.
# Returns an empty array if none of the ranges are satisfiable.
- def byte_ranges(env, size)
- warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if
$VERBOSE
- get_byte_ranges env['HTTP_RANGE'], size
+ def byte_ranges(env, size, max_ranges: 100)
+ get_byte_ranges env['HTTP_RANGE'], size, max_ranges: max_ranges
end
- def get_byte_ranges(http_range, size)
+ def get_byte_ranges(http_range, size, max_ranges: 100)
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
return nil unless http_range && http_range =~ /bytes=([^;]+)/
+ byte_range = $1
+ return nil if byte_range.count(',') >= max_ranges
ranges = []
- $1.split(/,\s*/).each do |range_spec|
+ byte_range.split(/,[ \t]*/).each do |range_spec|
return nil unless range_spec.include?('-')
range = range_spec.split('-')
r0, r1 = range[0], range[1]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rack/version.rb new/lib/rack/version.rb
--- old/lib/rack/version.rb 1980-01-02 01:00:00.000000000 +0100
+++ new/lib/rack/version.rb 1980-01-02 01:00:00.000000000 +0100
@@ -20,7 +20,7 @@
VERSION.join(".")
end
- RELEASE = "2.2.22"
+ RELEASE = "2.2.23"
# Return the Rack release as a dotted string.
def self.release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 1980-01-02 01:00:00.000000000 +0100
+++ new/metadata 1980-01-02 01:00:00.000000000 +0100
@@ -1,7 +1,7 @@
--- !ruby/object:Gem::Specification
name: rack
version: !ruby/object:Gem::Version
- version: 2.2.22
+ version: 2.2.23
platform: ruby
authors:
- Leah Neukirchen
@@ -182,7 +182,7 @@
- !ruby/object:Gem::Version
version: '0'
requirements: []
-rubygems_version: 4.0.3
+rubygems_version: 4.0.6
specification_version: 4
summary: A modular Ruby webserver interface.
test_files: []