Skip to content

Fix XPath variable handling and invalid value contamination#342

Open
tompng wants to merge 2 commits into
ruby:masterfrom
tompng:fix_xpath_nil_contamination
Open

Fix XPath variable handling and invalid value contamination#342
tompng wants to merge 2 commits into
ruby:masterfrom
tompng:fix_xpath_nil_contamination

Conversation

@tompng

@tompng tompng commented Jun 28, 2026

Copy link
Copy Markdown
Member

Fixes #15

Fix nil and other invalid value contamination. Nil can be injected through variables and through id function.
Unimplemented function id() is fixed to return [] instead of nil.
Add variable coerce to variable read, and applies predicates if it exists after variable.

Minor behavior changes for an invalid XPath match:

doc = REXML::Document.new("<root/>")
REXML::XPath.match(doc, '($x)[1<2]', {}, {'x'=>42})
#=> [42] → []
REXML::XPath.match(doc, '$x[1<2]', {}, {'x'=>42})
#=> [42] → []

It may raise TypeError (because $x wasn't evaluated to a nodeset), though, it shoudn't return [42]

Note

Although nodeset as a variable has been accepted before, the code added in pull request explicitly permits it.
Nokogiri only accepts string value as a variable, so there's an option to limit the value types here.
Accepting nodeset may cause a problem if the passed nodes doesn't belong to a single document root. (or just consider such case as unsupported)

Copilot AI review requested due to automatic review settings June 28, 2026 08:40

Copilot AI left a comment

Copy link
Copy Markdown

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 adjusts REXML’s XPath evaluation to prevent nil/invalid values from “contaminating” results, primarily by making id() return an empty node-set and by coercing variable values + applying remaining predicates/steps after variable/group evaluation.

Changes:

  • Make unimplemented XPath function id() return [] (empty node-set) instead of nil.
  • Add variable coercion and ensure remaining predicates/steps are applied after variable and grouped expressions.
  • Add/extend tests covering id() and variable handling (including invalid predicate application on non-node-set variables).

Reviewed changes

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

File Description
test/xpath/test_base.rb Adds regression tests for id() and variable coercion/predicate behavior.
lib/rexml/xpath_parser.rb Implements variable coercion and applies remaining predicates after variable/group evaluation.
lib/rexml/functions.rb Changes id() to return an empty node-set ([]).

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

Comment thread lib/rexml/xpath_parser.rb
Comment thread test/xpath/test_base.rb Outdated
Fix nil and other invalid value contamination. Nil can be injected through variables and through id function.
Unimplemented function `id()` is fixed to return `[]` instead of `nil`.
Add variable coerce and predicates after variables.
@tompng tompng force-pushed the fix_xpath_nil_contamination branch from 3f20d0b to f343f5b Compare June 28, 2026 09:23
Copilot AI review requested due to automatic review settings July 7, 2026 03:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comment thread lib/rexml/xpath_parser.rb
Comment on lines +337 to +338
# If evaluated value is not a nodeset, treat it as an empty nodeset.
# TODO: Decide whether REXML should raise type error or keep this behavior.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If evaluated value is not a nodeset, treat it as an empty nodeset.
TODO: Decide whether REXML should raise type error or keep this behavior.

According to the XPath specification, anything other than a node set results in an error.

https://www.w3.org/TR/1999/REC-xpath-19991116/#node-sets

It is an error if the expression to be filtered does not evaluate to a node-set.

It is an error if the expression does not evaluate to a node-set.

I think it would be better to raise an error, as that would make the processing clearer.
What do you think? @kou @tompng

Comment thread lib/rexml/xpath_parser.rb
Comment on lines +346 to +347
# Coerces a variable value to a type that can be used in XPath expressions.
# TODO: Decide whether REXML should warn, raise, or ignore when a variable value is invalid.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Coerces a variable value to a type that can be used in XPath expressions.
TODO: Decide whether REXML should warn, raise, or ignore when a variable value is invalid.

According to the XPath specification, if a variable name is not bound to any value, an error occurs.

https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Basics

A VariableReference evaluates to the value to which the variable name is bound in the set of variable bindings in the context.

I think it would be better to raise an error, as that would make the processing clearer.
What do you think? @kou @tompng

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think referencing unbound variable should raise error. Concern: when to do that, and what kind of error should it raise.

when :variable
  var_name = path_stack.shift
  # Handle unbound variables
  raise unless @variables.key?(var_name)

  # Handle conversion and validation of variable value
  # @variables[var_name]==nil case is handled here
  value = coerce_variable(@variables[var_name])
  ...

Even if we reject unbound variables, this TODO comment for bound variables (reject bound invalid-value variable or convert it to a fallback value "") still remains.

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.

XPath: fix default node detection in functions

3 participants