Ruby : XPath Injection Query (CWE-643)

This commit is contained in:
Maiky
2023-05-11 15:29:54 +02:00
parent a0a8468071
commit 071a77cedc
17 changed files with 716 additions and 0 deletions

View File

@@ -840,6 +840,58 @@ module XmlParserCall {
}
}
/**
* A data-flow node that constructs an XPath expression.
*
* If it is important that the XPath expression is indeed executed, then use `XPathExecution`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `XPathConstruction::Range` instead.
*/
class XPathConstruction extends DataFlow::Node instanceof XPathConstruction::Range {
/** Gets the argument that specifies the XPath expressions to be constructed. */
DataFlow::Node getXPath() { result = super.getXPath() }
}
/** Provides a class for modeling new XPath construction APIs. */
module XPathConstruction {
/**
* A data-flow node that constructs an XPath expression.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `XPathConstruction` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the XPath expressions to be constructed. */
abstract DataFlow::Node getXPath();
}
}
/**
* A data-flow node that executes an XPath expression.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `XPathExecution::Range` instead.
*/
class XPathExecution extends DataFlow::Node instanceof XPathExecution::Range {
/** Gets the argument that specifies the XPath expressions to be executed. */
DataFlow::Node getXPath() { result = super.getXPath() }
}
/** Provides a class for modeling new XPath execution APIs. */
module XPathExecution {
/**
* A data-flow node that executes an XPath expression.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `XPathExecution` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the XPath expressions to be executed. */
abstract DataFlow::Node getXPath();
}
}
/**
* A data-flow node that may represent a database object in an ORM system.
*

View File

@@ -32,3 +32,6 @@ private import codeql.ruby.frameworks.Slim
private import codeql.ruby.frameworks.Sinatra
private import codeql.ruby.frameworks.Twirp
private import codeql.ruby.frameworks.Sqlite3
private import codeql.ruby.frameworks.Rexml
private import codeql.ruby.frameworks.Nokogiri
private import codeql.ruby.frameworks.Libxml

View File

@@ -0,0 +1,48 @@
/**
* Provides modeling for `libxml`, an XML library for Ruby.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for `libxml`, an XML library for Ruby.
*/
module Libxml {
/**
* Flow summary for `libxml`. Wraps a string, parsing it as an XML document.
*/
private class XMLSummary extends SummarizedCallable {
XMLSummary() { this = "LibXML::XML" }
override MethodCall getACall() { result = any(LibXmlRubyXmlParserCall c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call that parses XML. */
private class LibXmlRubyXmlParserCall extends DataFlow::CallNode {
LibXmlRubyXmlParserCall() {
this =
[API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")]
.getMember(["Document", "Parser"])
.getAMethodCall(["file", "io", "string"])
}
DataFlow::Node getInput() { result = this.getArgument(0) }
}
/** Execution of a XPath statement. */
private class LibXmlXPathExecution extends XPathExecution::Range, DataFlow::CallNode {
LibXmlXPathExecution() {
exists(LibXmlRubyXmlParserCall parserCall |
this = parserCall.getAMethodCall(["find", "find_first"])
)
}
override DataFlow::Node getXPath() { result = this.getArgument(0) }
}
}

View File

@@ -0,0 +1,54 @@
/**
* Provides modeling for `nokogiri`, an XML library for Ruby.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for `nokogiri`, an XML library for Ruby.
*/
module Nokogiri {
/**
* Flow summary for `nokogiri`. Wraps a string, parsing it as an XML document.
*/
private class XMLSummary extends SummarizedCallable {
XMLSummary() { this = "Nokogiri::XML.parse" }
override MethodCall getACall() { result = any(NokogiriXmlParserCall p).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call that parses XML. */
private class NokogiriXmlParserCall extends DataFlow::CallNode {
NokogiriXmlParserCall() {
this =
[
API::getTopLevelMember("Nokogiri").getMember("XML"),
API::getTopLevelMember("Nokogiri").getMember("XML").getMember("Document"),
API::getTopLevelMember("Nokogiri")
.getMember("XML")
.getMember("SAX")
.getMember("Parser")
.getInstance()
].getAMethodCall("parse")
}
DataFlow::Node getInput() { result = this.getArgument(0) }
}
/** Execution of a XPath statement. */
private class NokogiriXPathExecution extends XPathExecution::Range, DataFlow::CallNode {
NokogiriXPathExecution() {
exists(NokogiriXmlParserCall parserCall |
this = parserCall.getAMethodCall(["xpath", "at_xpath", "search", "at"])
)
}
override DataFlow::Node getXPath() { result = this.getArgument(0) }
}
}

View File

@@ -0,0 +1,48 @@
/**
* Provides modeling for `rexml`, an XML toolkit for Ruby.
*/
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
/**
* Provides modeling for `rexml`, an XML toolkit for Ruby.
*/
module Rexml {
/**
* Flow summary for `REXML::Document.new()`. This method wraps a string, parsing it as an XML document.
*/
private class XMLSummary extends SummarizedCallable {
XMLSummary() { this = "REXML::Document.new()" }
override MethodCall getACall() { result = any(RexmlParserCall c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
/** A call to `REXML::Document.new`, considered as a XML parsing. */
private class RexmlParserCall extends XmlParserCall::Range, DataFlow::CallNode {
RexmlParserCall() {
this = API::getTopLevelMember("REXML").getMember("Document").getAnInstantiation()
}
override DataFlow::Node getInput() { result = this.getArgument(0) }
/** No option for parsing */
override predicate externalEntitiesEnabled() { none() }
}
/** Execution of a XPath statement. */
private class RexmlXPathExecution extends XPathExecution::Range, DataFlow::CallNode {
RexmlXPathExecution() {
this =
[API::getTopLevelMember("REXML").getMember("XPath"), API::getTopLevelMember("XPath")]
.getAMethodCall(["each", "first", "match"])
}
override DataFlow::Node getXPath() { result = this.getArgument(1) }
}
}

View File

@@ -0,0 +1,54 @@
/**
* Provides class and predicates to track external data that
* may represent malicious xpath query objects.
*
* This module is intended to be imported into a taint-tracking query.
*/
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.RemoteFlowSources
/** Models Xpath Injection related classes and functions */
module XpathInjection {
/** A data flow source for "XPath injection" vulnerabilities. */
abstract class Source extends DataFlow::Node { }
/** A data flow sink for "XPath injection" vulnerabilities */
abstract class Sink extends DataFlow::Node { }
/** A sanitizer for "XPath injection" vulnerabilities. */
abstract class Sanitizer extends DataFlow::Node { }
/**
* A source of remote user input, considered as a flow source.
*/
private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/**
* An execution of an XPath expression, considered as a sink.
*/
private class XPathExecutionAsSink extends Sink {
XPathExecutionAsSink() { this = any(XPathExecution e).getXPath() }
}
/**
* A construction of an XPath expression, considered as a sink.
*/
private class XPathConstructionAsSink extends Sink {
XPathConstructionAsSink() { this = any(XPathConstruction c).getXPath() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
private class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
/**
* An inclusion check against an array of constant strings, considered as a
* sanitizer-guard.
*/
private class StringConstArrayInclusionCallAsSanitizer extends Sanitizer,
StringConstArrayInclusionCallBarrier { }
}

View File

@@ -0,0 +1,24 @@
/**
* Provides a taint-tracking configuration for detecting "Xpath Injection" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `XpathInjection::Configuration` is needed, otherwise
* `XpathInjectionCustomizations` should be imported instead.
*/
private import codeql.ruby.DataFlow
private import codeql.ruby.TaintTracking
import XpathInjectionCustomizations::XpathInjection
/**
* A taint-tracking configuration for detecting "Xpath Injection" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "Xpath Injection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node source) { source instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new experimental query, `rb/xpath-injection`, to detect cases where user input may be embedded into a template's code in an unsafe manner.

View File

@@ -0,0 +1,37 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
If an XPath expression is built using string concatenation, and the components of the concatenation
include user input, it makes it very easy for a user to create a malicious XPath expression.
</p>
</overview>
<recommendation>
<p>
If user input must be included in an XPath expression, either sanitize the data or use variable
references to safely embed it without altering the structure of the expression.
</p>
</recommendation>
<example>
<p>
The following example uses the <code>nokogiri</code>, <code>rexml</code> and <code>libxml</code> XML parsers to parse a string <code>xml</code>.
Then the xpath query is controlled by the user and hence leads to a vulnerability.
</p>
<sample src="examples/XPathBad.rb"/>
<p>
To guard against XPath Injection attacks, the user input should be sanitized.
</p>
<sample src="examples/XPathGood.rb"/>
</example>
<references>
<li>
OWASP:
<a href="https://owasp.org/www-community/attacks/XPATH_Injection">XPath injection</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name XPath query built from user-controlled sources
* @description Building a XPath query from user-controlled sources is vulnerable to insertion of
* malicious Xpath code by the user.
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @precision high
* @id rb/xpath-injection
* @tags security
* external/cwe/cwe-643
*/
import codeql.ruby.DataFlow
import codeql.ruby.security.XpathInjectionQuery
import DataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "XPath expression depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -0,0 +1,45 @@
require 'nokogiri'
require 'rexml'
require 'libxml'
class BadNokogiriController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = Nokogiri::XML.parse(xml)
results = doc.xpath("//#{name}")
end
end
class BadRexmlController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = REXML::Document.new(xml)
results = REXML::XPath.first(doc, "//#{name}")
end
end
class BadLibxmlController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = LibXML::XML::Document.string(xml)
results = doc.find_first("//#{name}")
end
end

View File

@@ -0,0 +1,60 @@
require 'nokogiri'
require 'rexml'
require 'libxml'
class BadNokogiriController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = Nokogiri::XML.parse(xml)
name = if ["foo", "foo2"].include? name
name
else
name = "foo"
end
results = doc.xpath("//#{name}")
end
end
class BadRexmlController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = REXML::Document.new(xml)
name = if ["foo", "foo2"].include? name
name
else
name = "foo"
end
results = REXML::XPath.first(doc, "//#{name}")
end
end
class BadLibxmlController < ActionController::Base
def some_request_handler
name = params["name"]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
doc = LibXML::XML::Document.string(xml)
name = if ["foo", "foo2"].include? name
name
else
name = "foo"
end
results = doc.find_first("//#{name}")
end
end

View File

@@ -0,0 +1,59 @@
require 'libxml'
class FooController < ActionController::Base
def libxml_handler(event:, context:)
name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = LibXML::XML::Document.string(xml)
# GOOD: XPath query is not constructed from user input
results1 = doc.find_first('//foo')
# BAD: XPath query is constructed from user input
results2 = doc.find_first("//#{name}")
# GOOD: XPath query is not constructed from user input
results3 = doc.find('//foo')
# BAD: XPath query is constructed from user input
results4 = doc.find("//#{name}")
end
end
class BarController < ActionController::Base
def libxml_safe_handler(event:, context:)
safe_name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = REXML::Document.new(xml)
# GOOD: barrier guard prevents taint flow
safe_name = if ["foo", "foo2"].include? safe_name
safe_name
else
safe_name = "foo"
end
# GOOD: XPath query is not constructed from unsanitized user input
results5 = doc.find_first("//#{safe_name}")
# GOOD: XPath query is not constructed from unsanitized user input
results6 = doc.find("//#{safe_name}")
end
end

View File

@@ -0,0 +1,88 @@
require 'nokogiri'
class FooController < ActionController::Base
def nokogiri_handler(event:, context:)
name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = Nokogiri::XML.parse(xml)
# GOOD: XPath query is not constructed from user input
results1 = doc.at('//foo')
# BAD: XPath query is constructed from user input
results2 = doc.at("//#{name}")
# GOOD: XPath query is not constructed from user input
results3 = doc.xpath('//foo')
# BAD: XPath query is constructed from user input
results4 = doc.xpath("//#{name}")
# GOOD: XPath query is not constructed from user input
results5 = doc.at_xpath('//foo')
# BAD: XPath query is constructed from user input
results6 = doc.at_xpath("//#{name}")
# GOOD: XPath query is not constructed from user input
doc.xpath('//foo').each do |element|
puts element.text
end
# BAD: XPath query constructed from user input
doc.xpath("//#{name}").each do |element|
puts element.text
end
# GOOD: XPath query is not constructed from user input
doc.search('//foo').each do |element|
puts element.text
end
# BAD: XPath query constructed from user input
doc.search("//#{name}").each do |element|
puts element.text
end
end
end
class BarController < ActionController::Base
def nokogiri_safe_handler(event:, context:)
safe_name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = Nokogiri::XML.parse(xml)
# GOOD: barrier guard prevents taint flow
safe_name = if ["foo", "foo2"].include? safe_name
safe_name
else
safe_name = "foo"
end
# GOOD: XPath query is not constructed from unsanitized user input
results7 = doc.at("//#{safe_name}")
# GOOD: XPath query is not constructed from unsanitized user input
results8 = doc.xpath("//#{safe_name}")
# GOOD: XPath query is not constructed from unsanitized user input
results9 = doc.at_xpath("//#{safe_name}")
end
end

View File

@@ -0,0 +1,69 @@
require 'rexml'
class FooController < ActionController::Base
def rexml_handler(event:, context:)
name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = REXML::Document.new(xml)
# GOOD: XPath query is not constructed from user input
results1 = REXML::XPath.first(doc, "//foo")
# BAD: XPath query is constructed from user input
results2 = REXML::XPath.first(doc, "//#{name}")
# GOOD: XPath query is not constructed from user input
results3 = REXML::XPath.match(doc, "//foo", nil)
# BAD: XPath query is constructed from user input
results4 = REXML::XPath.match(doc, "//#{name}", nil)
# GOOD: XPath query is not constructed from user input
REXML::XPath.each(doc, "//foo") do |element|
puts element.text
end
# BAD: XPath query constructed from user input
REXML::XPath.each(doc, "//#{name}") do |element|
puts element.text
end
end
end
class BarController < ActionController::Base
def rexml_safe_handler(event:, context:)
safe_name = params[:user_name]
xml = <<-XML
<root>
<foo>bar</foo>
<password>THIS IS SECRET</password>
</root>
XML
# Parse the XML
doc = REXML::Document.new(xml)
# GOOD: barrier guard prevents taint flow
safe_name = if ["foo", "foo2"].include? safe_name
safe_name
else
safe_name = "foo"
end
# GOOD: XPath query is not constructed from unsanitized user input
results5 = REXML::XPath.first(doc, "//#{safe_name}")
# GOOD: XPath query is not constructed from unsanitized user input
results6 = REXML::XPath.match(doc, "//#{safe_name}", nil)
end
end

View File

@@ -0,0 +1,49 @@
edges
| LibxmlInjection.rb:5:5:5:8 | name | LibxmlInjection.rb:21:31:21:41 | "//#{...}" |
| LibxmlInjection.rb:5:5:5:8 | name | LibxmlInjection.rb:27:25:27:35 | "//#{...}" |
| LibxmlInjection.rb:5:12:5:17 | call to params | LibxmlInjection.rb:5:12:5:29 | ...[...] |
| LibxmlInjection.rb:5:12:5:29 | ...[...] | LibxmlInjection.rb:5:5:5:8 | name |
| NokogiriInjection.rb:5:5:5:8 | name | NokogiriInjection.rb:21:23:21:33 | "//#{...}" |
| NokogiriInjection.rb:5:5:5:8 | name | NokogiriInjection.rb:27:26:27:36 | "//#{...}" |
| NokogiriInjection.rb:5:5:5:8 | name | NokogiriInjection.rb:33:29:33:39 | "//#{...}" |
| NokogiriInjection.rb:5:5:5:8 | name | NokogiriInjection.rb:41:15:41:25 | "//#{...}" |
| NokogiriInjection.rb:5:5:5:8 | name | NokogiriInjection.rb:51:16:51:26 | "//#{...}" |
| NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:5:12:5:29 | ...[...] |
| NokogiriInjection.rb:5:12:5:29 | ...[...] | NokogiriInjection.rb:5:5:5:8 | name |
| RexmlInjection.rb:5:5:5:8 | name | RexmlInjection.rb:21:40:21:50 | "//#{...}" |
| RexmlInjection.rb:5:5:5:8 | name | RexmlInjection.rb:27:40:27:50 | "//#{...}" |
| RexmlInjection.rb:5:5:5:8 | name | RexmlInjection.rb:35:28:35:38 | "//#{...}" |
| RexmlInjection.rb:5:12:5:17 | call to params | RexmlInjection.rb:5:12:5:29 | ...[...] |
| RexmlInjection.rb:5:12:5:29 | ...[...] | RexmlInjection.rb:5:5:5:8 | name |
nodes
| LibxmlInjection.rb:5:5:5:8 | name | semmle.label | name |
| LibxmlInjection.rb:5:12:5:17 | call to params | semmle.label | call to params |
| LibxmlInjection.rb:5:12:5:29 | ...[...] | semmle.label | ...[...] |
| LibxmlInjection.rb:21:31:21:41 | "//#{...}" | semmle.label | "//#{...}" |
| LibxmlInjection.rb:27:25:27:35 | "//#{...}" | semmle.label | "//#{...}" |
| NokogiriInjection.rb:5:5:5:8 | name | semmle.label | name |
| NokogiriInjection.rb:5:12:5:17 | call to params | semmle.label | call to params |
| NokogiriInjection.rb:5:12:5:29 | ...[...] | semmle.label | ...[...] |
| NokogiriInjection.rb:21:23:21:33 | "//#{...}" | semmle.label | "//#{...}" |
| NokogiriInjection.rb:27:26:27:36 | "//#{...}" | semmle.label | "//#{...}" |
| NokogiriInjection.rb:33:29:33:39 | "//#{...}" | semmle.label | "//#{...}" |
| NokogiriInjection.rb:41:15:41:25 | "//#{...}" | semmle.label | "//#{...}" |
| NokogiriInjection.rb:51:16:51:26 | "//#{...}" | semmle.label | "//#{...}" |
| RexmlInjection.rb:5:5:5:8 | name | semmle.label | name |
| RexmlInjection.rb:5:12:5:17 | call to params | semmle.label | call to params |
| RexmlInjection.rb:5:12:5:29 | ...[...] | semmle.label | ...[...] |
| RexmlInjection.rb:21:40:21:50 | "//#{...}" | semmle.label | "//#{...}" |
| RexmlInjection.rb:27:40:27:50 | "//#{...}" | semmle.label | "//#{...}" |
| RexmlInjection.rb:35:28:35:38 | "//#{...}" | semmle.label | "//#{...}" |
subpaths
#select
| LibxmlInjection.rb:21:31:21:41 | "//#{...}" | LibxmlInjection.rb:5:12:5:17 | call to params | LibxmlInjection.rb:21:31:21:41 | "//#{...}" | XPath expression depends on a $@. | LibxmlInjection.rb:5:12:5:17 | call to params | user-provided value |
| LibxmlInjection.rb:27:25:27:35 | "//#{...}" | LibxmlInjection.rb:5:12:5:17 | call to params | LibxmlInjection.rb:27:25:27:35 | "//#{...}" | XPath expression depends on a $@. | LibxmlInjection.rb:5:12:5:17 | call to params | user-provided value |
| NokogiriInjection.rb:21:23:21:33 | "//#{...}" | NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:21:23:21:33 | "//#{...}" | XPath expression depends on a $@. | NokogiriInjection.rb:5:12:5:17 | call to params | user-provided value |
| NokogiriInjection.rb:27:26:27:36 | "//#{...}" | NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:27:26:27:36 | "//#{...}" | XPath expression depends on a $@. | NokogiriInjection.rb:5:12:5:17 | call to params | user-provided value |
| NokogiriInjection.rb:33:29:33:39 | "//#{...}" | NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:33:29:33:39 | "//#{...}" | XPath expression depends on a $@. | NokogiriInjection.rb:5:12:5:17 | call to params | user-provided value |
| NokogiriInjection.rb:41:15:41:25 | "//#{...}" | NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:41:15:41:25 | "//#{...}" | XPath expression depends on a $@. | NokogiriInjection.rb:5:12:5:17 | call to params | user-provided value |
| NokogiriInjection.rb:51:16:51:26 | "//#{...}" | NokogiriInjection.rb:5:12:5:17 | call to params | NokogiriInjection.rb:51:16:51:26 | "//#{...}" | XPath expression depends on a $@. | NokogiriInjection.rb:5:12:5:17 | call to params | user-provided value |
| RexmlInjection.rb:21:40:21:50 | "//#{...}" | RexmlInjection.rb:5:12:5:17 | call to params | RexmlInjection.rb:21:40:21:50 | "//#{...}" | XPath expression depends on a $@. | RexmlInjection.rb:5:12:5:17 | call to params | user-provided value |
| RexmlInjection.rb:27:40:27:50 | "//#{...}" | RexmlInjection.rb:5:12:5:17 | call to params | RexmlInjection.rb:27:40:27:50 | "//#{...}" | XPath expression depends on a $@. | RexmlInjection.rb:5:12:5:17 | call to params | user-provided value |
| RexmlInjection.rb:35:28:35:38 | "//#{...}" | RexmlInjection.rb:5:12:5:17 | call to params | RexmlInjection.rb:35:28:35:38 | "//#{...}" | XPath expression depends on a $@. | RexmlInjection.rb:5:12:5:17 | call to params | user-provided value |

View File

@@ -0,0 +1 @@
experimental/xpath-injection/XpathInjection.ql