Skip to content

Commit

Permalink
Fix RuntimeError in REXML::Parsers::BaseParser for valid feeds (#199)
Browse files Browse the repository at this point in the history
GitHub: fix GH-198

Change `#entity` to not match against default entities

After this change, the following example will not raise a RuntimeError:

```ruby
# rexml/refactor_entity_example.rb

$LOAD_PATH.unshift(File.expand_path("lib"))

require "rexml/parsers/baseparser"

valid_feed = "<p>#{'A' * 10_240}</p>"

base_parser = REXML::Parsers::BaseParser.new("")
base_parser.unnormalize(valid_feed) # => "<p>" + "A" * 10_240 + "</p>"
```

Default entities now gets substituted by this block instead


https://github.com/ruby/rexml/blob/e14847cee53d26eb162ad786ba12e3cd7a86fce0/lib/rexml/parsers/baseparser.rb#L560-L563

---------

Co-authored-by: Sutou Kouhei <[email protected]>
Co-authored-by: NAITOH Jun <[email protected]>
  • Loading branch information
3 people authored Aug 17, 2024
1 parent 2f019f9 commit 1c76dbb
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 9 deletions.
16 changes: 7 additions & 9 deletions lib/rexml/parsers/baseparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -521,15 +521,13 @@ def pull_event
private :pull_event

def entity( reference, entities )
value = nil
value = entities[ reference ] if entities
if value
record_entity_expansion
else
value = DEFAULT_ENTITIES[ reference ]
value = value[2] if value
end
unnormalize( value, entities ) if value
return unless entities

value = entities[ reference ]
return if value.nil?

record_entity_expansion
unnormalize( value, entities )
end

# Escapes all possible entities
Expand Down
30 changes: 30 additions & 0 deletions test/test_pullparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,36 @@ def test_with_default_entity
end
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<member>
#{member_value}
</member>
XML

parser = REXML::Parsers::PullParser.new(source)
events = {}
element_name = ''
while parser.has_next?
event = parser.pull
case event.event_type
when :start_element
element_name = event[0]
when :text
events[element_name] = event[1]
end
end

expected_value = "<p>#{'A' * @default_entity_expansion_text_limit}</p>"
assert_equal(expected_value, events['member'].strip)
assert_equal(0, parser.entity_expansion_count)
assert do
events['member'].bytesize > @default_entity_expansion_text_limit
end
end

def test_entity_expansion_text_limit
source = <<-XML
<!DOCTYPE member [
Expand Down
24 changes: 24 additions & 0 deletions test/test_sax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,30 @@ def test_with_default_entity
end
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<member>
#{member_value}
</member>
XML

sax = REXML::Parsers::SAX2Parser.new(source)
text_value = nil
sax.listen(:characters, ["member"]) do |text|
text_value = text
end
sax.parse

expected_value = "<p>#{'A' * @default_entity_expansion_text_limit}</p>"
assert_equal(expected_value, text_value.strip)
assert_equal(0, sax.entity_expansion_count)
assert do
text_value.bytesize > @default_entity_expansion_text_limit
end
end

def test_entity_expansion_text_limit
source = <<-XML
<!DOCTYPE member [
Expand Down
37 changes: 37 additions & 0 deletions test/test_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,43 @@ def entity(content)
end
end

class EntityExpansionLimitTest < Test::Unit::TestCase
def setup
@default_entity_expansion_limit = REXML::Security.entity_expansion_limit
@default_entity_expansion_text_limit = REXML::Security.entity_expansion_text_limit
end

def teardown
REXML::Security.entity_expansion_limit = @default_entity_expansion_limit
REXML::Security.entity_expansion_text_limit = @default_entity_expansion_text_limit
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<member>
#{member_value}
</member>
XML

listener = MyListener.new
class << listener
attr_accessor :text_value
def text(text)
@text_value << text
end
end
listener.text_value = ""
REXML::Document.parse_stream(source, listener)

expected_value = "<p>#{'A' * @default_entity_expansion_text_limit}</p>"
assert_equal(expected_value, listener.text_value.strip)
assert do
listener.text_value.bytesize > @default_entity_expansion_text_limit
end
end
end

# For test_listener
class RequestReader
Expand Down

0 comments on commit 1c76dbb

Please sign in to comment.