mirror of
https://github.com/github/codeql.git
synced 2026-04-27 01:35:13 +02:00
XXE query
This commit is contained in:
@@ -503,3 +503,38 @@ module CodeExecution {
|
||||
abstract DataFlow::Node getCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that parses XML content.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `XmlParserCall::Range` instead.
|
||||
*/
|
||||
class XmlParserCall extends DataFlow::Node {
|
||||
XmlParserCall::Range range;
|
||||
|
||||
XmlParserCall() { this = range }
|
||||
|
||||
/** Gets the argument that specifies the XML content to be parsed. */
|
||||
DataFlow::Node getInput() { result = range.getInput() }
|
||||
|
||||
/** Holds if this XML parser call is configured to process external entities */
|
||||
predicate externalEntitiesEnabled() { range.externalEntitiesEnabled() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new XML parsing APIs. */
|
||||
module XmlParserCall {
|
||||
/**
|
||||
* A data-flow node that parses XML content.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `class XmlParserCall` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument that specifies the XML content to be parsed. */
|
||||
abstract DataFlow::Node getInput();
|
||||
|
||||
/** Holds if this XML parser call is configured to process external entities */
|
||||
abstract predicate externalEntitiesEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,4 @@ private import codeql.ruby.frameworks.ActionView
|
||||
private import codeql.ruby.frameworks.StandardLibrary
|
||||
private import codeql.ruby.frameworks.Files
|
||||
private import codeql.ruby.frameworks.HttpClients
|
||||
private import codeql.ruby.frameworks.XmlParsing
|
||||
|
||||
103
ql/lib/codeql/ruby/frameworks/XmlParsing.qll
Normal file
103
ql/lib/codeql/ruby/frameworks/XmlParsing.qll
Normal file
@@ -0,0 +1,103 @@
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.typetracking.TypeTracker
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.controlflow.CfgNodes as CfgNodes
|
||||
|
||||
private class NokogiriXmlParserCall extends XmlParserCall::Range, 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")
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = this.getArgument(0) }
|
||||
|
||||
override predicate externalEntitiesEnabled() {
|
||||
this.getArgument(3) = trackNoEnt()
|
||||
or
|
||||
this.asExpr()
|
||||
.getExpr()
|
||||
.(MethodCall)
|
||||
.getBlock()
|
||||
.getAStmt()
|
||||
.getAChild*()
|
||||
.(MethodCall)
|
||||
.getMethodName() = "noent"
|
||||
}
|
||||
}
|
||||
|
||||
private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::CallNode {
|
||||
LibXmlRubyXmlParserCall() {
|
||||
this =
|
||||
[API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")]
|
||||
.getMember(["Document", "Parser"])
|
||||
.getAMethodCall(["file", "io", "string"])
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = this.getArgument(0) }
|
||||
|
||||
override predicate externalEntitiesEnabled() {
|
||||
exists(Pair pair |
|
||||
pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
pair.getKey().(Literal).getValueText() = "options" and
|
||||
trackNoEnt().asExpr().getExpr() = pair.getValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::LocalSourceNode trackNoEnt(TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
result.asExpr().getExpr().(IntegerLiteral).getValue().bitAnd(2) = 2
|
||||
or
|
||||
result =
|
||||
API::getTopLevelMember("Nokogiri")
|
||||
.getMember("XML")
|
||||
.getMember("ParseOptions")
|
||||
.getMember("NOENT")
|
||||
.getAUse()
|
||||
or
|
||||
result =
|
||||
[API::getTopLevelMember("LibXML").getMember("XML"), API::getTopLevelMember("XML")]
|
||||
.getMember("Options")
|
||||
.getMember("NOENT")
|
||||
.getAUse()
|
||||
or
|
||||
result.asExpr().getExpr() instanceof BitwiseOrExpr and
|
||||
result.asExpr().(CfgNodes::ExprNodes::OperationCfgNode).getAnOperand() = trackNoEnt().asExpr()
|
||||
or
|
||||
result =
|
||||
API::getTopLevelMember("Nokogiri")
|
||||
.getMember("XML")
|
||||
.getMember("ParseOptions")
|
||||
.getAnInstantiation() and
|
||||
result.asExpr().(CfgNodes::ExprNodes::CallCfgNode).getArgument(0) = trackNoEnt().asExpr()
|
||||
or
|
||||
exists(CfgNodes::ExprNodes::CallCfgNode call |
|
||||
call.getExpr().(MethodCall).getMethodName() = "noent" and
|
||||
(
|
||||
result.asExpr() = call
|
||||
or
|
||||
result.flowsTo(any(DataFlow::Node n | n.asExpr() = call.getReceiver()))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(CfgNodes::ExprNodes::CallCfgNode call |
|
||||
trackNoEnt().asExpr() = call.getReceiver() and
|
||||
result.asExpr() = call
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(TypeTracker t2 | result = trackNoEnt(t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::Node trackNoEnt() { trackNoEnt(TypeTracker::end()).flowsTo(result) }
|
||||
Reference in New Issue
Block a user