Skip to content

Commit

Permalink
(PA-6507) Patch rexml for CVE-2024-35176
Browse files Browse the repository at this point in the history
 - Ruby 3.2.4 has rexml as its bundled gem, so the oatch was applied after the install step since bundled gems are available in the build after the install step in the .bundle folder.
 - Ruby 2.7.8 has rexml as its default gem, so we can go with the usual way to patch it.
 - Note that in the patch files for ruby_32, the file paths are prefixed with .bundle/gems/rexml-3.2.5 because that is where the bundled gem rexml is located with respect to the working directory of patching.
 - The upstream fix commit: ruby/rexml@4325835
  • Loading branch information
shubhamshinde360 committed Jul 10, 2024
1 parent 08c0261 commit 16d488b
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 0 deletions.
3 changes: 3 additions & 0 deletions configs/components/ruby-2.7.8.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@

pkg.apply_patch "#{base}/uri-redos-cve-2023-36617.patch"

# This patch is not required for ruby >= 3.3.3
pkg.apply_patch "#{base}/rexml_for_CVE-2024-35176.patch"

if platform.is_cross_compiled?
unless platform.is_macos?
pkg.apply_patch "#{base}/uri_generic_remove_safe_nav_operator_r2.5.patch"
Expand Down
14 changes: 14 additions & 0 deletions configs/components/ruby-3.2.4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,18 @@
]
end
end

#########
# BUILD
#########

pkg.add_source("file://resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch")

pkg.build do
# This patch is applied after the install step because rexml gem is the bundled gem hence build
# cannot find the path of the files to be patched prior to configuring and installing.
# This patch is not required for ruby >= 3.3.3
steps = ["#{platform.patch} --strip=1 --fuzz=0 --ignore-whitespace --no-backup-if-mismatch < ../rexml_for_CVE-2024-35176.patch"]
end

end
135 changes: 135 additions & 0 deletions resources/patches/ruby_27/rexml_for_CVE-2024-35176.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
commit e597f07718c27dc4414ad39f121376e53056475a
Author: Shubham Shinde <[email protected]>
Date: Tue Jul 9 19:40:43 2024 +0530

Read quoted attributes in chunks (#126)


# Conflicts:
# lib/rexml/parsers/baseparser.rb
# lib/rexml/source.rb

diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index f76aed0..f0b365d 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -518,25 +518,43 @@ module REXML
message = "Missing attribute equal: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- quote = scanner.scan(/['"]/)
- unless quote
+ unless match = @source.match(/(['"])/, true)
message = "Missing attribute value start quote: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
- match_data = @source.match(/^(.*?)(\/)?>/um, true)
- if match_data
- scanner << "/" if closed
- scanner << ">"
- scanner << match_data[1]
- scanner.pos = pos
- closed = !match_data[2].nil?
- next
- end
- message =
- "Missing attribute value end quote: <#{name}>: <#{quote}>"
+ quote = match[1]
+ value = @source.read_until(quote)
+ unless value.chomp!(quote)
+ message = "Missing attribute value end quote: <#{name}>: <#{quote}>"
raise REXML::ParseException.new(message, @source)
end
+ @source.match(/\s*/um, true)
+ if prefix == "xmlns"
+ if local_part == "xml"
+ if value != "http://www.w3.org/XML/1998/namespace"
+ msg = "The 'xml' prefix must not be bound to any other namespace "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self )
+ end
+ elsif local_part == "xmlns"
+ msg = "The 'xmlns' prefix must not be declared "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self)
+ end
+ curr_ns << local_part
+ elsif prefix
+ prefixes << prefix unless prefix == "xml"
+ end
+
+ if attributes[name]
+ msg = "Duplicate attribute #{name.inspect}"
+ raise REXML::ParseException.new(msg, @source, self)
+ end
+
+ attributes[name] = value
+ else
+ message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
+ raise REXML::ParseException.new(message, @source)
end
name = scanner[1]
prefix = scanner[2]
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index 770aefc..86bf6cf 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -81,7 +81,11 @@ module REXML
rv
end

- def read
+ def read(term = nil)
+ end
+
+ def read_until(term)
+ @scanner.scan_until(Regexp.union(term)) or @scanner.rest
end

def consume( pattern )
@@ -204,9 +208,9 @@ module REXML
rv
end

- def read
+ def read(term = nil)
begin
- @buffer << readline
+ @buffer << readline(term)
rescue Exception, NameError
@source = nil
end
@@ -216,6 +220,21 @@ module REXML
match( pattern, true )
end

+ def read_until(term)
+ pattern = Regexp.union(term)
+ data = []
+ begin
+ until str = @scanner.scan_until(pattern)
+ @scanner << readline(term)
+ end
+ rescue EOFError
+ @scanner.rest
+ else
+ read if @scanner.eos? and [email protected]?
+ str
+ end
+ end
+
def match( pattern, cons=false )
rv = pattern.match(@buffer)
@buffer = $' if cons and rv
@@ -263,8 +282,8 @@ module REXML
end

private
- def readline
- str = @source.readline(@line_break)
+ def readline(term = nil)
+ str = @source.readline(term || @line_break)
if @pending_buffer
if str.nil?
str = @pending_buffer
123 changes: 123 additions & 0 deletions resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
index 90b370b..373abcf 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/source.rb
@@ -81,7 +81,11 @@ module REXML
rv
end

- def read
+ def read(term = nil)
+ end
+
+ def read_until(term)
+ @scanner.scan_until(Regexp.union(term)) or @scanner.rest
end

def consume( pattern )
@@ -204,14 +208,29 @@ module REXML
rv
end

- def read
+ def read(term = nil)
begin
- @buffer << readline
+ @buffer << readline(term)
rescue Exception, NameError
@source = nil
end
end

+ def read_until(term)
+ pattern = Regexp.union(term)
+ data = []
+ begin
+ until str = @scanner.scan_until(pattern)
+ @scanner << readline(term)
+ end
+ rescue EOFError
+ @scanner.rest
+ else
+ read if @scanner.eos? and [email protected]?
+ str
+ end
+ end
+
def consume( pattern )
match( pattern, true )
end
@@ -263,8 +282,8 @@ module REXML
end

private
- def readline
- str = @source.readline(@line_break)
+ def readline(term = nil)
+ str = @source.readline(term || @line_break)
if @pending_buffer
if str.nil?
str = @pending_buffer
diff --git a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
index 305b120..7001bb2 100644
--- a/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
+++ b/.bundle/gems/rexml-3.2.5/lib/rexml/parsers/baseparser.rb
@@ -618,25 +618,43 @@ module REXML
message = "Missing attribute equal: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- quote = scanner.scan(/['"]/)
- unless quote
+ unless match = @source.match(/(['"])/, true)
message = "Missing attribute value start quote: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- unless scanner.scan(/.*#{Regexp.escape(quote)}/um)
- match_data = @source.match(/^(.*?)(\/)?>/um, true)
- if match_data
- scanner << "/" if closed
- scanner << ">"
- scanner << match_data[1]
- scanner.pos = pos
- closed = !match_data[2].nil?
- next
- end
- message =
- "Missing attribute value end quote: <#{name}>: <#{quote}>"
+ quote = match[1]
+ value = @source.read_until(quote)
+ unless value.chomp!(quote)
+ message = "Missing attribute value end quote: <#{name}>: <#{quote}>"
raise REXML::ParseException.new(message, @source)
end
+ @source.match(/\s*/um, true)
+ if prefix == "xmlns"
+ if local_part == "xml"
+ if value != "http://www.w3.org/XML/1998/namespace"
+ msg = "The 'xml' prefix must not be bound to any other namespace "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self )
+ end
+ elsif local_part == "xmlns"
+ msg = "The 'xmlns' prefix must not be declared "+
+ "(http://www.w3.org/TR/REC-xml-names/#ns-decl)"
+ raise REXML::ParseException.new( msg, @source, self)
+ end
+ curr_ns << local_part
+ elsif prefix
+ prefixes << prefix unless prefix == "xml"
+ end
+
+ if attributes[name]
+ msg = "Duplicate attribute #{name.inspect}"
+ raise REXML::ParseException.new(msg, @source, self)
+ end
+
+ attributes[name] = value
+ else
+ message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
+ raise REXML::ParseException.new(message, @source)
end
name = scanner[1]
prefix = scanner[2]

0 comments on commit 16d488b

Please sign in to comment.