1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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
This commit is contained in:
Aaron Patterson 2015-06-09 11:24:25 -07:00 committed by Rafael Mendonça França
parent 193b76cbf0
commit 9b635292db
3 changed files with 15 additions and 10 deletions

View file

@ -78,6 +78,9 @@ module ActiveSupport
) )
end end
attr_accessor :depth
self.depth = 100
delegate :parse, :to => :backend delegate :parse, :to => :backend
def backend def backend

View file

@ -46,7 +46,7 @@ module ActiveSupport
xml_string_reader = StringReader.new(data) xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader) xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source) 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
end end
@ -58,9 +58,10 @@ module ActiveSupport
# Hash to merge the converted element into. # Hash to merge the converted element into.
# element:: # element::
# XML element to merge into hash # 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) delete_empty(hash)
merge!(hash, element.tag_name, collapse(element)) merge!(hash, element.tag_name, collapse(element, depth))
end end
def delete_empty(hash) def delete_empty(hash)
@ -71,14 +72,14 @@ module ActiveSupport
# #
# element:: # element::
# The document element to be collapsed. # The document element to be collapsed.
def collapse(element) def collapse(element, depth)
hash = get_attributes(element) hash = get_attributes(element)
child_nodes = element.child_nodes child_nodes = element.child_nodes
if child_nodes.length > 0 if child_nodes.length > 0
(0...child_nodes.length).each do |i| (0...child_nodes.length).each do |i|
child = child_nodes.item(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 end
merge_texts!(hash, element) unless empty_content?(element) merge_texts!(hash, element) unless empty_content?(element)
hash hash

View file

@ -29,7 +29,7 @@ module ActiveSupport
doc = REXML::Document.new(data) doc = REXML::Document.new(data)
if doc.root if doc.root
merge_element!({}, doc.root) merge_element!({}, doc.root, XmlMini.depth)
else else
raise REXML::ParseException, raise REXML::ParseException,
"The document #{doc.to_s.inspect} does not have a valid root" "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. # Hash to merge the converted element into.
# element:: # element::
# XML element to merge into hash # XML element to merge into hash
def merge_element!(hash, element) def merge_element!(hash, element, depth)
merge!(hash, element.name, collapse(element)) raise REXML::ParseException, "The document is too deep" if depth == 0
merge!(hash, element.name, collapse(element, depth))
end end
# Actually converts an XML document element into a data structure. # Actually converts an XML document element into a data structure.
# #
# element:: # element::
# The document element to be collapsed. # The document element to be collapsed.
def collapse(element) def collapse(element, depth)
hash = get_attributes(element) hash = get_attributes(element)
if element.has_elements? 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) merge_texts!(hash, element) unless empty_content?(element)
hash hash
else else