Skip to content

Commit

Permalink
parser tree: improve namespace conflicted attribute check performance
Browse files Browse the repository at this point in the history
It was slow for deep element.

Reported by l33thaxor. Thanks!!!
  • Loading branch information
kou committed Aug 22, 2024
1 parent 6109e01 commit 7cb5eae
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 11 deletions.
11 changes: 0 additions & 11 deletions lib/rexml/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2384,17 +2384,6 @@ def []=( name, value )
elsif old_attr.kind_of? Hash
old_attr[value.prefix] = value
elsif old_attr.prefix != value.prefix
# Check for conflicting namespaces
if value.prefix != "xmlns" and old_attr.prefix != "xmlns"
old_namespace = old_attr.namespace
new_namespace = value.namespace
if old_namespace == new_namespace
raise ParseException.new(
"Namespace conflict in adding attribute \"#{value.name}\": "+
"Prefix \"#{old_attr.prefix}\" = \"#{old_namespace}\" and "+
"prefix \"#{value.prefix}\" = \"#{new_namespace}\"")
end
end
store value.name, {old_attr.prefix => old_attr,
value.prefix => value}
else
Expand Down
15 changes: 15 additions & 0 deletions lib/rexml/parsers/baseparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ def process_instruction

def parse_attributes(prefixes)
attributes = {}
expanded_names = {}
closed = false
while true
if @source.match(">", true)
Expand Down Expand Up @@ -805,6 +806,20 @@ def parse_attributes(prefixes)
raise REXML::ParseException.new(msg, @source, self)
end

unless prefix == "xmlns"
uri = @namespaces[prefix]
expanded_name = [uri, local_part]
existing_prefix = expanded_names[expanded_name]
if existing_prefix
message = "Namespace conflict in adding attribute " +
"\"#{local_part}\": " +
"Prefix \"#{existing_prefix}\" = \"#{uri}\" and " +
"prefix \"#{prefix}\" = \"#{uri}\""
raise REXML::ParseException.new(message, @source, self)
end
expanded_names[expanded_name] = prefix
end

attributes[name] = value
else
message = "Invalid attribute name: <#{@source.buffer.split(%r{[/>\s]}).first}>"
Expand Down
14 changes: 14 additions & 0 deletions test/parse/test_element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,19 @@ def test_linear_performance_attribute_value_gt
REXML::Document.new('<test testing="' + ">" * n + '"></test>')
end
end

def test_linear_performance_deep_same_name_attributes
seq = [100, 500, 1000, 1500, 2000]
assert_linear_performance(seq, rehearsal: 10) do |n|
xml = <<-XML
<?xml version="1.0"?>
<root xmlns:ns="ns-uri">
#{"<x ns:name='ns-value' name='value'>\n" * n}
#{"</x>\n" * n}
</root>
XML
REXML::Document.new(xml)
end
end
end
end
4 changes: 4 additions & 0 deletions test/test_core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ def test_attribute_namespace_conflict
# https://www.w3.org/TR/xml-names/#uniqAttrs
message = <<-MESSAGE.chomp
Namespace conflict in adding attribute "a": Prefix "n1" = "http://www.w3.org" and prefix "n2" = "http://www.w3.org"
Line: 4
Position: 140
Last 80 unconsumed characters:
/>
MESSAGE
assert_raise(REXML::ParseException.new(message)) do
Document.new(<<-XML)
Expand Down

0 comments on commit 7cb5eae

Please sign in to comment.