From eb4f1d6a02e9557b97cdbed76157dc5a625cdb82 Mon Sep 17 00:00:00 2001 From: Aaron Patterson 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