From eb4f1d6a02e9557b97cdbed76157dc5a625cdb82 Mon Sep 17 00:00:00 2001 | |
From: Aaron Patterson <aaron.patterson@gmail.com> | |
Date: Tue, 9 Jun 2015 11:24:25 -0700 | |
Subject: [PATCH] enforce a depth limit on XML documents | |
XML documents that are too deep can cause an stack overflow, which in | |
turn will cause a potential DoS attack. | |
CVE-2015-3227 | |
--- | |
activesupport/lib/active_support/xml_mini.rb | 3 +++ | |
activesupport/lib/active_support/xml_mini/jdom.rb | 11 ++++++----- | |
activesupport/lib/active_support/xml_mini/rexml.rb | 11 ++++++----- | |
3 files changed, 15 insertions(+), 10 deletions(-) | |
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb | |
index 009ee4d..df7b081 100644 | |
--- a/activesupport/lib/active_support/xml_mini.rb | |
+++ b/activesupport/lib/active_support/xml_mini.rb | |
@@ -78,6 +78,9 @@ module ActiveSupport | |
) | |
end | |
+ attr_accessor :depth | |
+ self.depth = 100 | |
+ | |
delegate :parse, :to => :backend | |
def backend | |
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb | |
index 27c64c4..cdc5490 100644 | |
--- a/activesupport/lib/active_support/xml_mini/jdom.rb | |
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb | |
@@ -46,7 +46,7 @@ module ActiveSupport | |
xml_string_reader = StringReader.new(data) | |
xml_input_source = InputSource.new(xml_string_reader) | |
doc = @dbf.new_document_builder.parse(xml_input_source) | |
- merge_element!({CONTENT_KEY => ''}, doc.document_element) | |
+ merge_element!({CONTENT_KEY => ''}, doc.document_element, XmlMini.depth) | |
end | |
end | |
@@ -58,9 +58,10 @@ module ActiveSupport | |
# Hash to merge the converted element into. | |
# element:: | |
# XML element to merge into hash | |
- def merge_element!(hash, element) | |
+ def merge_element!(hash, element, depth) | |
+ raise 'Document too deep!' if depth == 0 | |
delete_empty(hash) | |
- merge!(hash, element.tag_name, collapse(element)) | |
+ merge!(hash, element.tag_name, collapse(element, depth)) | |
end | |
def delete_empty(hash) | |
@@ -71,14 +72,14 @@ module ActiveSupport | |
# | |
# element:: | |
# The document element to be collapsed. | |
- def collapse(element) | |
+ def collapse(element, depth) | |
hash = get_attributes(element) | |
child_nodes = element.child_nodes | |
if child_nodes.length > 0 | |
(0...child_nodes.length).each do |i| | |
child = child_nodes.item(i) | |
- merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE | |
+ merge_element!(hash, child, depth - 1) unless child.node_type == Node.TEXT_NODE | |
end | |
merge_texts!(hash, element) unless empty_content?(element) | |
hash | |
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb | |
index 5c7c78b..924ed72 100644 | |
--- a/activesupport/lib/active_support/xml_mini/rexml.rb | |
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb | |
@@ -29,7 +29,7 @@ module ActiveSupport | |
doc = REXML::Document.new(data) | |
if doc.root | |
- merge_element!({}, doc.root) | |
+ merge_element!({}, doc.root, XmlMini.depth) | |
else | |
raise REXML::ParseException, | |
"The document #{doc.to_s.inspect} does not have a valid root" | |
@@ -44,19 +44,20 @@ module ActiveSupport | |
# Hash to merge the converted element into. | |
# element:: | |
# XML element to merge into hash | |
- def merge_element!(hash, element) | |
- merge!(hash, element.name, collapse(element)) | |
+ def merge_element!(hash, element, depth) | |
+ raise REXML::ParseException, "The document is too deep" if depth == 0 | |
+ merge!(hash, element.name, collapse(element, depth)) | |
end | |
# Actually converts an XML document element into a data structure. | |
# | |
# element:: | |
# The document element to be collapsed. | |
- def collapse(element) | |
+ def collapse(element, depth) | |
hash = get_attributes(element) | |
if element.has_elements? | |
- element.each_element {|child| merge_element!(hash, child) } | |
+ element.each_element {|child| merge_element!(hash, child, depth - 1) } | |
merge_texts!(hash, element) unless empty_content?(element) | |
hash | |
else | |
-- | |
2.2.1 | |