Skip to content

Fix infinite entity expansion by detecting circular references#312

Open
naitoh wants to merge 1 commit intoruby:masterfrom
naitoh:fix_detecting_circular_references
Open

Fix infinite entity expansion by detecting circular references#312
naitoh wants to merge 1 commit intoruby:masterfrom
naitoh:fix_detecting_circular_references

Conversation

@naitoh
Copy link
Copy Markdown
Contributor

@naitoh naitoh commented May 5, 2026

Why?

Fix an issue where a stack level too deep (SystemStackError) error caused by a circular reference was not detected as an appropriate error.

require 'rexml/parsers/streamparser'
require 'rexml/streamlistener'

source = <<~XML
<!DOCTYPE root [
  <!ENTITY x "&x;">
]>
<root>&x;</root>
XML

listener = Class.new { include REXML::StreamListener }.new
REXML::Parsers::StreamParser.new(source, listener).parse
  • before
lib/rexml/parsers/baseparser.rb:544:in 'REXML::Parsers::BaseParser#entity': stack level too deep (SystemStackError)
  • after
lib/rexml/parsers/baseparser.rb:543:in 'REXML::Parsers::BaseParser#entity': Detected an entity reference loop: x (RuntimeError)

## Why?
Fix an issue where a `stack level too deep (SystemStackError)` error caused by a circular reference was not detected as an appropriate error.

```
require 'rexml/parsers/streamparser'
require 'rexml/streamlistener'

source = <<~XML
<!DOCTYPE root [
  <!ENTITY x "&x;">
]>
<root>&x;</root>
XML

listener = Class.new { include REXML::StreamListener }.new
REXML::Parsers::StreamParser.new(source, listener).parse
```

- before
```
lib/rexml/parsers/baseparser.rb:544:in 'REXML::Parsers::BaseParser#entity': stack level too deep (SystemStackError)
```
- after
```
lib/rexml/parsers/baseparser.rb:543:in 'REXML::Parsers::BaseParser#entity': Detected an entity reference loop: x (RuntimeError)
```
@naitoh naitoh requested a review from kou May 5, 2026 12:37
@kou kou requested a review from Copilot May 6, 2026 03:28
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds circular entity-reference detection to prevent unbounded recursive expansion (previously resulting in SystemStackError) during streaming-style parsing.

Changes:

  • Track the current entity-expansion chain in REXML::Parsers::BaseParser and raise a clear error when a loop is detected.
  • Extend BaseParser#unnormalize/#entity to thread the expansion state through nested expansions.
  • Add regression tests covering direct/indirect circular references (including a long entity value) via StreamParser.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
lib/rexml/parsers/baseparser.rb Adds loop detection during entity expansion by propagating an “currently expanding” set through recursive unnormalization.
test/test_entity.rb Adds regression tests ensuring circular entity references raise an appropriate error instead of stack overflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


record_entity_expansion
unnormalize( value, entities )
unnormalize( value, entities, nil, (expanding || Set.new) | [reference] )
Comment on lines 541 to +546
value = entities[ reference ]
return if value.nil?
raise "Detected an entity reference loop: #{reference}" if expanding&.include?(reference)

record_entity_expansion
unnormalize( value, entities )
unnormalize( value, entities, nil, (expanding || Set.new) | [reference] )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants