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