Skip to content

Add local entity expansion limit methods #202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/rexml/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ def to_s
# have been expanded to their values
def value
return @unnormalized if @unnormalized
@unnormalized = Text::unnormalize( @normalized, doctype )
@unnormalized

@unnormalized = Text::unnormalize(@normalized, doctype,
entity_expansion_text_limit: @element&.document&.entity_expansion_text_limit)
end

# The normalized value of this attribute. That is, the attribute with
Expand Down
6 changes: 5 additions & 1 deletion lib/rexml/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ class Document < Element
#
def initialize( source = nil, context = {} )
@entity_expansion_count = 0
@entity_expansion_limit = Security.entity_expansion_limit
@entity_expansion_text_limit = Security.entity_expansion_text_limit
super()
@context = context
return if source.nil?
Expand Down Expand Up @@ -431,10 +433,12 @@ def Document::entity_expansion_text_limit
end

attr_reader :entity_expansion_count
attr_writer :entity_expansion_limit
attr_accessor :entity_expansion_text_limit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the reader for this?

Copy link
Contributor Author

@naitoh naitoh Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes
In the following we use reader.


def record_entity_expansion
@entity_expansion_count += 1
if @entity_expansion_count > Security.entity_expansion_limit
if @entity_expansion_count > @entity_expansion_limit
raise "number of entity expansions exceeded, processing aborted."
end
end
Expand Down
7 changes: 5 additions & 2 deletions lib/rexml/entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ def Entity::matches? string
# Evaluates to the unnormalized value of this entity; that is, replacing
# &ent; entities.
def unnormalized
document.record_entity_expansion unless document.nil?
document&.record_entity_expansion

return nil if @value.nil?
@unnormalized = Text::unnormalize(@value, parent)

@unnormalized = Text::unnormalize(@value, parent,
entity_expansion_text_limit: document&.entity_expansion_text_limit)
end

#once :unnormalized
Expand Down
8 changes: 6 additions & 2 deletions lib/rexml/parsers/baseparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ def initialize( source )
@listeners = []
@prefixes = Set.new
@entity_expansion_count = 0
@entity_expansion_limit = Security.entity_expansion_limit
@entity_expansion_text_limit = Security.entity_expansion_text_limit
end

def add_listener( listener )
Expand All @@ -172,6 +174,8 @@ def add_listener( listener )

attr_reader :source
attr_reader :entity_expansion_count
attr_writer :entity_expansion_limit
attr_writer :entity_expansion_text_limit

def stream=( source )
@source = SourceFactory.create_from( source )
Expand Down Expand Up @@ -585,7 +589,7 @@ def unnormalize( string, entities=nil, filter=nil )
end
re = Private::DEFAULT_ENTITIES_PATTERNS[entity_reference] || /&#{entity_reference};/
rv.gsub!( re, entity_value )
if rv.bytesize > Security.entity_expansion_text_limit
if rv.bytesize > @entity_expansion_text_limit
raise "entity expansion has grown too large"
end
else
Expand Down Expand Up @@ -627,7 +631,7 @@ def pop_namespaces_restore

def record_entity_expansion(delta=1)
@entity_expansion_count += delta
if @entity_expansion_count > Security.entity_expansion_limit
if @entity_expansion_count > @entity_expansion_limit
raise "number of entity expansions exceeded, processing aborted."
end
end
Expand Down
8 changes: 8 additions & 0 deletions lib/rexml/parsers/pullparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ def entity_expansion_count
@parser.entity_expansion_count
end

def entity_expansion_limit=( limit )
@parser.entity_expansion_limit = limit
end

def entity_expansion_text_limit=( limit )
@parser.entity_expansion_text_limit = limit
end

def each
while has_next?
yield self.pull
Expand Down
8 changes: 8 additions & 0 deletions lib/rexml/parsers/sax2parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def entity_expansion_count
@parser.entity_expansion_count
end

def entity_expansion_limit=( limit )
@parser.entity_expansion_limit = limit
end

def entity_expansion_text_limit=( limit )
@parser.entity_expansion_text_limit = limit
end

def add_listener( listener )
@parser.add_listener( listener )
end
Expand Down
8 changes: 8 additions & 0 deletions lib/rexml/parsers/streamparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ def entity_expansion_count
@parser.entity_expansion_count
end

def entity_expansion_limit=( limit )
@parser.entity_expansion_limit = limit
end

def entity_expansion_text_limit=( limit )
@parser.entity_expansion_text_limit = limit
end

def parse
# entity string
while true
Expand Down
8 changes: 5 additions & 3 deletions lib/rexml/text.rb
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ def inspect
# u = Text.new( "sean russell", false, nil, true )
# u.value #-> "sean russell"
def value
@unnormalized ||= Text::unnormalize( @string, doctype )
@unnormalized ||= Text::unnormalize(@string, doctype,
entity_expansion_text_limit: document&.entity_expansion_text_limit)
end

# Sets the contents of this text node. This expects the text to be
Expand Down Expand Up @@ -411,11 +412,12 @@ def Text::normalize( input, doctype=nil, entity_filter=nil )
end

# Unescapes all possible entities
def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil )
def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil, entity_expansion_text_limit: nil )
entity_expansion_text_limit ||= Security.entity_expansion_text_limit
sum = 0
string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) {
s = Text.expand($&, doctype, filter)
if sum + s.bytesize > Security.entity_expansion_text_limit
if sum + s.bytesize > entity_expansion_text_limit
raise "entity expansion has grown too large"
else
sum += s.bytesize
Expand Down
22 changes: 5 additions & 17 deletions test/test_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,6 @@ def test_new
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

class GeneralEntityTest < self
def test_have_value
xml = <<XML
Expand All @@ -64,9 +54,8 @@ def test_have_value
doc.root.children.first.value
end

REXML::Security.entity_expansion_limit = 100
assert_equal(100, REXML::Security.entity_expansion_limit)
doc = REXML::Document.new(xml)
doc.entity_expansion_limit = 100
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
doc.root.children.first.value
end
Expand Down Expand Up @@ -95,9 +84,8 @@ def test_empty_value
doc.root.children.first.value
end

REXML::Security.entity_expansion_limit = 100
assert_equal(100, REXML::Security.entity_expansion_limit)
doc = REXML::Document.new(xml)
doc.entity_expansion_limit = 100
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
doc.root.children.first.value
end
Expand All @@ -118,12 +106,12 @@ def test_with_default_entity
</member>
XML

REXML::Security.entity_expansion_limit = 4
doc = REXML::Document.new(xml)
doc.entity_expansion_limit = 4
assert_equal("\na\na a\n<\n", doc.root.children.first.value)

REXML::Security.entity_expansion_limit = 3
doc = REXML::Document.new(xml)
doc.entity_expansion_limit = 3
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
doc.root.children.first.value
end
Expand All @@ -142,8 +130,8 @@ def test_entity_expansion_text_limit
<member>&a;</member>
XML

REXML::Security.entity_expansion_text_limit = 90
doc = REXML::Document.new(xml)
doc.entity_expansion_text_limit = 90
assert_equal(90, doc.root.children.first.value.bytesize)
end
end
Expand Down
27 changes: 8 additions & 19 deletions test/test_pullparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,6 @@ def test_peek
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

class GeneralEntityTest < self
def test_have_value
source = <<-XML
Expand Down Expand Up @@ -206,22 +196,21 @@ def test_empty_value
</member>
XML

REXML::Security.entity_expansion_limit = 100000
parser = REXML::Parsers::PullParser.new(source)
parser.entity_expansion_limit = 100000
while parser.has_next?
parser.pull
end
assert_equal(11111, parser.entity_expansion_count)

REXML::Security.entity_expansion_limit = @default_entity_expansion_limit
parser = REXML::Parsers::PullParser.new(source)
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
while parser.has_next?
parser.pull
end
end
assert do
parser.entity_expansion_count > @default_entity_expansion_limit
parser.entity_expansion_count > REXML::Security.entity_expansion_limit
end
end

Expand All @@ -239,14 +228,14 @@ def test_with_default_entity
</member>
XML

REXML::Security.entity_expansion_limit = 4
parser = REXML::Parsers::PullParser.new(source)
parser.entity_expansion_limit = 4
while parser.has_next?
parser.pull
end

REXML::Security.entity_expansion_limit = 3
parser = REXML::Parsers::PullParser.new(source)
parser.entity_expansion_limit = 3
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
while parser.has_next?
parser.pull
Expand All @@ -255,7 +244,7 @@ def test_with_default_entity
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
member_value = "&lt;p&gt;#{'A' * REXML::Security.entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<member>
Expand All @@ -276,11 +265,11 @@ def test_with_only_default_entities
end
end

expected_value = "<p>#{'A' * @default_entity_expansion_text_limit}</p>"
expected_value = "<p>#{'A' * REXML::Security.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
events['member'].bytesize > REXML::Security.entity_expansion_text_limit
end
end

Expand All @@ -296,8 +285,8 @@ def test_entity_expansion_text_limit
<member>&a;</member>
XML

REXML::Security.entity_expansion_text_limit = 90
parser = REXML::Parsers::PullParser.new(source)
parser.entity_expansion_text_limit = 90
events = {}
element_name = ''
while parser.has_next?
Expand Down
27 changes: 8 additions & 19 deletions test/test_sax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,6 @@ def test_sax2
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

class GeneralEntityTest < self
def test_have_value
source = <<-XML
Expand Down Expand Up @@ -147,18 +137,17 @@ def test_empty_value
</member>
XML

REXML::Security.entity_expansion_limit = 100000
sax = REXML::Parsers::SAX2Parser.new(source)
sax.entity_expansion_limit = 100000
sax.parse
assert_equal(11111, sax.entity_expansion_count)

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

Expand All @@ -176,19 +165,19 @@ def test_with_default_entity
</member>
XML

REXML::Security.entity_expansion_limit = 4
sax = REXML::Parsers::SAX2Parser.new(source)
sax.entity_expansion_limit = 4
sax.parse

REXML::Security.entity_expansion_limit = 3
sax = REXML::Parsers::SAX2Parser.new(source)
sax.entity_expansion_limit = 3
assert_raise(RuntimeError.new("number of entity expansions exceeded, processing aborted.")) do
sax.parse
end
end

def test_with_only_default_entities
member_value = "&lt;p&gt;#{'A' * @default_entity_expansion_text_limit}&lt;/p&gt;"
member_value = "&lt;p&gt;#{'A' * REXML::Security.entity_expansion_text_limit}&lt;/p&gt;"
source = <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<member>
Expand All @@ -203,11 +192,11 @@ def test_with_only_default_entities
end
sax.parse

expected_value = "<p>#{'A' * @default_entity_expansion_text_limit}</p>"
expected_value = "<p>#{'A' * REXML::Security.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
text_value.bytesize > REXML::Security.entity_expansion_text_limit
end
end

Expand All @@ -223,8 +212,8 @@ def test_entity_expansion_text_limit
<member>&a;</member>
XML

REXML::Security.entity_expansion_text_limit = 90
sax = REXML::Parsers::SAX2Parser.new(source)
sax.entity_expansion_text_limit = 90
text_size = nil
sax.listen(:characters, ["member"]) do |text|
text_size = text.size
Expand Down
Loading