From 022e103b43f50aac13676e064a8e5701885c6cbc Mon Sep 17 00:00:00 2001 From: Amit Karsale Date: Tue, 25 Jun 2024 11:09:47 +0530 Subject: [PATCH] (PA-6507) Patch REXML for CVE-2024-35176 in ruby 2.7 and 3.2 --- configs/components/ruby-2.7.8.rb | 2 + configs/components/ruby-3.2.4.rb | 3 + .../ruby_27/rexml_for_CVE-2024-35176.patch | 135 ++++++++++++++++++ .../ruby_32/rexml_for_CVE-2024-35176.patch | 135 ++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 resources/patches/ruby_27/rexml_for_CVE-2024-35176.patch create mode 100644 resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch diff --git a/configs/components/ruby-2.7.8.rb b/configs/components/ruby-2.7.8.rb index cf1df8999..794041db6 100644 --- a/configs/components/ruby-2.7.8.rb +++ b/configs/components/ruby-2.7.8.rb @@ -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? diff --git a/configs/components/ruby-3.2.4.rb b/configs/components/ruby-3.2.4.rb index b5e808a1b..6bffdcf6d 100644 --- a/configs/components/ruby-3.2.4.rb +++ b/configs/components/ruby-3.2.4.rb @@ -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 diff --git a/resources/patches/ruby_27/rexml_for_CVE-2024-35176.patch b/resources/patches/ruby_27/rexml_for_CVE-2024-35176.patch new file mode 100644 index 000000000..e9df6fcc7 --- /dev/null +++ b/resources/patches/ruby_27/rexml_for_CVE-2024-35176.patch @@ -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 !@source.eof? ++ 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) + +@@ -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('" * n + '">') ++ end ++ end ++ + class WriteTest < Test::Unit::TestCase + def setup + @document = REXML::Document.new(<<-EOX) \ No newline at end of file diff --git a/resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch b/resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch new file mode 100644 index 000000000..e9df6fcc7 --- /dev/null +++ b/resources/patches/ruby_32/rexml_for_CVE-2024-35176.patch @@ -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 !@source.eof? ++ 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) + +@@ -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('" * n + '">') ++ end ++ end ++ + class WriteTest < Test::Unit::TestCase + def setup + @document = REXML::Document.new(<<-EOX) \ No newline at end of file