mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge pull request #8482 from michaelnebel/csharp/capturesourcesink-models
C#: Capture[Source|Sink]Models utility.
This commit is contained in:
@@ -75,13 +75,9 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - Utils": [
|
||||
"java/ql/src/utils/model-generator/ModelGeneratorUtils.qll",
|
||||
"csharp/ql/src/utils/model-generator/ModelGeneratorUtils.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - SummaryModels": [
|
||||
"java/ql/src/utils/model-generator/CaptureSummaryModels.qll",
|
||||
"csharp/ql/src/utils/model-generator/CaptureSummaryModels.qll"
|
||||
"Model as Data Generation Java/C# - CaptureModels": [
|
||||
"java/ql/src/utils/model-generator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/model-generator/internal/CaptureModels.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
@@ -549,4 +545,4 @@
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1461,7 +1461,10 @@ private class InstanceFieldOrProperty extends FieldOrProperty {
|
||||
InstanceFieldOrProperty() { not this.isStatic() }
|
||||
}
|
||||
|
||||
private class FieldOrPropertyAccess extends AssignableAccess, QualifiableExpr {
|
||||
/**
|
||||
* An access to a field or a property.
|
||||
*/
|
||||
class FieldOrPropertyAccess extends AssignableAccess, QualifiableExpr {
|
||||
FieldOrPropertyAccess() { this.getTarget() instanceof FieldOrProperty }
|
||||
}
|
||||
|
||||
|
||||
11
csharp/ql/src/utils/model-generator/CaptureSinkModels.ql
Normal file
11
csharp/ql/src/utils/model-generator/CaptureSinkModels.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Capture sink models.
|
||||
* @description Finds public methods that act as sinks as they flow into a a known sink.
|
||||
* @id csharp/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
private import internal.CaptureModels
|
||||
|
||||
from TargetApi api, string sink
|
||||
where sink = captureSink(api)
|
||||
select sink order by sink
|
||||
11
csharp/ql/src/utils/model-generator/CaptureSourceModels.ql
Normal file
11
csharp/ql/src/utils/model-generator/CaptureSourceModels.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @name Capture source models.
|
||||
* @description Finds APIs that act as sources as they expose already known sources.
|
||||
* @id csharp/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
private import internal.CaptureModels
|
||||
|
||||
from TargetApi api, string source
|
||||
where source = captureSource(api)
|
||||
select source order by source
|
||||
@@ -4,7 +4,7 @@
|
||||
* @id csharp/utils/model-generator/summary-models
|
||||
*/
|
||||
|
||||
private import CaptureSummaryModels
|
||||
private import internal.CaptureModels
|
||||
|
||||
/**
|
||||
* Capture fluent APIs that return `this`.
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates related to capturing summary models
|
||||
* of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
import CaptureSummaryModelsSpecific
|
||||
|
||||
/**
|
||||
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
|
||||
*/
|
||||
string captureQualifierFlow(TargetApi api) {
|
||||
exists(ReturnNodeExt ret |
|
||||
api = returnNodeEnclosingCallable(ret) and
|
||||
isOwnInstanceAccessNode(ret)
|
||||
) and
|
||||
result = asValueModel(api, qualifierString(), "ReturnValue")
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted read.
|
||||
*/
|
||||
private class TaintRead extends DataFlow::FlowState {
|
||||
TaintRead() { this = "TaintRead" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted write.
|
||||
*/
|
||||
private class TaintStore extends DataFlow::FlowState {
|
||||
TaintStore() { this = "TaintStore" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
|
||||
*
|
||||
* This can be used to generate Flow summaries for APIs from parameter to return.
|
||||
*/
|
||||
class ThroughFlowConfig extends TaintTracking::Configuration {
|
||||
ThroughFlowConfig() { this = "ThroughFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
source instanceof DataFlow::ParameterNode and
|
||||
source.getEnclosingCallable() instanceof TargetApi and
|
||||
state instanceof TaintRead
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
sink instanceof ReturnNodeExt and
|
||||
not isOwnInstanceAccessNode(sink) and
|
||||
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
|
||||
(state instanceof TaintRead or state instanceof TaintStore)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(TypedContent tc |
|
||||
store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
|
||||
state2 instanceof TaintStore
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content c |
|
||||
readStep(node1, c, node2) and
|
||||
isRelevantContent(c) and
|
||||
state1 instanceof TaintRead and
|
||||
state2 instanceof TaintRead
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(Type t | t = n.getType() and not isRelevantType(t))
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
|
||||
*/
|
||||
string captureThroughFlow(TargetApi api) {
|
||||
exists(
|
||||
ThroughFlowConfig config, DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input,
|
||||
string output
|
||||
|
|
||||
config.hasFlow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
result = asTaintModel(api, input, output)
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.TaintTracking
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
import ModelGeneratorUtils
|
||||
|
||||
Callable returnNodeEnclosingCallable(ReturnNodeExt ret) { result = getNodeEnclosingCallable(ret) }
|
||||
|
||||
predicate isOwnInstanceAccessNode(ReturnNode node) { node.asExpr() instanceof ThisAccess }
|
||||
|
||||
string qualifierString() { result = "Argument[Qualifier]" }
|
||||
@@ -1,71 +0,0 @@
|
||||
import ModelGeneratorUtilsSpecific
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
|
||||
*/
|
||||
predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::Content f |
|
||||
readStep(node1, f, node2) and
|
||||
if f instanceof DataFlow::FieldContent
|
||||
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
|
||||
else
|
||||
if f instanceof DataFlow::SyntheticFieldContent
|
||||
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
|
||||
else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content f | storeStep(node1, f, node2) | containerContent(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if content `c` is either a field or synthetic field of a relevant type
|
||||
* or a container like content.
|
||||
*/
|
||||
predicate isRelevantContent(DataFlow::Content c) {
|
||||
isRelevantType(c.(DataFlow::FieldContent).getField().getType()) or
|
||||
isRelevantType(c.(DataFlow::SyntheticFieldContent).getField().getType()) or
|
||||
containerContent(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model for `api` with `input`, `output` and `kind`.
|
||||
*/
|
||||
bindingset[input, output, kind]
|
||||
string asSummaryModel(TargetApi api, string input, string output, string kind) {
|
||||
result =
|
||||
asPartialModel(api) + input + ";" //
|
||||
+ output + ";" //
|
||||
+ kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
string asValueModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "value")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the taint summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
string asTaintModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "taint")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model for `api` with `input` and `kind`.
|
||||
*/
|
||||
bindingset[input, kind]
|
||||
string asSinkModel(TargetApi api, string input, string kind) {
|
||||
result = asPartialModel(api) + input + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model for `api` with `output` and `kind`.
|
||||
*/
|
||||
bindingset[output, kind]
|
||||
string asSourceModel(TargetApi api, string output, string kind) {
|
||||
result = asPartialModel(api) + output + ";" + kind
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.commons.Util
|
||||
private import semmle.code.csharp.commons.Collections
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
|
||||
|
||||
private predicate isRelevantForModels(Callable api) { not api instanceof MainMethod }
|
||||
|
||||
/**
|
||||
* A class of callables that are relevant generating summary, source and sinks models for.
|
||||
*
|
||||
* In the Standard library and 3rd party libraries it the callables that can be called
|
||||
* from outside the library itself.
|
||||
*/
|
||||
class TargetApi extends DataFlowCallable {
|
||||
TargetApi() {
|
||||
[this.(Modifiable), this.(Accessor).getDeclaration()].isEffectivelyPublic() and
|
||||
this.fromSource() and
|
||||
isRelevantForModels(this)
|
||||
}
|
||||
}
|
||||
|
||||
predicate asPartialModel = Csv::asPartialModel/1;
|
||||
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
*/
|
||||
predicate isRelevantType(Type t) { not t instanceof Enum }
|
||||
|
||||
private string parameterAccess(Parameter p) {
|
||||
if isCollectionType(p.getType())
|
||||
then result = "Argument[" + p.getPosition() + "].Element"
|
||||
else result = "Argument[" + p.getPosition() + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model string representation of the parameter node `p`.
|
||||
*/
|
||||
string parameterNodeAsInput(DataFlow::ParameterNode p) {
|
||||
result = parameterAccess(p.asParameter())
|
||||
or
|
||||
result = "Argument[Qualifier]" and p instanceof InstanceParameterNode
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Parameter getParameter(ReturnNodeExt node, ParameterPosition pos) {
|
||||
result = node.getEnclosingCallable().getParameter(pos.getPosition())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model string represention of the the return node `node`.
|
||||
*/
|
||||
string returnNodeAsOutput(ReturnNodeExt node) {
|
||||
if node.getKind() instanceof ValueReturnKind
|
||||
then result = "ReturnValue"
|
||||
else
|
||||
exists(ParameterPosition pos | pos = node.getKind().(ParamUpdateReturnKind).getPosition() |
|
||||
result = parameterAccess(getParameter(node, pos))
|
||||
or
|
||||
pos.isThisParameter() and
|
||||
result = "Argument[Qualifier]"
|
||||
)
|
||||
}
|
||||
242
csharp/ql/src/utils/model-generator/internal/CaptureModels.qll
Normal file
242
csharp/ql/src/utils/model-generator/internal/CaptureModels.qll
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Provides classes and predicates related to capturing summary, source,
|
||||
* and sink models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
private import CaptureModelsSpecific
|
||||
|
||||
class TargetApi = TargetApiSpecific;
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
|
||||
*/
|
||||
private predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::Content f |
|
||||
DataFlowPrivate::readStep(node1, f, node2) and
|
||||
if f instanceof DataFlow::FieldContent
|
||||
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
|
||||
else
|
||||
if f instanceof DataFlow::SyntheticFieldContent
|
||||
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
|
||||
else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content f | DataFlowPrivate::storeStep(node1, f, node2) |
|
||||
DataFlowPrivate::containerContent(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if content `c` is either a field or synthetic field of a relevant type
|
||||
* or a container like content.
|
||||
*/
|
||||
private predicate isRelevantContent(DataFlow::Content c) {
|
||||
isRelevantType(c.(DataFlow::FieldContent).getField().getType()) or
|
||||
isRelevantType(c.(DataFlow::SyntheticFieldContent).getField().getType()) or
|
||||
DataFlowPrivate::containerContent(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model for `api` with `input`, `output` and `kind`.
|
||||
*/
|
||||
bindingset[input, output, kind]
|
||||
private string asSummaryModel(TargetApi api, string input, string output, string kind) {
|
||||
result =
|
||||
asPartialModel(api) + input + ";" //
|
||||
+ output + ";" //
|
||||
+ kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
private string asValueModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "value")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the taint summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
private string asTaintModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "taint")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model for `api` with `input` and `kind`.
|
||||
*/
|
||||
bindingset[input, kind]
|
||||
private string asSinkModel(TargetApi api, string input, string kind) {
|
||||
result = asPartialModel(api) + input + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model for `api` with `output` and `kind`.
|
||||
*/
|
||||
bindingset[output, kind]
|
||||
private string asSourceModel(TargetApi api, string output, string kind) {
|
||||
result = asPartialModel(api) + output + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
|
||||
*/
|
||||
string captureQualifierFlow(TargetApi api) {
|
||||
exists(DataFlowImplCommon::ReturnNodeExt ret |
|
||||
api = returnNodeEnclosingCallable(ret) and
|
||||
isOwnInstanceAccessNode(ret)
|
||||
) and
|
||||
result = asValueModel(api, qualifierString(), "ReturnValue")
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted read.
|
||||
*/
|
||||
private class TaintRead extends DataFlow::FlowState {
|
||||
TaintRead() { this = "TaintRead" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted write.
|
||||
*/
|
||||
private class TaintStore extends DataFlow::FlowState {
|
||||
TaintStore() { this = "TaintStore" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
|
||||
*
|
||||
* This can be used to generate Flow summaries for APIs from parameter to return.
|
||||
*/
|
||||
private class ThroughFlowConfig extends TaintTracking::Configuration {
|
||||
ThroughFlowConfig() { this = "ThroughFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
source instanceof DataFlow::ParameterNode and
|
||||
source.getEnclosingCallable() instanceof TargetApi and
|
||||
state instanceof TaintRead
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
sink instanceof DataFlowImplCommon::ReturnNodeExt and
|
||||
not isOwnInstanceAccessNode(sink) and
|
||||
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
|
||||
(state instanceof TaintRead or state instanceof TaintStore)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(DataFlowImplCommon::TypedContent tc |
|
||||
DataFlowImplCommon::store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
|
||||
state2 instanceof TaintStore
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content c |
|
||||
DataFlowPrivate::readStep(node1, c, node2) and
|
||||
isRelevantContent(c) and
|
||||
state1 instanceof TaintRead and
|
||||
state2 instanceof TaintRead
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(Type t | t = n.getType() and not isRelevantType(t))
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
|
||||
*/
|
||||
string captureThroughFlow(TargetApi api) {
|
||||
exists(
|
||||
ThroughFlowConfig config, DataFlow::ParameterNode p,
|
||||
DataFlowImplCommon::ReturnNodeExt returnNodeExt, string input, string output
|
||||
|
|
||||
config.hasFlow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
result = asTaintModel(api, input, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the already known existing sources and the sinks are the API return nodes.
|
||||
*
|
||||
* This can be used to generate Source summaries for an API, if the API expose an already known source
|
||||
* via its return (then the API itself becomes a source).
|
||||
*/
|
||||
private class FromSourceConfiguration extends TaintTracking::Configuration {
|
||||
FromSourceConfiguration() { this = "FromSourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { ExternalFlow::sourceNode(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TargetApi c |
|
||||
sink instanceof DataFlowImplCommon::ReturnNodeExt and
|
||||
sink.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isRelevantTaintStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model(s) of `api`, if there is flow from an existing known source to the return of `api`.
|
||||
*/
|
||||
string captureSource(TargetApi api) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
|
||||
config.hasFlow(source, sink) and
|
||||
ExternalFlow::sourceNode(source, kind) and
|
||||
api = sink.getEnclosingCallable() and
|
||||
result = asSourceModel(api, returnNodeAsOutput(sink), kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of the API and the fields of the enclosing type.
|
||||
*
|
||||
* This can be used to generate Sink summaries for APIs, if the API propgates a parameter (or enclosing type field)
|
||||
* into an existing known sink (then the API itself becomes a sink).
|
||||
*/
|
||||
private class PropagateToSinkConfiguration extends PropagateToSinkConfigurationSpecific {
|
||||
PropagateToSinkConfiguration() { this = "parameters or fields flowing into sinks" }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { ExternalFlow::sinkNode(sink, _) }
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSourceCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model(s) of `api`, if there is flow from a parameter to an existing known sink.
|
||||
*/
|
||||
string captureSink(TargetApi api) {
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
|
||||
config.hasFlow(src, sink) and
|
||||
ExternalFlow::sinkNode(sink, kind) and
|
||||
api = src.getEnclosingCallable() and
|
||||
isRelevantSinkKind(kind) and
|
||||
result = asSinkModel(api, asInputArgument(src), kind)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
private import csharp as CS
|
||||
private import semmle.code.csharp.commons.Util as Util
|
||||
private import semmle.code.csharp.commons.Collections as Collections
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
|
||||
import semmle.code.csharp.dataflow.ExternalFlow as ExternalFlow
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
|
||||
module DataFlow = CS::DataFlow;
|
||||
|
||||
module TaintTracking = CS::TaintTracking;
|
||||
|
||||
class Type = CS::Type;
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to generate models for `api`.
|
||||
*/
|
||||
private predicate isRelevantForModels(CS::Callable api) {
|
||||
[api.(CS::Modifiable), api.(CS::Accessor).getDeclaration()].isEffectivelyPublic() and
|
||||
not api instanceof Util::MainMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of callables that are relevant generating summary, source and sinks models for.
|
||||
*
|
||||
* In the Standard library and 3rd party libraries it the callables that can be called
|
||||
* from outside the library itself.
|
||||
*/
|
||||
class TargetApiSpecific extends DataFlowCallable {
|
||||
TargetApiSpecific() {
|
||||
this.fromSource() and
|
||||
isRelevantForModels(this)
|
||||
}
|
||||
}
|
||||
|
||||
predicate asPartialModel = DataFlowPrivate::Csv::asPartialModel/1;
|
||||
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
*/
|
||||
predicate isRelevantType(CS::Type t) { not t instanceof CS::Enum }
|
||||
|
||||
/**
|
||||
* Gets the CSV string representation of the qualifier.
|
||||
*/
|
||||
string qualifierString() { result = "Argument[Qualifier]" }
|
||||
|
||||
private string parameterAccess(CS::Parameter p) {
|
||||
if Collections::isCollectionType(p.getType())
|
||||
then result = "Argument[" + p.getPosition() + "].Element"
|
||||
else result = "Argument[" + p.getPosition() + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string representation of the parameter node `p`.
|
||||
*/
|
||||
string parameterNodeAsInput(DataFlow::ParameterNode p) {
|
||||
result = parameterAccess(p.asParameter())
|
||||
or
|
||||
result = qualifierString() and p instanceof DataFlowPrivate::InstanceParameterNode
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private CS::Parameter getParameter(DataFlowImplCommon::ReturnNodeExt node, ParameterPosition pos) {
|
||||
result = node.getEnclosingCallable().getParameter(pos.getPosition())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string represention of the the return node `node`.
|
||||
*/
|
||||
string returnNodeAsOutput(DataFlowImplCommon::ReturnNodeExt node) {
|
||||
if node.getKind() instanceof DataFlowImplCommon::ValueReturnKind
|
||||
then result = "ReturnValue"
|
||||
else
|
||||
exists(ParameterPosition pos |
|
||||
pos = node.getKind().(DataFlowImplCommon::ParamUpdateReturnKind).getPosition()
|
||||
|
|
||||
result = parameterAccess(getParameter(node, pos))
|
||||
or
|
||||
pos.isThisParameter() and
|
||||
result = qualifierString()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable of `ret`.
|
||||
*/
|
||||
CS::Callable returnNodeEnclosingCallable(DataFlowImplCommon::ReturnNodeExt ret) {
|
||||
result = DataFlowImplCommon::getNodeEnclosingCallable(ret)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an own instance access.
|
||||
*/
|
||||
predicate isOwnInstanceAccessNode(DataFlowPrivate::ReturnNode node) {
|
||||
node.asExpr() instanceof CS::ThisAccess
|
||||
}
|
||||
|
||||
private predicate isRelevantMemberAccess(DataFlow::Node node) {
|
||||
exists(CS::MemberAccess access | access = node.asExpr() |
|
||||
access.hasThisQualifier() and
|
||||
access.getTarget().isEffectivelyPublic() and
|
||||
(
|
||||
access instanceof CS::FieldAccess
|
||||
or
|
||||
access.getTarget().(CS::Property).getSetter().isPublic()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Language specific parts of the `PropagateToSinkConfiguration`.
|
||||
*/
|
||||
class PropagateToSinkConfigurationSpecific extends CS::TaintTracking::Configuration {
|
||||
PropagateToSinkConfigurationSpecific() { this = "parameters or fields flowing into sinks" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
(isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode) and
|
||||
isRelevantForModels(source.getEnclosingCallable())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV input string representation of `source`.
|
||||
*/
|
||||
string asInputArgument(DataFlow::Node source) {
|
||||
exists(int pos |
|
||||
pos = source.(DataFlow::ParameterNode).getParameter().getPosition() and
|
||||
result = "Argument[" + pos + "]"
|
||||
)
|
||||
or
|
||||
source.asExpr() instanceof DataFlowPrivate::FieldOrPropertyAccess and
|
||||
result = qualifierString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `kind` is a relevant sink kind for creating sink models.
|
||||
*/
|
||||
bindingset[kind]
|
||||
predicate isRelevantSinkKind(string kind) { any() }
|
||||
File diff suppressed because one or more lines are too long
@@ -156,6 +156,7 @@ namespace System.Web
|
||||
public class HttpResponse
|
||||
{
|
||||
public void Write(object o) { }
|
||||
public void WriteFile(string s) { }
|
||||
public HttpCookieCollection Cookies => null;
|
||||
public void AddHeader(string name, string value) { }
|
||||
public void Redirect(string url) { }
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| Sinks;NewSinks;false;WrapFieldResponseWriteFile;();Argument[Qualifier];html |
|
||||
| Sinks;NewSinks;false;WrapPropResponseWriteFile;();Argument[Qualifier];html |
|
||||
| Sinks;NewSinks;false;WrapResponseWrite;(System.Object);Argument[0];html |
|
||||
| Sinks;NewSinks;false;WrapResponseWriteFile;(System.String);Argument[0];html |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureSinkModels.ql
|
||||
@@ -0,0 +1,3 @@
|
||||
| Sources;NewSources;false;WrapConsoleReadKey;();ReturnValue;local |
|
||||
| Sources;NewSources;false;WrapConsoleReadLine;();ReturnValue;local |
|
||||
| Sources;NewSources;false;WrapConsoleReadLineAndProcees;(System.String);ReturnValue;local |
|
||||
@@ -0,0 +1 @@
|
||||
utils/model-generator/CaptureSourceModels.ql
|
||||
70
csharp/ql/test/utils/model-generator/Sinks.cs
Normal file
70
csharp/ql/test/utils/model-generator/Sinks.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Sinks;
|
||||
|
||||
public class NewSinks
|
||||
{
|
||||
private string privateTainted;
|
||||
public string tainted;
|
||||
|
||||
private string PrivateTaintedProp { get; set; }
|
||||
public string TaintedProp { get; set; }
|
||||
public string PrivateSetTaintedProp { get; private set; }
|
||||
|
||||
// New sink
|
||||
public void WrapResponseWrite(object o)
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.Write(o);
|
||||
}
|
||||
|
||||
// NOT new sink as method is private
|
||||
private void PrivateWrapResponseWrite(object o)
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.Write(o);
|
||||
}
|
||||
|
||||
// New sink
|
||||
public void WrapResponseWriteFile(string s)
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(s);
|
||||
}
|
||||
|
||||
// New sink
|
||||
public void WrapFieldResponseWriteFile()
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(tainted);
|
||||
}
|
||||
|
||||
// NOT new sink as field is private
|
||||
public void WrapPrivateFieldResponseWriteFile()
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(privateTainted);
|
||||
}
|
||||
|
||||
// New sink
|
||||
public void WrapPropResponseWriteFile()
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(TaintedProp);
|
||||
}
|
||||
|
||||
// NOT new sink as property is private
|
||||
public void WrapPrivatePropResponseWriteFile()
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(PrivateTaintedProp);
|
||||
}
|
||||
|
||||
// NOT new sink as property setter is private
|
||||
public void WrapPropPrivateSetResponseWriteFile()
|
||||
{
|
||||
var response = new HttpResponse();
|
||||
response.WriteFile(PrivateSetTaintedProp);
|
||||
}
|
||||
}
|
||||
31
csharp/ql/test/utils/model-generator/Sources.cs
Normal file
31
csharp/ql/test/utils/model-generator/Sources.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Sources;
|
||||
|
||||
public class NewSources
|
||||
{
|
||||
// New source
|
||||
public string? WrapConsoleReadLine()
|
||||
{
|
||||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
// New source
|
||||
public string WrapConsoleReadLineAndProcees(string prompt)
|
||||
{
|
||||
var s = Console.ReadLine();
|
||||
return string.IsNullOrEmpty(s) ? "" : s.ToUpper();
|
||||
}
|
||||
|
||||
// NOT new source as method is private
|
||||
private string? PrivateWrapConsoleReadLine()
|
||||
{
|
||||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
// New source
|
||||
public ConsoleKeyInfo WrapConsoleReadKey()
|
||||
{
|
||||
return Console.ReadKey();
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
semmle-extractor-options: /r:System.Linq.dll
|
||||
semmle-extractor-options: /r:System.Linq.dll /r:System.Collections.Specialized.dll
|
||||
semmle-extractor-options: ${testdir}/../../resources/stubs/System.Web.cs
|
||||
|
||||
@@ -4,54 +4,7 @@
|
||||
* @id java/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
import java
|
||||
private import Telemetry.ExternalAPI
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import ModelGeneratorUtils
|
||||
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
|
||||
|
||||
class PropagateToSinkConfiguration extends TaintTracking::Configuration {
|
||||
PropagateToSinkConfiguration() { this = "parameters or flowing into sinks" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
(source.asExpr().(FieldAccess).isOwnFieldAccess() or source instanceof DataFlow::ParameterNode) and
|
||||
source.getEnclosingCallable().isPublic() and
|
||||
exists(RefType t |
|
||||
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
|
||||
not t instanceof TypeObject and
|
||||
t.isPublic()
|
||||
) and
|
||||
isRelevantForModels(source.getEnclosingCallable())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sinkNode(sink, _) }
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSourceCallContext
|
||||
}
|
||||
}
|
||||
|
||||
string asInputArgument(DataFlow::Node source) {
|
||||
exists(int pos |
|
||||
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
|
||||
result = "Argument[" + pos + "]"
|
||||
)
|
||||
or
|
||||
source.asExpr() instanceof FieldAccess and
|
||||
result = "Argument[-1]"
|
||||
}
|
||||
|
||||
string captureSink(TargetApi api) {
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
|
||||
config.hasFlow(src, sink) and
|
||||
sinkNode(sink, kind) and
|
||||
api = src.getEnclosingCallable() and
|
||||
not kind = "logging" and
|
||||
result = asSinkModel(api, asInputArgument(src), kind)
|
||||
)
|
||||
}
|
||||
private import internal.CaptureModels
|
||||
|
||||
from TargetApi api, string sink
|
||||
where sink = captureSink(api)
|
||||
|
||||
@@ -4,50 +4,8 @@
|
||||
* @id java/utils/model-generator/sink-models
|
||||
*/
|
||||
|
||||
import java
|
||||
private import Telemetry.ExternalAPI
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import ModelGeneratorUtils
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
|
||||
private import internal.CaptureModels
|
||||
|
||||
class FromSourceConfiguration extends TaintTracking::Configuration {
|
||||
FromSourceConfiguration() { this = "FromSourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { sourceNode(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TargetApi c |
|
||||
sink instanceof ReturnNodeExt and
|
||||
sink.getEnclosingCallable() = c and
|
||||
c.isPublic() and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isRelevantTaintStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
string captureSource(TargetApi api) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
|
||||
config.hasFlow(source, sink) and
|
||||
sourceNode(source, kind) and
|
||||
api = sink.getEnclosingCallable() and
|
||||
result = asSourceModel(api, returnNodeAsOutput(sink), kind)
|
||||
)
|
||||
}
|
||||
|
||||
from TargetApi api, string sink
|
||||
where sink = captureSource(api)
|
||||
select sink order by sink
|
||||
from TargetApi api, string source
|
||||
where source = captureSource(api)
|
||||
select source order by source
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @id java/utils/model-generator/summary-models
|
||||
*/
|
||||
|
||||
private import CaptureSummaryModels
|
||||
private import internal.CaptureModels
|
||||
|
||||
/**
|
||||
* Capture fluent APIs that return `this`.
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates related to capturing summary models
|
||||
* of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
import CaptureSummaryModelsSpecific
|
||||
|
||||
/**
|
||||
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
|
||||
*/
|
||||
string captureQualifierFlow(TargetApi api) {
|
||||
exists(ReturnNodeExt ret |
|
||||
api = returnNodeEnclosingCallable(ret) and
|
||||
isOwnInstanceAccessNode(ret)
|
||||
) and
|
||||
result = asValueModel(api, qualifierString(), "ReturnValue")
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted read.
|
||||
*/
|
||||
private class TaintRead extends DataFlow::FlowState {
|
||||
TaintRead() { this = "TaintRead" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted write.
|
||||
*/
|
||||
private class TaintStore extends DataFlow::FlowState {
|
||||
TaintStore() { this = "TaintStore" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
|
||||
*
|
||||
* This can be used to generate Flow summaries for APIs from parameter to return.
|
||||
*/
|
||||
class ThroughFlowConfig extends TaintTracking::Configuration {
|
||||
ThroughFlowConfig() { this = "ThroughFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
source instanceof DataFlow::ParameterNode and
|
||||
source.getEnclosingCallable() instanceof TargetApi and
|
||||
state instanceof TaintRead
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
sink instanceof ReturnNodeExt and
|
||||
not isOwnInstanceAccessNode(sink) and
|
||||
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
|
||||
(state instanceof TaintRead or state instanceof TaintStore)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(TypedContent tc |
|
||||
store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
|
||||
state2 instanceof TaintStore
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content c |
|
||||
readStep(node1, c, node2) and
|
||||
isRelevantContent(c) and
|
||||
state1 instanceof TaintRead and
|
||||
state2 instanceof TaintRead
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(Type t | t = n.getType() and not isRelevantType(t))
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
|
||||
*/
|
||||
string captureThroughFlow(TargetApi api) {
|
||||
exists(
|
||||
ThroughFlowConfig config, DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input,
|
||||
string output
|
||||
|
|
||||
config.hasFlow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
result = asTaintModel(api, input, output)
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
import semmle.code.java.dataflow.internal.DataFlowNodes
|
||||
import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.java.dataflow.InstanceAccess
|
||||
import ModelGeneratorUtils
|
||||
|
||||
Callable returnNodeEnclosingCallable(ReturnNodeExt ret) {
|
||||
result = getNodeEnclosingCallable(ret).asCallable()
|
||||
}
|
||||
|
||||
predicate isOwnInstanceAccessNode(ReturnNode node) {
|
||||
node.asExpr().(ThisAccess).isOwnInstanceAccess()
|
||||
}
|
||||
|
||||
string qualifierString() { result = "Argument[-1]" }
|
||||
@@ -1,71 +0,0 @@
|
||||
import ModelGeneratorUtilsSpecific
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
|
||||
*/
|
||||
predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::Content f |
|
||||
readStep(node1, f, node2) and
|
||||
if f instanceof DataFlow::FieldContent
|
||||
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
|
||||
else
|
||||
if f instanceof DataFlow::SyntheticFieldContent
|
||||
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
|
||||
else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content f | storeStep(node1, f, node2) | containerContent(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if content `c` is either a field or synthetic field of a relevant type
|
||||
* or a container like content.
|
||||
*/
|
||||
predicate isRelevantContent(DataFlow::Content c) {
|
||||
isRelevantType(c.(DataFlow::FieldContent).getField().getType()) or
|
||||
isRelevantType(c.(DataFlow::SyntheticFieldContent).getField().getType()) or
|
||||
containerContent(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model for `api` with `input`, `output` and `kind`.
|
||||
*/
|
||||
bindingset[input, output, kind]
|
||||
string asSummaryModel(TargetApi api, string input, string output, string kind) {
|
||||
result =
|
||||
asPartialModel(api) + input + ";" //
|
||||
+ output + ";" //
|
||||
+ kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
string asValueModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "value")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the taint summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
string asTaintModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "taint")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model for `api` with `input` and `kind`.
|
||||
*/
|
||||
bindingset[input, kind]
|
||||
string asSinkModel(TargetApi api, string input, string kind) {
|
||||
result = asPartialModel(api) + input + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model for `api` with `output` and `kind`.
|
||||
*/
|
||||
bindingset[output, kind]
|
||||
string asSourceModel(TargetApi api, string output, string kind) {
|
||||
result = asPartialModel(api) + output + ";" + kind
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
|
||||
|
||||
private Method superImpl(Method m) {
|
||||
result = m.getAnOverride() and
|
||||
not exists(result.getAnOverride()) and
|
||||
not m instanceof ToStringMethod
|
||||
}
|
||||
|
||||
private predicate isInTestFile(File file) {
|
||||
file.getAbsolutePath().matches("%src/test/%") or
|
||||
file.getAbsolutePath().matches("%/guava-tests/%") or
|
||||
file.getAbsolutePath().matches("%/guava-testlib/%")
|
||||
}
|
||||
|
||||
private predicate isJdkInternal(CompilationUnit cu) {
|
||||
cu.getPackage().getName().matches("org.graalvm%") or
|
||||
cu.getPackage().getName().matches("com.sun%") or
|
||||
cu.getPackage().getName().matches("javax.swing%") or
|
||||
cu.getPackage().getName().matches("java.awt%") or
|
||||
cu.getPackage().getName().matches("sun%") or
|
||||
cu.getPackage().getName().matches("jdk.%") or
|
||||
cu.getPackage().getName().matches("java2d.%") or
|
||||
cu.getPackage().getName().matches("build.tools.%") or
|
||||
cu.getPackage().getName().matches("propertiesparser.%") or
|
||||
cu.getPackage().getName().matches("org.jcp.%") or
|
||||
cu.getPackage().getName().matches("org.w3c.%") or
|
||||
cu.getPackage().getName().matches("org.ietf.jgss.%") or
|
||||
cu.getPackage().getName().matches("org.xml.sax%") or
|
||||
cu.getPackage().getName() = "compileproperties" or
|
||||
cu.getPackage().getName() = "netscape.javascript" or
|
||||
cu.getPackage().getName() = ""
|
||||
}
|
||||
|
||||
predicate isRelevantForModels(Callable api) {
|
||||
not isInTestFile(api.getCompilationUnit().getFile()) and
|
||||
not isJdkInternal(api.getCompilationUnit()) and
|
||||
not api instanceof MainMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of Callables that are relevant for generating summary, source and sinks models for.
|
||||
*
|
||||
* In the Standard library and 3rd party libraries it the Callables that can be called
|
||||
* from outside the library itself.
|
||||
*/
|
||||
class TargetApi extends Callable {
|
||||
TargetApi() {
|
||||
this.isPublic() and
|
||||
this.fromSource() and
|
||||
(
|
||||
this.getDeclaringType().isPublic() or
|
||||
superImpl(this).getDeclaringType().isPublic()
|
||||
) and
|
||||
isRelevantForModels(this)
|
||||
}
|
||||
}
|
||||
|
||||
private string isExtensible(RefType ref) {
|
||||
if ref.isFinal() then result = "false" else result = "true"
|
||||
}
|
||||
|
||||
private string typeAsModel(RefType type) {
|
||||
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
|
||||
}
|
||||
|
||||
private RefType bestTypeForModel(TargetApi api) {
|
||||
if exists(superImpl(api))
|
||||
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
|
||||
else result = api.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate type name for the model. Either the type
|
||||
* declaring the method or the supertype introducing the method.
|
||||
*/
|
||||
private string typeAsSummaryModel(TargetApi api) { result = typeAsModel(bestTypeForModel(api)) }
|
||||
|
||||
/**
|
||||
* Computes the first 6 columns for CSV rows.
|
||||
*/
|
||||
string asPartialModel(TargetApi api) {
|
||||
result =
|
||||
typeAsSummaryModel(api) + ";" //
|
||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||
+ api.getName() + ";" //
|
||||
+ paramsString(api) + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
}
|
||||
|
||||
private predicate isPrimitiveTypeUsedForBulkData(Type t) {
|
||||
t.getName().regexpMatch("byte|char|Byte|Character")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
*/
|
||||
predicate isRelevantType(Type t) {
|
||||
not t instanceof TypeClass and
|
||||
not t instanceof EnumType and
|
||||
not t instanceof PrimitiveType and
|
||||
not t instanceof BoxedType and
|
||||
not t.(RefType).getAnAncestor().hasQualifiedName("java.lang", "Number") and
|
||||
not t.(RefType).getAnAncestor().hasQualifiedName("java.nio.charset", "Charset") and
|
||||
(
|
||||
not t.(Array).getElementType() instanceof PrimitiveType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(Array).getElementType() instanceof BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(CollectionType).getElementType() instanceof BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(CollectionType).getElementType())
|
||||
)
|
||||
}
|
||||
|
||||
private string parameterAccess(Parameter p) {
|
||||
if
|
||||
p.getType() instanceof Array and
|
||||
not isPrimitiveTypeUsedForBulkData(p.getType().(Array).getElementType())
|
||||
then result = "Argument[" + p.getPosition() + "].ArrayElement"
|
||||
else
|
||||
if p.getType() instanceof ContainerType
|
||||
then result = "Argument[" + p.getPosition() + "].Element"
|
||||
else result = "Argument[" + p.getPosition() + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model string representation of the parameter node `p`.
|
||||
*/
|
||||
string parameterNodeAsInput(DataFlow::ParameterNode p) {
|
||||
result = parameterAccess(p.asParameter())
|
||||
or
|
||||
result = "Argument[-1]" and p instanceof DataFlow::InstanceParameterNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model string represention of the the return node `node`.
|
||||
*/
|
||||
string returnNodeAsOutput(ReturnNodeExt node) {
|
||||
if node.getKind() instanceof ValueReturnKind
|
||||
then result = "ReturnValue"
|
||||
else
|
||||
exists(int pos | pos = node.getKind().(ParamUpdateReturnKind).getPosition() |
|
||||
result = parameterAccess(node.getEnclosingCallable().getParameter(pos))
|
||||
or
|
||||
result = "Argument[-1]" and pos = -1
|
||||
)
|
||||
}
|
||||
242
java/ql/src/utils/model-generator/internal/CaptureModels.qll
Normal file
242
java/ql/src/utils/model-generator/internal/CaptureModels.qll
Normal file
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* Provides classes and predicates related to capturing summary, source,
|
||||
* and sink models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
private import CaptureModelsSpecific
|
||||
|
||||
class TargetApi = TargetApiSpecific;
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` either via a read or a write of an intermediate field `f`.
|
||||
*/
|
||||
private predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::Content f |
|
||||
DataFlowPrivate::readStep(node1, f, node2) and
|
||||
if f instanceof DataFlow::FieldContent
|
||||
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
|
||||
else
|
||||
if f instanceof DataFlow::SyntheticFieldContent
|
||||
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
|
||||
else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content f | DataFlowPrivate::storeStep(node1, f, node2) |
|
||||
DataFlowPrivate::containerContent(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if content `c` is either a field or synthetic field of a relevant type
|
||||
* or a container like content.
|
||||
*/
|
||||
private predicate isRelevantContent(DataFlow::Content c) {
|
||||
isRelevantType(c.(DataFlow::FieldContent).getField().getType()) or
|
||||
isRelevantType(c.(DataFlow::SyntheticFieldContent).getField().getType()) or
|
||||
DataFlowPrivate::containerContent(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model for `api` with `input`, `output` and `kind`.
|
||||
*/
|
||||
bindingset[input, output, kind]
|
||||
private string asSummaryModel(TargetApi api, string input, string output, string kind) {
|
||||
result =
|
||||
asPartialModel(api) + input + ";" //
|
||||
+ output + ";" //
|
||||
+ kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
private string asValueModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "value")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the taint summary model for `api` with `input` and `output`.
|
||||
*/
|
||||
bindingset[input, output]
|
||||
private string asTaintModel(TargetApi api, string input, string output) {
|
||||
result = asSummaryModel(api, input, output, "taint")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model for `api` with `input` and `kind`.
|
||||
*/
|
||||
bindingset[input, kind]
|
||||
private string asSinkModel(TargetApi api, string input, string kind) {
|
||||
result = asPartialModel(api) + input + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model for `api` with `output` and `kind`.
|
||||
*/
|
||||
bindingset[output, kind]
|
||||
private string asSourceModel(TargetApi api, string output, string kind) {
|
||||
result = asPartialModel(api) + output + ";" + kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model of `api`, if it follows the `fluent` programming pattern (returns `this`).
|
||||
*/
|
||||
string captureQualifierFlow(TargetApi api) {
|
||||
exists(DataFlowImplCommon::ReturnNodeExt ret |
|
||||
api = returnNodeEnclosingCallable(ret) and
|
||||
isOwnInstanceAccessNode(ret)
|
||||
) and
|
||||
result = asValueModel(api, qualifierString(), "ReturnValue")
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted read.
|
||||
*/
|
||||
private class TaintRead extends DataFlow::FlowState {
|
||||
TaintRead() { this = "TaintRead" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A FlowState representing a tainted write.
|
||||
*/
|
||||
private class TaintStore extends DataFlow::FlowState {
|
||||
TaintStore() { this = "TaintStore" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
|
||||
*
|
||||
* This can be used to generate Flow summaries for APIs from parameter to return.
|
||||
*/
|
||||
private class ThroughFlowConfig extends TaintTracking::Configuration {
|
||||
ThroughFlowConfig() { this = "ThroughFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
|
||||
source instanceof DataFlow::ParameterNode and
|
||||
source.getEnclosingCallable() instanceof TargetApi and
|
||||
state instanceof TaintRead
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
|
||||
sink instanceof DataFlowImplCommon::ReturnNodeExt and
|
||||
not isOwnInstanceAccessNode(sink) and
|
||||
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
|
||||
(state instanceof TaintRead or state instanceof TaintStore)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
exists(DataFlowImplCommon::TypedContent tc |
|
||||
DataFlowImplCommon::store(node1, tc, node2, _) and
|
||||
isRelevantContent(tc.getContent()) and
|
||||
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
|
||||
state2 instanceof TaintStore
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Content c |
|
||||
DataFlowPrivate::readStep(node1, c, node2) and
|
||||
isRelevantContent(c) and
|
||||
state1 instanceof TaintRead and
|
||||
state2 instanceof TaintRead
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(Type t | t = n.getType() and not isRelevantType(t))
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
|
||||
*/
|
||||
string captureThroughFlow(TargetApi api) {
|
||||
exists(
|
||||
ThroughFlowConfig config, DataFlow::ParameterNode p,
|
||||
DataFlowImplCommon::ReturnNodeExt returnNodeExt, string input, string output
|
||||
|
|
||||
config.hasFlow(p, returnNodeExt) and
|
||||
returnNodeExt.getEnclosingCallable() = api and
|
||||
input = parameterNodeAsInput(p) and
|
||||
output = returnNodeAsOutput(returnNodeExt) and
|
||||
input != output and
|
||||
result = asTaintModel(api, input, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the already known existing sources and the sinks are the API return nodes.
|
||||
*
|
||||
* This can be used to generate Source summaries for an API, if the API expose an already known source
|
||||
* via its return (then the API itself becomes a source).
|
||||
*/
|
||||
private class FromSourceConfiguration extends TaintTracking::Configuration {
|
||||
FromSourceConfiguration() { this = "FromSourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { ExternalFlow::sourceNode(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TargetApi c |
|
||||
sink instanceof DataFlowImplCommon::ReturnNodeExt and
|
||||
sink.getEnclosingCallable() = c
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isRelevantTaintStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source model(s) of `api`, if there is flow from an existing known source to the return of `api`.
|
||||
*/
|
||||
string captureSource(TargetApi api) {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
|
||||
config.hasFlow(source, sink) and
|
||||
ExternalFlow::sourceNode(source, kind) and
|
||||
api = sink.getEnclosingCallable() and
|
||||
result = asSourceModel(api, returnNodeAsOutput(sink), kind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A TaintTracking Configuration used for tracking flow through APIs.
|
||||
* The sources are the parameters of the API and the fields of the enclosing type.
|
||||
*
|
||||
* This can be used to generate Sink summaries for APIs, if the API propgates a parameter (or enclosing type field)
|
||||
* into an existing known sink (then the API itself becomes a sink).
|
||||
*/
|
||||
private class PropagateToSinkConfiguration extends PropagateToSinkConfigurationSpecific {
|
||||
PropagateToSinkConfiguration() { this = "parameters or fields flowing into sinks" }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { ExternalFlow::sinkNode(sink, _) }
|
||||
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSourceCallContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sink model(s) of `api`, if there is flow from a parameter to an existing known sink.
|
||||
*/
|
||||
string captureSink(TargetApi api) {
|
||||
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
|
||||
config.hasFlow(src, sink) and
|
||||
ExternalFlow::sinkNode(sink, kind) and
|
||||
api = src.getEnclosingCallable() and
|
||||
isRelevantSinkKind(kind) and
|
||||
result = asSinkModel(api, asInputArgument(src), kind)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Provides predicates related to capturing summary models of the Standard or a 3rd party library.
|
||||
*/
|
||||
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.internal.DataFlowNodes
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow as ContainerFlow
|
||||
private import semmle.code.java.dataflow.DataFlow as Df
|
||||
private import semmle.code.java.dataflow.TaintTracking as Tt
|
||||
import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
|
||||
import semmle.code.java.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
import semmle.code.java.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
|
||||
module DataFlow = Df::DataFlow;
|
||||
|
||||
module TaintTracking = Tt::TaintTracking;
|
||||
|
||||
class Type = J::Type;
|
||||
|
||||
private J::Method superImpl(J::Method m) {
|
||||
result = m.getAnOverride() and
|
||||
not exists(result.getAnOverride()) and
|
||||
not m instanceof J::ToStringMethod
|
||||
}
|
||||
|
||||
private predicate isInTestFile(J::File file) {
|
||||
file.getAbsolutePath().matches("%src/test/%") or
|
||||
file.getAbsolutePath().matches("%/guava-tests/%") or
|
||||
file.getAbsolutePath().matches("%/guava-testlib/%")
|
||||
}
|
||||
|
||||
private predicate isJdkInternal(J::CompilationUnit cu) {
|
||||
cu.getPackage().getName().matches("org.graalvm%") or
|
||||
cu.getPackage().getName().matches("com.sun%") or
|
||||
cu.getPackage().getName().matches("javax.swing%") or
|
||||
cu.getPackage().getName().matches("java.awt%") or
|
||||
cu.getPackage().getName().matches("sun%") or
|
||||
cu.getPackage().getName().matches("jdk.%") or
|
||||
cu.getPackage().getName().matches("java2d.%") or
|
||||
cu.getPackage().getName().matches("build.tools.%") or
|
||||
cu.getPackage().getName().matches("propertiesparser.%") or
|
||||
cu.getPackage().getName().matches("org.jcp.%") or
|
||||
cu.getPackage().getName().matches("org.w3c.%") or
|
||||
cu.getPackage().getName().matches("org.ietf.jgss.%") or
|
||||
cu.getPackage().getName().matches("org.xml.sax%") or
|
||||
cu.getPackage().getName() = "compileproperties" or
|
||||
cu.getPackage().getName() = "netscape.javascript" or
|
||||
cu.getPackage().getName() = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to generate models for `api`.
|
||||
*/
|
||||
private predicate isRelevantForModels(J::Callable api) {
|
||||
not isInTestFile(api.getCompilationUnit().getFile()) and
|
||||
not isJdkInternal(api.getCompilationUnit()) and
|
||||
not api instanceof J::MainMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* A class of Callables that are relevant for generating summary, source and sinks models for.
|
||||
*
|
||||
* In the Standard library and 3rd party libraries it the Callables that can be called
|
||||
* from outside the library itself.
|
||||
*/
|
||||
class TargetApiSpecific extends J::Callable {
|
||||
TargetApiSpecific() {
|
||||
this.isPublic() and
|
||||
this.fromSource() and
|
||||
(
|
||||
this.getDeclaringType().isPublic() or
|
||||
superImpl(this).getDeclaringType().isPublic()
|
||||
) and
|
||||
isRelevantForModels(this)
|
||||
}
|
||||
}
|
||||
|
||||
private string isExtensible(J::RefType ref) {
|
||||
if ref.isFinal() then result = "false" else result = "true"
|
||||
}
|
||||
|
||||
private string typeAsModel(J::RefType type) {
|
||||
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
|
||||
}
|
||||
|
||||
private J::RefType bestTypeForModel(TargetApiSpecific api) {
|
||||
if exists(superImpl(api))
|
||||
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
|
||||
else result = api.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate type name for the model. Either the type
|
||||
* declaring the method or the supertype introducing the method.
|
||||
*/
|
||||
private string typeAsSummaryModel(TargetApiSpecific api) {
|
||||
result = typeAsModel(bestTypeForModel(api))
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the first 6 columns for CSV rows.
|
||||
*/
|
||||
string asPartialModel(TargetApiSpecific api) {
|
||||
result =
|
||||
typeAsSummaryModel(api) + ";" //
|
||||
+ isExtensible(bestTypeForModel(api)) + ";" //
|
||||
+ api.getName() + ";" //
|
||||
+ ExternalFlow::paramsString(api) + ";" //
|
||||
+ /* ext + */ ";" //
|
||||
}
|
||||
|
||||
private predicate isPrimitiveTypeUsedForBulkData(J::Type t) {
|
||||
t.getName().regexpMatch("byte|char|Byte|Character")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for type `t` for fields that are relevant as an intermediate
|
||||
* read or write step in the data flow analysis.
|
||||
*/
|
||||
predicate isRelevantType(J::Type t) {
|
||||
not t instanceof J::TypeClass and
|
||||
not t instanceof J::EnumType and
|
||||
not t instanceof J::PrimitiveType and
|
||||
not t instanceof J::BoxedType and
|
||||
not t.(J::RefType).getAnAncestor().hasQualifiedName("java.lang", "Number") and
|
||||
not t.(J::RefType).getAnAncestor().hasQualifiedName("java.nio.charset", "Charset") and
|
||||
(
|
||||
not t.(J::Array).getElementType() instanceof J::PrimitiveType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(J::Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(J::Array).getElementType() instanceof J::BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(J::Array).getElementType())
|
||||
) and
|
||||
(
|
||||
not t.(ContainerFlow::CollectionType).getElementType() instanceof J::BoxedType or
|
||||
isPrimitiveTypeUsedForBulkData(t.(ContainerFlow::CollectionType).getElementType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string representation of the qualifier.
|
||||
*/
|
||||
string qualifierString() { result = "Argument[-1]" }
|
||||
|
||||
private string parameterAccess(J::Parameter p) {
|
||||
if
|
||||
p.getType() instanceof J::Array and
|
||||
not isPrimitiveTypeUsedForBulkData(p.getType().(J::Array).getElementType())
|
||||
then result = "Argument[" + p.getPosition() + "].ArrayElement"
|
||||
else
|
||||
if p.getType() instanceof ContainerFlow::ContainerType
|
||||
then result = "Argument[" + p.getPosition() + "].Element"
|
||||
else result = "Argument[" + p.getPosition() + "]"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string representation of the parameter node `p`.
|
||||
*/
|
||||
string parameterNodeAsInput(DataFlow::ParameterNode p) {
|
||||
result = parameterAccess(p.asParameter())
|
||||
or
|
||||
result = qualifierString() and p instanceof DataFlow::InstanceParameterNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV string represention of the the return node `node`.
|
||||
*/
|
||||
string returnNodeAsOutput(DataFlowImplCommon::ReturnNodeExt node) {
|
||||
if node.getKind() instanceof DataFlowImplCommon::ValueReturnKind
|
||||
then result = "ReturnValue"
|
||||
else
|
||||
exists(int pos |
|
||||
pos = node.getKind().(DataFlowImplCommon::ParamUpdateReturnKind).getPosition()
|
||||
|
|
||||
result = parameterAccess(node.getEnclosingCallable().getParameter(pos))
|
||||
or
|
||||
result = qualifierString() and pos = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable of `ret`.
|
||||
*/
|
||||
J::Callable returnNodeEnclosingCallable(DataFlowImplCommon::ReturnNodeExt ret) {
|
||||
result = DataFlowImplCommon::getNodeEnclosingCallable(ret).asCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an own instance access.
|
||||
*/
|
||||
predicate isOwnInstanceAccessNode(ReturnNode node) {
|
||||
node.asExpr().(J::ThisAccess).isOwnInstanceAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Language specific parts of the `PropagateToSinkConfiguration`.
|
||||
*/
|
||||
class PropagateToSinkConfigurationSpecific extends TaintTracking::Configuration {
|
||||
PropagateToSinkConfigurationSpecific() { this = "parameters or fields flowing into sinks" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
(
|
||||
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
|
||||
source instanceof DataFlow::ParameterNode
|
||||
) and
|
||||
source.getEnclosingCallable().isPublic() and
|
||||
exists(J::RefType t |
|
||||
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
|
||||
not t instanceof J::TypeObject and
|
||||
t.isPublic()
|
||||
) and
|
||||
isRelevantForModels(source.getEnclosingCallable())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CSV input string representation of `source`.
|
||||
*/
|
||||
string asInputArgument(DataFlow::Node source) {
|
||||
exists(int pos |
|
||||
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
|
||||
result = "Argument[" + pos + "]"
|
||||
)
|
||||
or
|
||||
source.asExpr() instanceof J::FieldAccess and
|
||||
result = qualifierString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `kind` is a relevant sink kind for creating sink models.
|
||||
*/
|
||||
bindingset[kind]
|
||||
predicate isRelevantSinkKind(string kind) { not kind = "logging" }
|
||||
Reference in New Issue
Block a user