mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into useStringComp
This commit is contained in:
78
cpp/ql/src/Security/CWE/CWE-611/Libxml2.qll
Normal file
78
cpp/ql/src/Security/CWE/CWE-611/Libxml2.qll
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Models the libxml2 XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to a `libxml2` function that parses XML.
|
||||
*/
|
||||
class Libxml2ParseCall extends FunctionCall {
|
||||
int optionsArg;
|
||||
|
||||
Libxml2ParseCall() {
|
||||
exists(string fname | this.getTarget().getName() = fname |
|
||||
fname = "xmlCtxtUseOptions" and optionsArg = 1
|
||||
or
|
||||
fname = "xmlReadFile" and optionsArg = 2
|
||||
or
|
||||
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
|
||||
optionsArg = 3
|
||||
or
|
||||
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
|
||||
or
|
||||
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
|
||||
or
|
||||
fname = "xmlCtxtReadIO" and optionsArg = 6
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies `xmlParserOption`s.
|
||||
*/
|
||||
Expr getOptions() { result = this.getArgument(optionsArg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `xmlParserOption` for `libxml2` that is considered unsafe.
|
||||
*/
|
||||
class Libxml2BadOption extends EnumConstant {
|
||||
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* The libxml2 XML library.
|
||||
*/
|
||||
class LibXml2Library extends XmlLibrary {
|
||||
LibXml2Library() { this = "LibXml2Library" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is an `options` argument on a libxml2 parse call that specifies
|
||||
// at least one unsafe option.
|
||||
//
|
||||
// note: we don't need to track an XML object for libxml2, so we don't
|
||||
// really need data flow. Nevertheless we jam it into this configuration,
|
||||
// with matching sources and sinks. This allows results to be presented by
|
||||
// the same query, in a consistent way as other results with flow paths.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2" and
|
||||
exists(Libxml2BadOption opt |
|
||||
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the `options` argument on a `libxml2` parse call.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2"
|
||||
)
|
||||
}
|
||||
}
|
||||
55
cpp/ql/src/Security/CWE/CWE-611/XML.qll
Normal file
55
cpp/ql/src/Security/CWE/CWE-611/XML.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Provides a abstract classes for modeling XML libraries. This design is
|
||||
* currently specialized for the purposes of the XXE query.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import Xerces
|
||||
import Libxml2
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XxeFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XxeFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML library or interface.
|
||||
*/
|
||||
abstract class XmlLibrary extends string {
|
||||
bindingset[this]
|
||||
XmlLibrary() { any() } // required characteristic predicate
|
||||
|
||||
/**
|
||||
* Holds if `node` is the source node for a potentially unsafe configuration
|
||||
* object for this XML library, along with `flowstate` representing its
|
||||
* initial state.
|
||||
*/
|
||||
abstract predicate configurationSource(DataFlow::Node node, string flowstate);
|
||||
|
||||
/**
|
||||
* Holds if `node` is the sink node where an unsafe configuration object is
|
||||
* used to interpret XML.
|
||||
*/
|
||||
abstract predicate configurationSink(DataFlow::Node node, string flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XxeFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XxeFlowStateTransformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(transform(x)) = transform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XxeFlowState transform(XxeFlowState flowstate);
|
||||
}
|
||||
@@ -13,344 +13,8 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import XML
|
||||
import DataFlow::PathGraph
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XXEFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XXEFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XXEFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XXEFlowStateTransformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(transform(x)) = transform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XXEFlowState transform(XXEFlowState flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDOMParserClass extends Class {
|
||||
AbstractDOMParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDOMParserClass extends Class {
|
||||
XercesDOMParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser` class.
|
||||
*/
|
||||
class DomLSParserClass extends Class {
|
||||
DomLSParserClass() { this.hasName("DOMLSParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAXParser` class.
|
||||
*/
|
||||
class SaxParserClass extends Class {
|
||||
SaxParserClass() { this.hasName("SAXParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAX2XMLReader` class.
|
||||
*/
|
||||
class Sax2XmlReader extends Class {
|
||||
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
|
||||
*
|
||||
* These flow states take the form `Xerces-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "Xerces-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "Xerces-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of an `AbstractDOMParser` or
|
||||
* `SAXParser` object.
|
||||
*/
|
||||
class XercesFlowState extends XXEFlowState {
|
||||
XercesFlowState() { encodeXercesFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
|
||||
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
(
|
||||
f.getDeclaringType() instanceof AbstractDOMParserClass or
|
||||
f.getDeclaringType() instanceof SaxParserClass
|
||||
) and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDOMParserClass and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
|
||||
*/
|
||||
class FeatureDisableDefaultEntityResolution extends Variable {
|
||||
FeatureDisableDefaultEntityResolution() {
|
||||
this.getName() = "fgXercesDisableDefaultEntityResolution" and
|
||||
this.getDeclaringType().getName() = "XMLUni"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* Transforms the flow state through the qualifier according to this setting.
|
||||
*/
|
||||
class SetFeatureTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
SetFeatureTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
|
||||
this = call.getQualifier() and
|
||||
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
|
||||
FeatureDisableDefaultEntityResolution and
|
||||
newValue = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser.getDomConfig` function.
|
||||
*/
|
||||
class GetDomConfig extends Function {
|
||||
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMConfiguration.setParameter` function.
|
||||
*/
|
||||
class DomConfigurationSetParameter extends Function {
|
||||
DomConfigurationSetParameter() {
|
||||
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `DOMConfiguration.setParameter`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* This is a slightly more complex transformer because the qualifier is a
|
||||
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
|
||||
* is *that* qualifier we want to transform the flow state of.
|
||||
*/
|
||||
class DomConfigurationSetParameterTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DomConfigurationSetParameterTransformer() {
|
||||
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
|
||||
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
|
||||
getDomConfigCall.getTarget() instanceof GetDomConfig and
|
||||
this = getDomConfigCall.getQualifier() and
|
||||
// `setParameterCall` is a call to `setParameter` on the return value of
|
||||
// the same call to `DOMLSParser.getDomConfig`.
|
||||
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
|
||||
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
|
||||
// the parameter being set is
|
||||
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
|
||||
instanceof FeatureDisableDefaultEntityResolution and
|
||||
// the value being set is `newValue`.
|
||||
newValue = setParameterCall.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser.parse`, `DOMLSParserClass.parse`, `SAXParser.parse`
|
||||
* or `SAX2XMLReader.parse` method.
|
||||
*/
|
||||
class ParseFunction extends Function {
|
||||
ParseFunction() {
|
||||
this.getClassAndName("parse") instanceof AbstractDOMParserClass or
|
||||
this.getClassAndName("parse") instanceof DomLSParserClass or
|
||||
this.getClassAndName("parse") instanceof SaxParserClass or
|
||||
this.getClassAndName("parse") instanceof Sax2XmlReader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `DOMLSParser`
|
||||
* object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
|
||||
* object.
|
||||
*/
|
||||
class CreateXmlReader extends Function {
|
||||
CreateXmlReader() {
|
||||
this.hasName("createXMLReader") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `libxml2` function that parses XML.
|
||||
*/
|
||||
class Libxml2ParseCall extends FunctionCall {
|
||||
int optionsArg;
|
||||
|
||||
Libxml2ParseCall() {
|
||||
exists(string fname | this.getTarget().getName() = fname |
|
||||
fname = "xmlCtxtUseOptions" and optionsArg = 1
|
||||
or
|
||||
fname = "xmlReadFile" and optionsArg = 2
|
||||
or
|
||||
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
|
||||
optionsArg = 3
|
||||
or
|
||||
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
|
||||
or
|
||||
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
|
||||
or
|
||||
fname = "xmlCtxtReadIO" and optionsArg = 6
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies `xmlParserOption`s.
|
||||
*/
|
||||
Expr getOptions() { result = this.getArgument(optionsArg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `xmlParserOption` for `libxml2` that is considered unsafe.
|
||||
*/
|
||||
class Libxml2BadOption extends EnumConstant {
|
||||
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking XML objects and their states.
|
||||
@@ -359,85 +23,25 @@ class XXEConfiguration extends DataFlow::Configuration {
|
||||
XXEConfiguration() { this = "XXEConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the write on `this` of a call to the `SAXParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the result of a call to `createXMLReader`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateXmlReader and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is an `options` argument on a `libxml2` parse call that specifies
|
||||
// at least one unsafe option.
|
||||
//
|
||||
// note: we don't need to track an XML object for `libxml2`, so we don't
|
||||
// really need data flow. Nevertheless we jam it into this configuration,
|
||||
// with matching sources and sinks. This allows results to be presented by
|
||||
// the same query, in a consistent way as other results with flow paths.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2" and
|
||||
exists(Libxml2BadOption opt |
|
||||
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
|
||||
0
|
||||
)
|
||||
)
|
||||
any(XmlLibrary l).configurationSource(node, flowstate)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `parse`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof ParseFunction and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
or
|
||||
// sink is the `options` argument on a `libxml2` parse call.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2"
|
||||
)
|
||||
any(XmlLibrary l).configurationSink(node, flowstate)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, string state1, DataFlow::Node node2, string state2
|
||||
) {
|
||||
// create additional flow steps for `XXEFlowStateTransformer`s
|
||||
state2 = node2.asConvertedExpr().(XXEFlowStateTransformer).transform(state1) and
|
||||
// create additional flow steps for `XxeFlowStateTransformer`s
|
||||
state2 = node2.asConvertedExpr().(XxeFlowStateTransformer).transform(state1) and
|
||||
DataFlow::simpleLocalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, string flowstate) {
|
||||
// when the flowstate is transformed at a call node, block the original
|
||||
// flowstate value.
|
||||
node.asConvertedExpr().(XXEFlowStateTransformer).transform(flowstate) != flowstate
|
||||
node.asConvertedExpr().(XxeFlowStateTransformer).transform(flowstate) != flowstate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
376
cpp/ql/src/Security/CWE/CWE-611/Xerces.qll
Normal file
376
cpp/ql/src/Security/CWE/CWE-611/Xerces.qll
Normal file
@@ -0,0 +1,376 @@
|
||||
/**
|
||||
* Models the Xerces XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
|
||||
*
|
||||
* These flow states take the form `Xerces-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "Xerces-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "Xerces-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of an `AbstractDOMParser` or
|
||||
* `SAXParser` object.
|
||||
*/
|
||||
class XercesFlowState extends XxeFlowState {
|
||||
XercesFlowState() { encodeXercesFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDomParserClass extends Class {
|
||||
AbstractDomParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDomParserClass extends Class {
|
||||
XercesDomParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` interface for the Xerces XML library.
|
||||
*/
|
||||
class XercesDomParserLibrary extends XmlLibrary {
|
||||
XercesDomParserLibrary() { this = "XercesDomParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDomParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser` class.
|
||||
*/
|
||||
class DomLSParserClass extends Class {
|
||||
DomLSParserClass() { this.hasName("DOMLSParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `DOMLSParser`
|
||||
* object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The createLSParser interface for the Xerces XML library.
|
||||
*/
|
||||
class CreateLSParserLibrary extends XmlLibrary {
|
||||
CreateLSParserLibrary() { this = "CreateLSParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAXParser` class.
|
||||
*/
|
||||
class SaxParserClass extends Class {
|
||||
SaxParserClass() { this.hasName("SAXParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAX2XMLReader` class.
|
||||
*/
|
||||
class Sax2XmlReader extends Class {
|
||||
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAXParser interface for the Xerces XML library.
|
||||
*/
|
||||
class SaxParserLibrary extends XmlLibrary {
|
||||
SaxParserLibrary() { this = "SaxParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `SAXParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAXParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
|
||||
* object.
|
||||
*/
|
||||
class CreateXmlReader extends Function {
|
||||
CreateXmlReader() {
|
||||
this.hasName("createXMLReader") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAX2XMLReader interface for the Xerces XML library.
|
||||
*/
|
||||
class Sax2XmlReaderLibrary extends XmlLibrary {
|
||||
Sax2XmlReaderLibrary() { this = "Sax2XmlReaderLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createXMLReader`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateXmlReader and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
|
||||
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
(
|
||||
f.getDeclaringType() instanceof AbstractDomParserClass or
|
||||
f.getDeclaringType() instanceof SaxParserClass
|
||||
) and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDomParserClass and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
|
||||
*/
|
||||
class FeatureDisableDefaultEntityResolution extends Variable {
|
||||
FeatureDisableDefaultEntityResolution() {
|
||||
this.getName() = "fgXercesDisableDefaultEntityResolution" and
|
||||
this.getDeclaringType().getName() = "XMLUni"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* Transforms the flow state through the qualifier according to this setting.
|
||||
*/
|
||||
class SetFeatureTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
SetFeatureTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
|
||||
this = call.getQualifier() and
|
||||
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
|
||||
FeatureDisableDefaultEntityResolution and
|
||||
newValue = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser.getDomConfig` function.
|
||||
*/
|
||||
class GetDomConfig extends Function {
|
||||
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMConfiguration.setParameter` function.
|
||||
*/
|
||||
class DomConfigurationSetParameter extends Function {
|
||||
DomConfigurationSetParameter() {
|
||||
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `DOMConfiguration.setParameter`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* This is a slightly more complex transformer because the qualifier is a
|
||||
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
|
||||
* is *that* qualifier we want to transform the flow state of.
|
||||
*/
|
||||
class DomConfigurationSetParameterTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DomConfigurationSetParameterTransformer() {
|
||||
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
|
||||
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
|
||||
getDomConfigCall.getTarget() instanceof GetDomConfig and
|
||||
this = getDomConfigCall.getQualifier() and
|
||||
// `setParameterCall` is a call to `setParameter` on the return value of
|
||||
// the same call to `DOMLSParser.getDomConfig`.
|
||||
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
|
||||
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
|
||||
// the parameter being set is
|
||||
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
|
||||
instanceof FeatureDisableDefaultEntityResolution and
|
||||
// the value being set is `newValue`.
|
||||
newValue = setParameterCall.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
|
||||
Dapper,55,,,,,,55,,,,
|
||||
Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,,
|
||||
Microsoft.EntityFrameworkCore,6,,,,,,6,,,,
|
||||
Microsoft.Extensions.Primitives,,,54,,,,,,,54,
|
||||
Microsoft.VisualBasic,,,4,,,,,,,,4
|
||||
MySql.Data.MySqlClient,48,,,,,,48,,,,
|
||||
|
||||
|
@@ -9,6 +9,6 @@ C# framework & library support
|
||||
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
|
||||
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
|
||||
System,"``System.*``, ``System``",3,2336,28,5
|
||||
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,131,
|
||||
Totals,,3,2492,353,5
|
||||
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,137,
|
||||
Totals,,3,2492,359,5
|
||||
|
||||
|
||||
@@ -781,11 +781,12 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
|
||||
exists(DataFlowCall call |
|
||||
bindingset[ret]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnNodeExt ret, OutNodeExt out) {
|
||||
exists(DataFlowCall call, ReturnKindExt rk |
|
||||
result = summaryArgParam0(call, arg) and
|
||||
out = rk.getAnOutNode(call)
|
||||
pragma[only_bind_out](ret).getKind() = pragma[only_bind_into](rk) and
|
||||
out = pragma[only_bind_into](rk).getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -797,9 +798,8 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) {
|
||||
exists(ReturnKindExt rk, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and
|
||||
ret.getKind() = rk
|
||||
exists(ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), ret, preservesValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -811,10 +811,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, ret, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -826,10 +825,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,14 @@ function RegisterExtractorPack(id)
|
||||
'Semmle.Extraction.CSharp.Driver'
|
||||
if OperatingSystem == 'windows' then extractor = extractor .. '.exe' end
|
||||
local windowsMatchers = {
|
||||
CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor,
|
||||
{prepend = {'--dotnetexec', '--cil'}}),
|
||||
CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor, {
|
||||
prepend = {'--dotnetexec', '--cil'},
|
||||
order = ORDER_BEFORE
|
||||
}),
|
||||
CreatePatternMatcher({'^csc.*%.exe$'}, MatchCompilerName, extractor, {
|
||||
prepend = {'--compiler', '"${compiler}"', '--cil'}
|
||||
prepend = {'--compiler', '"${compiler}"', '--cil'},
|
||||
order = ORDER_BEFORE
|
||||
|
||||
}),
|
||||
CreatePatternMatcher({'^fakes.*%.exe$', 'moles.*%.exe'},
|
||||
MatchCompilerName, nil, {trace = false})
|
||||
@@ -14,23 +18,28 @@ function RegisterExtractorPack(id)
|
||||
local posixMatchers = {
|
||||
CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName,
|
||||
extractor, {
|
||||
prepend = {'--compiler', '"${compiler}"', '--cil'}
|
||||
prepend = {'--compiler', '"${compiler}"', '--cil'},
|
||||
order = ORDER_BEFORE
|
||||
|
||||
}),
|
||||
CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName,
|
||||
extractor, {prepend = {'--dotnetexec', '--cil'}}),
|
||||
function(compilerName, compilerPath, compilerArguments, _languageId)
|
||||
extractor, {
|
||||
prepend = {'--dotnetexec', '--cil'},
|
||||
order = ORDER_BEFORE
|
||||
}), function(compilerName, compilerPath, compilerArguments, _languageId)
|
||||
if MatchCompilerName('^msbuild$', compilerName, compilerPath,
|
||||
compilerArguments) or
|
||||
MatchCompilerName('^xbuild$', compilerName, compilerPath,
|
||||
compilerArguments) then
|
||||
return {
|
||||
replace = true,
|
||||
invocations = {
|
||||
BuildExtractorInvocation(id, compilerPath, compilerPath,
|
||||
compilerArguments, nil, {
|
||||
'/p:UseSharedCompilation=false'
|
||||
})
|
||||
}
|
||||
order = ORDER_REPLACE,
|
||||
invocation = BuildExtractorInvocation(id, compilerPath,
|
||||
compilerPath,
|
||||
compilerArguments,
|
||||
nil, {
|
||||
'/p:UseSharedCompilation=false'
|
||||
})
|
||||
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -144,17 +144,6 @@ class NumberType extends RefType {
|
||||
NumberType() { exists(TypeNumber number | hasDescendant(number, this)) }
|
||||
}
|
||||
|
||||
/** A numeric type, including both primitive and boxed types. */
|
||||
class NumericType extends Type {
|
||||
NumericType() {
|
||||
exists(string name |
|
||||
name = [this.(PrimitiveType).getName(), this.(BoxedType).getPrimitiveType().getName()]
|
||||
|
|
||||
name = ["byte", "short", "int", "long", "double", "float"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An immutable type. */
|
||||
class ImmutableType extends Type {
|
||||
ImmutableType() {
|
||||
|
||||
@@ -153,6 +153,15 @@ class KtComment extends Top, @ktcomment {
|
||||
/** Gets the full text of this comment. */
|
||||
string getText() { ktComments(this, _, result) }
|
||||
|
||||
/** Holds if this comment is an EOL comment. */
|
||||
predicate isEolComment() { ktComments(this, 1, _) }
|
||||
|
||||
/** Holds if this comment is a block comment. */
|
||||
predicate isBlockComment() { ktComments(this, 2, _) }
|
||||
|
||||
/** Holds if this comment is a KDoc comment. */
|
||||
predicate isDocComment() { ktComments(this, 3, _) }
|
||||
|
||||
/** Gets the sections of this comment. */
|
||||
KtCommentSection getSections() { ktCommentSections(result, this, _) }
|
||||
|
||||
|
||||
@@ -1248,6 +1248,17 @@ class CharacterType extends Type {
|
||||
}
|
||||
}
|
||||
|
||||
/** A numeric type, including both primitive and boxed types. */
|
||||
class NumericType extends Type {
|
||||
NumericType() {
|
||||
exists(string name |
|
||||
name = [this.(PrimitiveType).getName(), this.(BoxedType).getPrimitiveType().getName()]
|
||||
|
|
||||
name = ["byte", "short", "int", "long", "double", "float"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A numeric or character type, which may be either a primitive or a boxed type. */
|
||||
class NumericOrCharType extends Type {
|
||||
NumericOrCharType() {
|
||||
|
||||
@@ -781,11 +781,12 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
|
||||
exists(DataFlowCall call |
|
||||
bindingset[ret]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnNodeExt ret, OutNodeExt out) {
|
||||
exists(DataFlowCall call, ReturnKindExt rk |
|
||||
result = summaryArgParam0(call, arg) and
|
||||
out = rk.getAnOutNode(call)
|
||||
pragma[only_bind_out](ret).getKind() = pragma[only_bind_into](rk) and
|
||||
out = pragma[only_bind_into](rk).getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -797,9 +798,8 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) {
|
||||
exists(ReturnKindExt rk, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and
|
||||
ret.getKind() = rk
|
||||
exists(ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), ret, preservesValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -811,10 +811,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, ret, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -826,10 +825,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,6 @@ private class IntentFlagsOrDataChangedSanitizer extends IntentUriPermissionManip
|
||||
* ```
|
||||
*/
|
||||
private class IntentFlagsOrDataCheckedGuard extends IntentUriPermissionManipulationGuard {
|
||||
Expr condition;
|
||||
|
||||
IntentFlagsOrDataCheckedGuard() { intentFlagsOrDataChecked(this, _, _) }
|
||||
|
||||
override predicate checks(Expr e, boolean branch) { intentFlagsOrDataChecked(this, e, branch) }
|
||||
|
||||
@@ -148,8 +148,6 @@ private predicate isDisallowedWord(CompileTimeConstantExpr word) {
|
||||
|
||||
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */
|
||||
class PathTraversalGuard extends Guard instanceof MethodAccess {
|
||||
Expr checked;
|
||||
|
||||
PathTraversalGuard() {
|
||||
super.getMethod().getDeclaringType() instanceof TypeString and
|
||||
super.getMethod().hasName(["contains", "indexOf"]) and
|
||||
|
||||
@@ -4,9 +4,19 @@ import java
|
||||
* A class representing line comments in Java, which is simply Javadoc restricted
|
||||
* to EOL comments, with an extra accessor used by the InlineExpectations core code
|
||||
*/
|
||||
class ExpectationComment extends Javadoc {
|
||||
ExpectationComment() { isEolComment(this) }
|
||||
|
||||
abstract class ExpectationComment extends Top {
|
||||
/** Gets the contents of the given comment, _without_ the preceding comment marker (`//`). */
|
||||
string getContents() { result = this.getChild(0).toString() }
|
||||
abstract string getContents();
|
||||
}
|
||||
|
||||
private class JavadocExpectationComment extends Javadoc, ExpectationComment {
|
||||
JavadocExpectationComment() { isEolComment(this) }
|
||||
|
||||
override string getContents() { result = this.getChild(0).toString() }
|
||||
}
|
||||
|
||||
private class KtExpectationComment extends KtComment, ExpectationComment {
|
||||
KtExpectationComment() { this.isEolComment() }
|
||||
|
||||
override string getContents() { result = this.getText().suffix(2).trim() }
|
||||
}
|
||||
|
||||
@@ -330,8 +330,6 @@ module ClientRequest {
|
||||
* A model of a URL request made using `require("needle")(...)`.
|
||||
*/
|
||||
class PromisedNeedleRequest extends ClientRequest::Range {
|
||||
DataFlow::Node url;
|
||||
|
||||
PromisedNeedleRequest() { this = DataFlow::moduleImport("needle").getACall() }
|
||||
|
||||
override DataFlow::Node getUrl() { result = this.getArgument(1) }
|
||||
|
||||
@@ -683,8 +683,6 @@ private module ExpressJwt {
|
||||
*/
|
||||
private module NodeRsa {
|
||||
private class CreateKey extends CryptographicKeyCreation, API::InvokeNode {
|
||||
CryptographicAlgorithm algorithm;
|
||||
|
||||
CreateKey() {
|
||||
this = API::moduleImport("node-rsa").getAnInstantiation()
|
||||
or
|
||||
|
||||
@@ -81,11 +81,11 @@ class ExcludeTarFilePy extends Sanitizer {
|
||||
|
||||
/* Any call to an extractall method */
|
||||
class ExtractAllSink extends TaintSink {
|
||||
CallNode call;
|
||||
|
||||
ExtractAllSink() {
|
||||
this = call.getFunction().(AttrNode).getObject("extractall") and
|
||||
count(call.getAnArg()) = 0
|
||||
exists(CallNode call |
|
||||
this = call.getFunction().(AttrNode).getObject("extractall") and
|
||||
not exists(call.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof OpenTarFile }
|
||||
|
||||
@@ -194,7 +194,7 @@ predicate function_object_consistency(string clsname, string problem, string wha
|
||||
exists(FunctionObject func | clsname = func.getAQlClass() |
|
||||
what = func.getName() and
|
||||
(
|
||||
count(func.descriptiveString()) = 0 and problem = "no descriptiveString()"
|
||||
not exists(func.descriptiveString()) and problem = "no descriptiveString()"
|
||||
or
|
||||
exists(int c | c = strictcount(func.descriptiveString()) and c > 1 |
|
||||
problem = c + "descriptiveString()s"
|
||||
|
||||
@@ -67,7 +67,10 @@ module ExperimentalFlask {
|
||||
private class FlaskResponse extends DataFlow::CallCfgNode, HeaderDeclaration::Range {
|
||||
KeyValuePair item;
|
||||
|
||||
FlaskResponse() { this = Flask::Response::classRef().getACall() }
|
||||
FlaskResponse() {
|
||||
this = Flask::Response::classRef().getACall() and
|
||||
item = this.getArg(_).asExpr().(Dict).getAnItem()
|
||||
}
|
||||
|
||||
override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() }
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ edges
|
||||
| flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header |
|
||||
| flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript |
|
||||
nodes
|
||||
| django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
|
||||
@@ -36,9 +34,6 @@ nodes
|
||||
| flask_bad.py:35:18:35:29 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:35:18:35:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
|
||||
| flask_bad.py:44:44:44:50 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| flask_bad.py:44:44:44:55 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
subpaths
|
||||
#select
|
||||
| django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | django_bad.py:7:40:7:49 | ControlFlowNode for rfs_header | This | django_bad.py:5:18:5:58 | ControlFlowNode for Attribute() | user-provided value |
|
||||
@@ -47,4 +42,3 @@ subpaths
|
||||
| flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | flask_bad.py:19:18:19:24 | ControlFlowNode for request | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:21:38:21:47 | ControlFlowNode for rfs_header | This | flask_bad.py:19:18:19:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | flask_bad.py:27:18:27:24 | ControlFlowNode for request | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:29:34:29:43 | ControlFlowNode for rfs_header | This | flask_bad.py:27:18:27:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | flask_bad.py:35:18:35:24 | ControlFlowNode for request | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | $@ HTTP header is constructed from a $@. | flask_bad.py:38:24:38:33 | ControlFlowNode for rfs_header | This | flask_bad.py:35:18:35:24 | ControlFlowNode for request | user-provided value |
|
||||
| flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | flask_bad.py:44:44:44:50 | ControlFlowNode for request | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | $@ HTTP header is constructed from a $@. | flask_bad.py:44:44:44:69 | ControlFlowNode for Subscript | This | flask_bad.py:44:44:44:50 | ControlFlowNode for request | user-provided value |
|
||||
|
||||
@@ -40,7 +40,7 @@ predicate isLiteralComparison(ComparisonFormula eq) {
|
||||
or
|
||||
exists(NewTypeBranch nt |
|
||||
rhs.(Call).getTarget() = nt and
|
||||
count(nt.getField(_)) = 0
|
||||
not exists(nt.getField(_))
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -69,7 +69,7 @@ predicate isSingleton(Type ty) {
|
||||
or
|
||||
isSingleton(ty.getASuperType())
|
||||
or
|
||||
exists(NewTypeBranch br | count(br.getField(_)) = 0 |
|
||||
exists(NewTypeBranch br | not exists(br.getField(_)) |
|
||||
ty.(NewTypeBranchType).getDeclaration() = br
|
||||
or
|
||||
br = unique(NewTypeBranch br2 | br2 = ty.(NewTypeType).getDeclaration().getABranch())
|
||||
|
||||
35
ql/ql/src/queries/performance/UnusedField.ql
Normal file
35
ql/ql/src/queries/performance/UnusedField.ql
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @name UnusedField
|
||||
* @description A field that is not used in the characteristic predicate will contain every value
|
||||
* of its type when accessed in other predicates, which is probably not intended.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id ql/unused-field
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import ql
|
||||
|
||||
from ClassType clz, ClassType implClz, FieldDecl field, string extraMsg
|
||||
where
|
||||
clz.getDeclaration().getAField() = field and
|
||||
implClz.getASuperType*() = clz and
|
||||
// The field is not accessed in the charpred (of any of the classes)
|
||||
not exists(FieldAccess access |
|
||||
access.getEnclosingPredicate() = [clz, implClz].getDeclaration().getCharPred()
|
||||
) and
|
||||
// The implementation class is not abstract, and the field is not an override
|
||||
not implClz.getDeclaration().isAbstract() and
|
||||
not field.isOverride() and
|
||||
// There doesn't exist a class in between `clz` and `implClz` that binds `field`.
|
||||
not exists(ClassType c, CharPred p |
|
||||
c.getASuperType*() = clz and
|
||||
implClz.getASuperType*() = c and
|
||||
p = c.getDeclaration().getCharPred() and
|
||||
exists(FieldAccess access | access.getName() = field.getName() |
|
||||
access.getEnclosingPredicate() = p
|
||||
)
|
||||
) and
|
||||
(if clz = implClz then extraMsg = "." else extraMsg = " of any class between it and $@.")
|
||||
select clz, "The field $@ declared in $@ is not used in the characteristic predicate" + extraMsg,
|
||||
field, field.getName(), clz, clz.getName(), implClz, implClz.getName()
|
||||
21
ql/ql/src/queries/style/CountingToZero.ql
Normal file
21
ql/ql/src/queries/style/CountingToZero.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Counting zero elements
|
||||
* @description Use not exists instead of checking that there is zero elements in a set.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id ql/counting-to-zero
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import ql
|
||||
|
||||
from ComparisonFormula comp, int c, string msg
|
||||
where
|
||||
c = 0 and // [0, 1] and // skipping the 1 case for now, as it seems too noisy.
|
||||
comp.getOperator() = ["=", "!="] and
|
||||
comp.getAnOperand().(Integer).getValue() = c and
|
||||
comp.getAnOperand().(Aggregate).getKind() = ["count", "strictcount"] and
|
||||
if c = 0
|
||||
then msg = "Use not exists(..) instead of checking that there is zero elements in a set."
|
||||
else msg = "Use unique(.. | |) instead of checking that there is one element in a set."
|
||||
select comp, msg
|
||||
@@ -10,18 +10,31 @@
|
||||
|
||||
import ql
|
||||
|
||||
from ClassPredicate pred, String constant
|
||||
/**
|
||||
* Gets a string that is allowed to be returned from the `getAPrimaryQlClass` implementation
|
||||
* in class `c`.
|
||||
*
|
||||
* For most languages this is just the name of `c`, but QL for Swift implements
|
||||
* the `getAPrimaryQlClass` predicate for a class `Foo` in another class `FooBase`.
|
||||
*/
|
||||
string getAnAllowedResultForClass(Class c) {
|
||||
result = c.getName()
|
||||
or
|
||||
c.getName() = result + "Base"
|
||||
}
|
||||
|
||||
from ClassPredicate pred, String constant, Class c
|
||||
where
|
||||
exists(string className, string constantName |
|
||||
pred.getParent().getName() = className and
|
||||
pred.getName() = "getAPrimaryQlClass" and
|
||||
c = pred.getParent() and
|
||||
pred.getName() = "getAPrimaryQlClass" and
|
||||
exists(string constantName |
|
||||
constant = pred.getBody().(ComparisonFormula).getRightOperand() and
|
||||
constant.getValue() = constantName and
|
||||
// might be "Foo::classname", detect by matching with a regexp
|
||||
not constantName.regexpMatch(".*\\b" + className + "$") and
|
||||
not constantName.regexpMatch(".*\\b" + getAnAllowedResultForClass(c) + "$") and
|
||||
// ignore constants with "?" in them
|
||||
not constantName.regexpMatch(".*\\?.*")
|
||||
)
|
||||
select pred,
|
||||
"The getAPrimaryQlClass predicate $@ instead of the class name \"" + pred.getParent().getName() +
|
||||
"\".", constant, "results in \"" + constant.getValue() + "\""
|
||||
"The getAPrimaryQlClass predicate $@ instead of the class name \"" + c.getName() + "\".",
|
||||
constant, "results in \"" + constant.getValue() + "\""
|
||||
|
||||
@@ -199,9 +199,11 @@ private class Argument extends CfgNodes::ExprCfgNode {
|
||||
/** A collection of cached types and predicates to be evaluated in the same stage. */
|
||||
cached
|
||||
private module Cached {
|
||||
private import TaintTrackingPrivate as TaintTrackingPrivate
|
||||
|
||||
cached
|
||||
newtype TNode =
|
||||
TExprNode(CfgNodes::ExprCfgNode n) or
|
||||
TExprNode(CfgNodes::ExprCfgNode n) { TaintTrackingPrivate::forceCachingInSameStage() } or
|
||||
TReturningNode(CfgNodes::ReturningCfgNode n) or
|
||||
TSynthReturnNode(CfgScope scope, ReturnKind kind) {
|
||||
exists(ReturningNode ret |
|
||||
|
||||
@@ -781,11 +781,12 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
|
||||
exists(DataFlowCall call |
|
||||
bindingset[ret]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnNodeExt ret, OutNodeExt out) {
|
||||
exists(DataFlowCall call, ReturnKindExt rk |
|
||||
result = summaryArgParam0(call, arg) and
|
||||
out = rk.getAnOutNode(call)
|
||||
pragma[only_bind_out](ret).getKind() = pragma[only_bind_into](rk) and
|
||||
out = pragma[only_bind_into](rk).getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -797,9 +798,8 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) {
|
||||
exists(ReturnKindExt rk, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and
|
||||
ret.getKind() = rk
|
||||
exists(ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), ret, preservesValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -811,10 +811,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, ret, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -826,10 +825,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret) and
|
||||
ret.getKind() = rk
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern(
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included
|
||||
* in all global taint flow configurations.
|
||||
|
||||
@@ -13,8 +13,12 @@ function RegisterExtractorPack(id)
|
||||
table.remove(compilerArguments.argv, 1)
|
||||
|
||||
-- Skip "info" queries in case there is nothing to extract
|
||||
if compilerArguments.argv[1] == '-print-target-info' then return nil end
|
||||
if compilerArguments.argv[1] == '-emit-supported-features' then return nil end
|
||||
if compilerArguments.argv[1] == '-print-target-info' then
|
||||
return nil
|
||||
end
|
||||
if compilerArguments.argv[1] == '-emit-supported-features' then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Skip actions in which we cannot extract anything
|
||||
if compilerArguments.argv[1] == '-merge-modules' then return nil end
|
||||
@@ -22,10 +26,11 @@ function RegisterExtractorPack(id)
|
||||
return {
|
||||
trace = true,
|
||||
replace = false,
|
||||
invocations = {{path = swiftExtractor, arguments = compilerArguments}}
|
||||
order = ORDER_AFTER,
|
||||
invocation = {path = swiftExtractor, arguments = compilerArguments}
|
||||
}
|
||||
end
|
||||
return { SwiftMatcher }
|
||||
return {SwiftMatcher}
|
||||
end
|
||||
|
||||
-- Return a list of minimum supported versions of the configuration file format
|
||||
|
||||
Reference in New Issue
Block a user