Skip to content

Commit

Permalink
(PA-6507) Patch REXML for CVE-2024-35176 in ruby 2.7 and 3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
amitkarsale committed Jun 25, 2024
1 parent a39b23b commit 022e103
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 0 deletions.
2 changes: 2 additions & 0 deletions configs/components/ruby-2.7.8.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
pkg.apply_patch "#{base}/regexp_use_after_free.patch"

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


if platform.is_cross_compiled?
unless platform.is_macos?
Expand Down
3 changes: 3 additions & 0 deletions configs/components/ruby-3.2.4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@

base = 'resources/patches/ruby_32'

#this patch is not required in ruby >= 3.2.5
pkg.apply_patch "#{base}/rexml_for_CVE-2024-35176.patch"

if platform.is_cross_compiled?
pkg.apply_patch "#{base}/rbinstall_gem_path.patch"
end
Expand Down
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 @@
diff --git a/Gemfile b/Gemfile
index 042ef8ac..f78cc861 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,4 +10,5 @@ group :development do
gem "bundler"
gem "rake"
gem "test-unit"
+ gem "test-unit-ruby-core"
end
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 8d62391c..d09237c5 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -628,17 +628,17 @@ def parse_attributes(prefixes, curr_ns)
message = "Missing attribute equal: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- unless match = @source.match(/(['"])(.*?)\1\s*/um, true)
- if match = @source.match(/(['"])/, true)
- message =
- "Missing attribute value end quote: <#{name}>: <#{match[1]}>"
- raise REXML::ParseException.new(message, @source)
- else
- message = "Missing attribute value start quote: <#{name}>"
- raise REXML::ParseException.new(message, @source)
- end
+ unless match = @source.match(/(['"])/, true)
+ message = "Missing attribute value start quote: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ 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
- value = match[2]
+ @source.match(/\s*/um, true)
if prefix == "xmlns"
if local_part == "xml"
if value != "http://www.w3.org/XML/1998/namespace"
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index 7f47c2be..999751b4 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -65,7 +65,11 @@ def encoding=(enc)
encoding_updated
end

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

def ensure_buffer
@@ -158,9 +162,9 @@ def initialize(arg, block_size=500, encoding=nil)
end
end

- def read
+ def read(term = nil)
begin
- @scanner << readline
+ @scanner << readline(term)
true
rescue Exception, NameError
@source = nil
@@ -168,6 +172,21 @@ def read
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 ensure_buffer
read if @scanner.eos? && @source
end
@@ -218,8 +237,8 @@ def current_line
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/test/test_document.rb b/test/test_document.rb
index 953656f8..f96bfd5d 100644
--- a/test/test_document.rb
+++ b/test/test_document.rb
@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-
# frozen_string_literal: false

+require 'core_assertions'
+
module REXMLTests
class TestDocument < Test::Unit::TestCase
+ include Test::Unit::CoreAssertions
+
def test_version_attributes_to_s
doc = REXML::Document.new(<<~eoxml)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
@@ -198,6 +202,13 @@ def test_xml_declaration_standalone
assert_equal('no', doc.stand_alone?, bug2539)
end

+ def test_gt_linear_performance
+ seq = [10000, 50000, 100000, 150000, 200000]
+ assert_linear_performance(seq) do |n|
+ REXML::Document.new('<test testing="' + ">" * n + '"></test>')
+ end
+ end
+
class WriteTest < Test::Unit::TestCase
def setup
@document = REXML::Document.new(<<-EOX)
135 changes: 135 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,135 @@
diff --git a/Gemfile b/Gemfile
index 042ef8ac..f78cc861 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,4 +10,5 @@ group :development do
gem "bundler"
gem "rake"
gem "test-unit"
+ gem "test-unit-ruby-core"
end
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 8d62391c..d09237c5 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -628,17 +628,17 @@ def parse_attributes(prefixes, curr_ns)
message = "Missing attribute equal: <#{name}>"
raise REXML::ParseException.new(message, @source)
end
- unless match = @source.match(/(['"])(.*?)\1\s*/um, true)
- if match = @source.match(/(['"])/, true)
- message =
- "Missing attribute value end quote: <#{name}>: <#{match[1]}>"
- raise REXML::ParseException.new(message, @source)
- else
- message = "Missing attribute value start quote: <#{name}>"
- raise REXML::ParseException.new(message, @source)
- end
+ unless match = @source.match(/(['"])/, true)
+ message = "Missing attribute value start quote: <#{name}>"
+ raise REXML::ParseException.new(message, @source)
+ end
+ 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
- value = match[2]
+ @source.match(/\s*/um, true)
if prefix == "xmlns"
if local_part == "xml"
if value != "http://www.w3.org/XML/1998/namespace"
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
index 7f47c2be..999751b4 100644
--- a/lib/rexml/source.rb
+++ b/lib/rexml/source.rb
@@ -65,7 +65,11 @@ def encoding=(enc)
encoding_updated
end

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

def ensure_buffer
@@ -158,9 +162,9 @@ def initialize(arg, block_size=500, encoding=nil)
end
end

- def read
+ def read(term = nil)
begin
- @scanner << readline
+ @scanner << readline(term)
true
rescue Exception, NameError
@source = nil
@@ -168,6 +172,21 @@ def read
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 ensure_buffer
read if @scanner.eos? && @source
end
@@ -218,8 +237,8 @@ def current_line
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/test/test_document.rb b/test/test_document.rb
index 953656f8..f96bfd5d 100644
--- a/test/test_document.rb
+++ b/test/test_document.rb
@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-
# frozen_string_literal: false

+require 'core_assertions'
+
module REXMLTests
class TestDocument < Test::Unit::TestCase
+ include Test::Unit::CoreAssertions
+
def test_version_attributes_to_s
doc = REXML::Document.new(<<~eoxml)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
@@ -198,6 +202,13 @@ def test_xml_declaration_standalone
assert_equal('no', doc.stand_alone?, bug2539)
end

+ def test_gt_linear_performance
+ seq = [10000, 50000, 100000, 150000, 200000]
+ assert_linear_performance(seq) do |n|
+ REXML::Document.new('<test testing="' + ">" * n + '"></test>')
+ end
+ end
+
class WriteTest < Test::Unit::TestCase
def setup
@document = REXML::Document.new(<<-EOX)

0 comments on commit 022e103

Please sign in to comment.