aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2015-06-09 11:24:25 -0700
committerRafael Mendonça França <rafaelmfranca@gmail.com>2015-06-16 12:10:51 -0300
commit153cc843ad95930b00b0ca91d30b599b7dec9680 (patch)
tree6d8f7c2ada15931eb505d445ffab711c02da8b98
parentb344986bc3d94ca7821fc5e0eef1874882ac6cbb (diff)
downloadrails-153cc843ad95930b00b0ca91d30b599b7dec9680.tar.gz
rails-153cc843ad95930b00b0ca91d30b599b7dec9680.tar.bz2
rails-153cc843ad95930b00b0ca91d30b599b7dec9680.zip
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 Conflicts: activesupport/lib/active_support/xml_mini.rb
-rw-r--r--activesupport/lib/active_support/xml_mini.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb11
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb11
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 a4ac1d7041..afe9c4a3e9 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -77,6 +77,9 @@ module ActiveSupport
end
attr_reader :backend
+ attr_accessor :depth
+ self.depth = 100
+
delegate :parse, :to => :backend
def backend=(name)
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 8d23ce4e18..8d64bc2aa2 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -47,7 +47,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
@@ -59,9 +59,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)
@@ -72,14 +73,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
for i in 0...child_nodes.length
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 a13ad10118..cb2a4f43a8 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -30,7 +30,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"
@@ -45,19 +45,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