From eb45c8dcca962c04e56f46b0040b2c33278ca3f9 Mon Sep 17 00:00:00 2001 From: NAITOH Jun Date: Mon, 8 Jul 2024 05:52:19 +0900 Subject: [PATCH] fix: Extra content at the end of the document (#161) ## Why? XML with additional content at the end of the document is invalid. https://www.w3.org/TR/2006/REC-xml11-20060816/#document ``` [1] document ::= ( prolog element Misc* ) - ( Char* RestrictedChar Char* ) ``` https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-Misc ``` [27] Misc ::= Comment | PI | S ``` https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-PI ``` [16] PI ::= '' Char*)))? '?>' ``` https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-PITarget ``` [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) ``` --- lib/rexml/parsers/baseparser.rb | 9 ++++++ test/parse/test_comment.rb | 12 ++++++++ test/parse/test_element.rb | 34 +++++++++++++++++++++++ test/parse/test_processing_instruction.rb | 12 ++++++++ test/parse/test_text.rb | 25 +++++++++++++++++ test/test_pullparser.rb | 14 +++++----- 6 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 test/parse/test_text.rb diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb index 02759e70..900c19cc 100644 --- a/lib/rexml/parsers/baseparser.rb +++ b/lib/rexml/parsers/baseparser.rb @@ -460,8 +460,12 @@ def pull_event @closed = tag @nsstack.shift else + if @tags.empty? and @have_root + raise ParseException.new("Malformed XML: Extra tag at the end of the document (got '<#{tag}')", @source) + end @tags.push( tag ) end + @have_root = true return [ :start_element, tag, attributes ] end else @@ -469,6 +473,11 @@ def pull_event if text.chomp!("<") @source.position -= "<".bytesize end + if @tags.empty? and @have_root + unless /\A\s*\z/.match?(text) + raise ParseException.new("Malformed XML: Extra content at the end of the document (got '#{text}')", @source) + end + end return [ :text, text ] end rescue REXML::UndefinedNamespaceException diff --git a/test/parse/test_comment.rb b/test/parse/test_comment.rb index ce6678e8..46a07409 100644 --- a/test/parse/test_comment.rb +++ b/test/parse/test_comment.rb @@ -105,5 +105,17 @@ def test_after_doctype_malformed_comment_end DETAIL end end + + def test_after_root + parser = REXML::Parsers::BaseParser.new('') + + events = {} + while parser.has_next? + event = parser.pull + events[event[0]] = event[1] + end + + assert_equal(" ok comment ", events[:comment]) + end end end diff --git a/test/parse/test_element.rb b/test/parse/test_element.rb index 14d0703a..a65cfa85 100644 --- a/test/parse/test_element.rb +++ b/test/parse/test_element.rb @@ -85,6 +85,40 @@ def test_garbage_less_than_slash_before_end_tag_at_line_start DETAIL end + + def test_after_root + exception = assert_raise(REXML::ParseException) do + parser = REXML::Parsers::BaseParser.new('') + while parser.has_next? + parser.pull + end + end + + assert_equal(<<~DETAIL.chomp, exception.to_s) + Malformed XML: Extra tag at the end of the document (got '') + while parser.has_next? + parser.pull + end + end + + assert_equal(<<~DETAIL.chomp, exception.to_s) + Malformed XML: Extra tag at the end of the document (got '') + + events = {} + while parser.has_next? + event = parser.pull + events[event[0]] = event[1] + end + + assert_equal("abc", events[:processing_instruction]) + end end end diff --git a/test/parse/test_text.rb b/test/parse/test_text.rb new file mode 100644 index 00000000..f1622b71 --- /dev/null +++ b/test/parse/test_text.rb @@ -0,0 +1,25 @@ +require "test/unit" +require 'rexml/parsers/baseparser' + +module REXMLTests + class TestParseText < Test::Unit::TestCase + class TestInvalid < self + def test_after_root + exception = assert_raise(REXML::ParseException) do + parser = REXML::Parsers::BaseParser.new('c') + while parser.has_next? + parser.pull + end + end + + assert_equal(<<~DETAIL.chomp, exception.to_s) + Malformed XML: Extra content at the end of the document (got 'c') + Line: 1 + Position: 8 + Last 80 unconsumed characters: + + DETAIL + end + end + end +end diff --git a/test/test_pullparser.rb b/test/test_pullparser.rb index 073d896d..0aca46be 100644 --- a/test/test_pullparser.rb +++ b/test/test_pullparser.rb @@ -63,8 +63,10 @@ def test_entity_replacement end def test_character_references - source = 'AB' + source = 'AB' parser = REXML::Parsers::PullParser.new( source ) + + events = {} element_name = '' while parser.has_next? event = parser.pull @@ -72,14 +74,12 @@ def test_character_references when :start_element element_name = event[0] when :text - case element_name - when 'a' - assert_equal('A', event[1]) - when 'b' - assert_equal('B', event[1]) - end + events[element_name] = event[1] end end + + assert_equal('A', events['a']) + assert_equal("B", events['b']) end def test_text_content_with_line_breaks