Skip to content

Commit

Permalink
Add support for XML entity expansion limitation in Stream parser
Browse files Browse the repository at this point in the history
## Why?

See:
- ruby#187
- ruby#195

## Change
- Supported `REXML::Security.entity_expansion_limit=` in Stream parser
- Supported `REXML::Security.entity_expansion_text_limit=` in Stream parser
  • Loading branch information
naitoh committed Aug 20, 2024
1 parent 8b97bae commit dc48407
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/rexml/parsers/streamparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def add_listener( listener )
@parser.add_listener( listener )
end

def entity_expansion_count
@parser.entity_expansion_count
end

def parse
# entity string
while true
Expand Down
101 changes: 101 additions & 0 deletions test/test_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,81 @@ def teardown
REXML::Security.entity_expansion_text_limit = @default_entity_expansion_text_limit
end

def test_have_value
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
<!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
<!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
<!ENTITY e "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
]>
<member>
&a;
</member>
XML

assert_raise(RuntimeError.new("entity expansion has grown too large")) do
REXML::Document.parse_stream(source, MyListener.new)
end
end

def test_empty_value
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
<!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
<!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
<!ENTITY e "">
]>
<member>
&a;
</member>
XML

listener = MyListener.new
REXML::Security.entity_expansion_limit = 100000
parser = REXML::Parsers::StreamParser.new( source, listener )
parser.parse
assert_equal(11111, parser.entity_expansion_count)

REXML::Security.entity_expansion_limit = @default_entity_expansion_limit
parser = REXML::Parsers::StreamParser.new( source, listener )
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
parser.parse
end
assert do
parser.entity_expansion_count > @default_entity_expansion_limit
end
end

def test_with_default_entity
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "a">
<!ENTITY a2 "&a; &a;">
]>
<member>
&a;
&a2;
&lt;
</member>
XML

listener = MyListener.new
REXML::Security.entity_expansion_limit = 4
REXML::Document.parse_stream(source, listener)

REXML::Security.entity_expansion_limit = 3
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
REXML::Document.parse_stream(source, listener)
end
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
Expand All @@ -159,6 +234,32 @@ def text(text)
listener.text_value.bytesize > @default_entity_expansion_text_limit
end
end

def test_entity_expansion_text_limit
source = <<-XML
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;">
<!ENTITY b "&c;&d;&e;">
<!ENTITY c "xxxxxxxxxx">
<!ENTITY d "yyyyyyyyyy">
<!ENTITY e "zzzzzzzzzz">
]>
<member>&a;</member>
XML

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

assert_equal(90, listener.text_value.size)
end
end

# For test_listener
Expand Down

0 comments on commit dc48407

Please sign in to comment.