Merge pull request #1757 from jbj/pyrameterized-taint

C++: Use pyrameterized modules for TaintTracking
This commit is contained in:
Anders Schack-Mulligen
2019-08-20 16:33:22 +02:00
committed by GitHub
40 changed files with 1578 additions and 1380 deletions

View File

@@ -25,6 +25,7 @@
- The predicate `Variable.getAnAssignedValue()` now reports assignments to fields resulting from aggregate initialization (` = {...}`).
- The predicate `TypeMention.toString()` has been simplified to always return the string "`type mention`". This may improve performance when using `Element.toString()` or its descendants.
- The `semmle.code.cpp.security.TaintTracking` library now considers a pointer difference calculation as blocking taint flow.
- The second copy of the interprocedural `TaintTracking` library has been renamed from `TaintTracking::Configuration2` to `TaintTracking2::Configuration`, and the old name is now deprecated. Import `semmle.code.cpp.dataflow.TaintTracking2` to access the new name.
- Fixed the `LocalScopeVariableReachability.qll` library's handling of loops with an entry condition is both always true upon first entry, and where there is more than one control flow path through the loop condition. This change increases the accuracy of the `LocalScopeVariableReachability.qll` library and queries which depend on it.
- The `semmle.code.cpp.models` library now models data flow through `std::swap`.
- There is a new `Variable.isThreadLocal()` predicate. It can be used to tell whether a variable is `thread_local`.

View File

@@ -16,4 +16,4 @@
removes false positives that arose from paths through impossible `toString()`
calls.
* The library `VCS.qll` and all queries that imported it have been removed.
* The second copy of the interprocedural `TaintTracking` library has been renamed from `TaintTracking::Configuration2` to `TaintTracking2::Configuration`, and the old name is now deprecated. Import `semmle.code.java.dataflow.TaintTracking2` to access the new name.

View File

@@ -25,6 +25,21 @@
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll"
],
"Taint tracking C/C++": [
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
],
"Taint tracking C#": [
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll"
],
"Taint tracking Java": [
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
],
"IR Instruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",

View File

@@ -9,259 +9,14 @@
*/
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.DataFlow2
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
module TaintTracking {
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
private import semmle.code.cpp.dataflow.TaintTracking2
/**
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
* this abstract class.
*
* A taint-tracking configuration is a special data flow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values but are still relevant from a taint-tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
* ```
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isSanitizer`.
* // Optionally override `isSanitizerEdge`.
* // Optionally override `isAdditionalTaintStep`.
* }
* ```
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but it is unsupported to depend on a
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
* overridden predicates that define sources, sinks, or additional steps.
* Instead, the dependency should go to a `TaintTracking::Configuration2` or
* a `DataFlow{2,3,4}::Configuration`.
* DEPRECATED: Use TaintTracking2::Configuration instead.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/** Holds if data flow from `node1` to `node2` is prohibited. */
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
none()
}
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source,
DataFlow::Node target)
{ none() }
final override
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/** DEPRECATED: use `isSanitizerEdge` instead. */
override deprecated
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
this.isSanitizerEdge(node1, node2)
}
final override
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}
/**
* A taint-tracking configuration that is backed by the `DataFlow2` library
* instead of `DataFlow`. Use this class when taint-tracking configurations
* or data-flow configurations must depend on each other.
*
* See `TaintTracking::Configuration` for the full documentation.
*/
abstract class Configuration2 extends DataFlow2::Configuration {
bindingset[this]
Configuration2() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/** Holds if data flow from `node1` to `node2` is prohibited. */
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
none()
}
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source,
DataFlow::Node target)
{ none() }
final override
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/** DEPRECATED: use `isSanitizerEdge` instead. */
override deprecated
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
this.isSanitizerEdge(node1, node2)
}
final override
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Taint can flow into using ordinary data flow.
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
exists(Expr exprFrom, Expr exprTo |
exprFrom = nodeFrom.asExpr() and
exprTo = nodeTo.asExpr()
|
exprFrom = exprTo.getAChild() and
not noParentExprFlow(exprFrom, exprTo) and
not noFlowFromChildExpr(exprTo)
or
// Taint can flow from the `x` variable in `x++` to all subsequent
// accesses to the unmodified `x` variable.
//
// `DataFlow` without taint specifies flow from `++x` and `x += 1` into the
// variable `x` and thus into subsequent accesses because those expressions
// compute the same value as `x`. This is not the case for `x++`, which
// computes a different value, so we have to add that ourselves for taint
// tracking. The flow from expression `x` into `x++` etc. is handled in the
// case above.
exprTo = DataFlow::getAnAccessToAssignedVariable(
exprFrom.(PostfixCrementOperation)
)
)
or
// Taint can flow through modeled functions
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
}
/**
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) {
localTaintStep*(source, sink)
}
/**
* Holds if we do not propagate taint from `fromExpr` to `toExpr`
* even though `toExpr` is the AST parent of `fromExpr`.
*/
private predicate noParentExprFlow(Expr fromExpr, Expr toExpr) {
fromExpr = toExpr.(ConditionalExpr).getCondition()
or
fromExpr = toExpr.(CommaExpr).getLeftOperand()
or
fromExpr = toExpr.(AssignExpr).getLValue() // LHS of `=`
}
/**
* Holds if we do not propagate taint from a child of `e` to `e` itself.
*/
private predicate noFlowFromChildExpr(Expr e) {
e instanceof ComparisonOperation
or
e instanceof LogicalAndExpr
or
e instanceof LogicalOrExpr
or
e instanceof Call
or
e instanceof SizeofOperator
or
e instanceof AlignofOperator
}
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
outModel.isOutParameterPointer(argOutIndex) and
exists(int argInIndex, FunctionInput inModel |
f.hasDataFlow(inModel, outModel)
|
// Taint flows from a pointer to a dereference, which DataFlow does not handle
// memcpy(&dest_var, tainted_ptr, len)
inModel.isInParameterPointer(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
or
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
outModel.isOutParameterPointer(argOutIndex) and
exists(int argInIndex, FunctionInput inModel |
f.hasTaintFlow(inModel, outModel)
|
inModel.isInParameterPointer(argInIndex) and
exprIn = call.getArgument(argInIndex)
or
inModel.isInParameterPointer(argInIndex) and
call.passesByReference(argInIndex, exprIn)
or
inModel.isInParameter(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
}
deprecated
class Configuration2 = TaintTracking2::Configuration;
}

View File

@@ -0,0 +1,12 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/
module TaintTracking2 {
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
}

View File

@@ -0,0 +1,123 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.Taint
private module DataFlow {
import semmle.code.cpp.dataflow.internal.DataFlowUtil
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Taint can flow into using ordinary data flow.
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
exists(Expr exprFrom, Expr exprTo |
exprFrom = nodeFrom.asExpr() and
exprTo = nodeTo.asExpr()
|
exprFrom = exprTo.getAChild() and
not noParentExprFlow(exprFrom, exprTo) and
not noFlowFromChildExpr(exprTo)
or
// Taint can flow from the `x` variable in `x++` to all subsequent
// accesses to the unmodified `x` variable.
//
// `DataFlow` without taint specifies flow from `++x` and `x += 1` into the
// variable `x` and thus into subsequent accesses because those expressions
// compute the same value as `x`. This is not the case for `x++`, which
// computes a different value, so we have to add that ourselves for taint
// tracking. The flow from expression `x` into `x++` etc. is handled in the
// case above.
exprTo = DataFlow::getAnAccessToAssignedVariable(
exprFrom.(PostfixCrementOperation)
)
)
or
// Taint can flow through modeled functions
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
}
/**
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) {
localTaintStep*(source, sink)
}
/**
* Holds if we do not propagate taint from `fromExpr` to `toExpr`
* even though `toExpr` is the AST parent of `fromExpr`.
*/
private predicate noParentExprFlow(Expr fromExpr, Expr toExpr) {
fromExpr = toExpr.(ConditionalExpr).getCondition()
or
fromExpr = toExpr.(CommaExpr).getLeftOperand()
or
fromExpr = toExpr.(AssignExpr).getLValue() // LHS of `=`
}
/**
* Holds if we do not propagate taint from a child of `e` to `e` itself.
*/
private predicate noFlowFromChildExpr(Expr e) {
e instanceof ComparisonOperation
or
e instanceof LogicalAndExpr
or
e instanceof LogicalOrExpr
or
e instanceof Call
or
e instanceof SizeofOperator
or
e instanceof AlignofOperator
}
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
outModel.isOutParameterPointer(argOutIndex) and
exists(int argInIndex, FunctionInput inModel |
f.hasDataFlow(inModel, outModel)
|
// Taint flows from a pointer to a dereference, which DataFlow does not handle
// memcpy(&dest_var, tainted_ptr, len)
inModel.isInParameterPointer(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
or
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
outModel.isOutParameterPointer(argOutIndex) and
exists(int argInIndex, FunctionInput inModel |
f.hasTaintFlow(inModel, outModel)
|
inModel.isInParameterPointer(argInIndex) and
exprIn = call.getArgument(argInIndex)
or
inModel.isInParameterPointer(argInIndex) and
call.passesByReference(argInIndex, exprIn)
or
inModel.isInParameter(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
}

View File

@@ -0,0 +1,98 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
* this abstract class.
*
* A taint-tracking configuration is a special data flow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values but are still relevant from a taint-tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
* ```
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isSanitizer`.
* // Optionally override `isSanitizerEdge`.
* // Optionally override `isAdditionalTaintStep`.
* }
* ```
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but it is unsupported to depend on a
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
* overridden predicates that define sources, sinks, or additional steps.
* Instead, the dependency should go to a `TaintTracking2::Configuration` or
* a `DataFlow{2,3,4}::Configuration`.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/** Holds if data flow from `node1` to `node2` is prohibited. */
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
none()
}
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source,
DataFlow::Node target)
{ none() }
final override
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/** DEPRECATED: use `isSanitizerEdge` instead. */
override deprecated
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
this.isSanitizerEdge(node1, node2)
}
final override
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}

View File

@@ -0,0 +1,5 @@
import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public
module Private {
import semmle.code.cpp.dataflow.DataFlow::DataFlow as DataFlow
}

View File

@@ -0,0 +1,98 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
* this abstract class.
*
* A taint-tracking configuration is a special data flow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values but are still relevant from a taint-tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
* ```
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isSanitizer`.
* // Optionally override `isSanitizerEdge`.
* // Optionally override `isAdditionalTaintStep`.
* }
* ```
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but it is unsupported to depend on a
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
* overridden predicates that define sources, sinks, or additional steps.
* Instead, the dependency should go to a `TaintTracking2::Configuration` or
* a `DataFlow{2,3,4}::Configuration`.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }
/** Holds if data flow from `node1` to `node2` is prohibited. */
predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) {
none()
}
/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source,
DataFlow::Node target)
{ none() }
final override
predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/** DEPRECATED: use `isSanitizerEdge` instead. */
override deprecated
predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
this.isSanitizerEdge(node1, node2)
}
final override
predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}

View File

@@ -0,0 +1,5 @@
import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public
module Private {
import semmle.code.cpp.dataflow.DataFlow2::DataFlow2 as DataFlow
}

View File

@@ -3,6 +3,8 @@ import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.DataFlow2
import semmle.code.cpp.dataflow.DataFlow3
import semmle.code.cpp.dataflow.DataFlow4
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.dataflow.TaintTracking2
import semmle.code.cpp.dataflow.RecursionPrevention
class TestConf1 extends DataFlow::Configuration {

View File

@@ -6,70 +6,5 @@
import csharp
module TaintTracking {
private import semmle.code.csharp.dataflow.DataFlow
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.csharp.dataflow.internal.tainttracking1.TaintTrackingImpl
}

View File

@@ -6,70 +6,5 @@
import csharp
module TaintTracking2 {
private import semmle.code.csharp.dataflow.DataFlow2
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow2::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.csharp.dataflow.internal.tainttracking2.TaintTrackingImpl
}

View File

@@ -6,70 +6,5 @@
import csharp
module TaintTracking3 {
private import semmle.code.csharp.dataflow.DataFlow3
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow3::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.csharp.dataflow.internal.tainttracking3.TaintTrackingImpl
}

View File

@@ -6,70 +6,5 @@
import csharp
module TaintTracking4 {
private import semmle.code.csharp.dataflow.DataFlow4
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow4::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.csharp.dataflow.internal.tainttracking4.TaintTrackingImpl
}

View File

@@ -6,70 +6,5 @@
import csharp
module TaintTracking5 {
private import semmle.code.csharp.dataflow.DataFlow5
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow5::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.csharp.dataflow.internal.tainttracking5.TaintTrackingImpl
}

View File

@@ -1,4 +1,4 @@
import csharp
private import csharp
private import TaintTrackingPublic
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate

View File

@@ -1,4 +1,4 @@
import csharp
private import csharp
private import TaintTrackingPrivate
/**

View File

@@ -0,0 +1,65 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,6 @@
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
module Private {
import semmle.code.csharp.dataflow.DataFlow::DataFlow as DataFlow
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
}

View File

@@ -0,0 +1,65 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,6 @@
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
module Private {
import semmle.code.csharp.dataflow.DataFlow2::DataFlow2 as DataFlow
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
}

View File

@@ -0,0 +1,65 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,6 @@
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
module Private {
import semmle.code.csharp.dataflow.DataFlow3::DataFlow3 as DataFlow
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
}

View File

@@ -0,0 +1,65 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,6 @@
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
module Private {
import semmle.code.csharp.dataflow.DataFlow4::DataFlow4 as DataFlow
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
}

View File

@@ -0,0 +1,65 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the intermediate node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }
/**
* Holds if the additional taint propagation step from `pred` to `succ`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
isAdditionalTaintStep(pred, succ)
or
localAdditionalTaintStep(pred, succ)
or
succ = pred.(DataFlow::NonLocalJumpNode).getAJumpSuccessor(false)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,6 @@
import semmle.code.csharp.dataflow.internal.TaintTrackingPublic as Public
module Private {
import semmle.code.csharp.dataflow.DataFlow5::DataFlow5 as DataFlow
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
}

View File

@@ -1,6 +1,13 @@
import csharp
import semmle.code.csharp.dataflow.TaintTracking
// Test that all the copies of the taint tracking library can be imported
// simultaneously without errors.
import semmle.code.csharp.dataflow.TaintTracking2
import semmle.code.csharp.dataflow.TaintTracking3
import semmle.code.csharp.dataflow.TaintTracking4
import semmle.code.csharp.dataflow.TaintTracking5
class FlowConfig extends TaintTracking::Configuration {
FlowConfig() { this = "FlowConfig" }

View File

@@ -12,10 +12,11 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
import DataFlow2::PathGraph
class XSSConfig extends TaintTracking::Configuration2 {
class XSSConfig extends TaintTracking2::Configuration {
XSSConfig() { this = "XSSConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

View File

@@ -12,10 +12,11 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
import DataFlow2::PathGraph
class XSSLocalConfig extends TaintTracking::Configuration2 {
class XSSLocalConfig extends TaintTracking2::Configuration {
XSSLocalConfig() { this = "XSSLocalConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }

View File

@@ -14,6 +14,7 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.security.XSS
/**
@@ -80,7 +81,7 @@ predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
)
}
class StackTraceStringToXssSinkFlowConfig extends TaintTracking::Configuration2 {
class StackTraceStringToXssSinkFlowConfig extends TaintTracking2::Configuration {
StackTraceStringToXssSinkFlowConfig() {
this = "StackTraceExposure::StackTraceStringToXssSinkFlowConfig"
}
@@ -119,7 +120,7 @@ class GetMessageFlowSource extends MethodAccess {
}
}
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking::Configuration2 {
class GetMessageFlowSourceToXssSinkFlowConfig extends TaintTracking2::Configuration {
GetMessageFlowSourceToXssSinkFlowConfig() {
this = "StackTraceExposure::GetMessageFlowSourceToXssSinkFlowConfig"
}

View File

@@ -13,9 +13,10 @@
import java
import XmlParsers
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import DataFlow::PathGraph
class SafeSAXSourceFlowConfig extends TaintTracking::Configuration2 {
class SafeSAXSourceFlowConfig extends TaintTracking2::Configuration {
SafeSAXSourceFlowConfig() { this = "XmlParsers::SafeSAXSourceFlowConfig" }
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXSource }

View File

@@ -2,803 +2,18 @@
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DataFlow2
import semmle.code.java.Collections
private import SSA
private import DefUse
private import semmle.code.java.security.SecurityTests
private import semmle.code.java.security.Validation
private import semmle.code.java.frameworks.android.Intent
private import semmle.code.java.frameworks.Guice
private import semmle.code.java.frameworks.Protobuf
private import semmle.code.java.Maps
private import semmle.code.java.dataflow.internal.ContainerFlow
import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule
module TaintTracking {
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
// Ignore paths through test code.
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
node.asExpr() instanceof ValidatedVariableAccess
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
localAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingImpl
private import semmle.code.java.dataflow.TaintTracking2
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
* DEPRECATED: Use TaintTracking2::Configuration instead.
*/
abstract class Configuration2 extends DataFlow2::Configuration {
bindingset[this]
Configuration2() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
// Ignore paths through test code.
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
node.asExpr() instanceof ValidatedVariableAccess
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
localAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}
/**
* Holds if taint can flow from `src` to `sink` in zero or more
* local (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
/**
* Holds if taint can flow in one local step from `src` to `sink`.
*/
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
DataFlow::localFlowStep(src, sink) or
localAdditionalTaintStep(src, sink)
}
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
* different objects.
*/
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
or
exists(Argument arg |
src.asExpr() = arg and
arg.isVararg() and
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
)
}
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
* different objects.
*/
private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString
or
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
or
sink.(ArrayCreationExpr).getInit() = src
or
sink.(ArrayInit).getAnInit() = src
or
sink.(ArrayAccess).getArray() = src
or
sink.(LogicExpr).getAnOperand() = src
or
exists(Assignment assign | assign.getSource() = src |
sink = assign.getDest().(ArrayAccess).getArray()
)
or
exists(EnhancedForStmt for, SsaExplicitUpdate v |
for.getExpr() = src and
v.getDefiningExpr() = for.getVariable() and
v.getAFirstUse() = sink
)
or
containerStep(src, sink)
or
constructorStep(src, sink)
or
qualifierToMethodStep(src, sink)
or
qualifierToArgumentStep(src, sink)
or
argToMethodStep(src, sink)
or
argToArgStep(src, sink)
or
argToQualifierStep(src, sink)
or
comparisonStep(src, sink)
or
stringBuilderStep(src, sink)
or
serializationStep(src, sink)
}
private class BulkData extends RefType {
BulkData() {
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
or
exists(RefType t | this.getASourceSupertype*() = t |
t.hasQualifiedName("java.io", "InputStream") or
t.hasQualifiedName("java.nio", "ByteBuffer") or
t.hasQualifiedName("java.lang", "Readable") or
t.hasQualifiedName("java.io", "DataInput") or
t.hasQualifiedName("java.nio.channels", "ReadableByteChannel")
)
}
}
/**
* Holds if `c` is a constructor for a subclass of `java.io.InputStream` that
* wraps an underlying data source. The underlying data source is given as a
* the `argi`'th parameter to the constructor.
*
* An object construction of such a wrapper is likely to preserve the data flow
* status of its argument.
*/
private predicate inputStreamWrapper(Constructor c, int argi) {
c.getParameterType(argi) instanceof BulkData and
c.getDeclaringType().getASourceSupertype().hasQualifiedName("java.io", "InputStream")
}
/** An object construction that preserves the data flow status of any of its arguments. */
private predicate constructorStep(Expr tracked, ConstructorCall sink) {
exists(int argi | sink.getArgument(argi) = tracked |
exists(string s | sink.getConstructedType().getQualifiedName() = s |
// String constructor does nothing to data
s = "java.lang.String" and argi = 0
or
// some readers preserve the content of streams
s = "java.io.InputStreamReader" and argi = 0
or
s = "java.io.BufferedReader" and argi = 0
or
s = "java.io.CharArrayReader" and argi = 0
or
s = "java.io.StringReader" and argi = 0
or
// data preserved through streams
s = "java.io.ObjectInputStream" and argi = 0
or
s = "java.io.ByteArrayInputStream" and argi = 0
or
s = "java.io.DataInputStream" and argi = 0
or
s = "java.io.BufferedInputStream" and argi = 0
or
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
or
s = "java.beans.XMLDecoder" and argi = 0
or
// a tokenizer preserves the content of a string
s = "java.util.StringTokenizer" and argi = 0
or
// unzipping the stream preserves content
s = "java.util.zip.ZipInputStream" and argi = 0
or
s = "java.util.zip.GZIPInputStream" and argi = 0
or
// string builders and buffers
s = "java.lang.StringBuilder" and argi = 0
or
s = "java.lang.StringBuffer" and argi = 0
or
// a cookie with tainted ingredients is tainted
s = "javax.servlet.http.Cookie" and argi = 0
or
s = "javax.servlet.http.Cookie" and argi = 1
or
// various xml stream source constructors.
s = "org.xml.sax.InputSource" and argi = 0
or
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
or
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
or
s = "javax.xml.transform.stream.StreamSource" and argi = 0
or
//a URI constructed from a tainted string is tainted.
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
)
or
exists(RefType t | t.getQualifiedName() = "java.lang.Number" |
hasSubtype*(t, sink.getConstructedType())
) and
argi = 0
or
// wrappers constructed by extension
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
c = sink.getConstructor() and
p = c.getParameter(argi) and
sup.getEnclosingCallable() = c and
constructorStep(p.getAnAccess(), sup)
)
or
// a custom InputStream that wraps a tainted data source is tainted
inputStreamWrapper(sink.getConstructor(), argi)
)
}
/** Access to a method that passes taint from qualifier to argument. */
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
exists(MethodAccess ma, int arg |
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
tracked = ma.getQualifier() and
sink = ma.getArgument(arg)
)
}
/** Methods that passes tainted data from qualifier to argument. */
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
m.hasName("writeTo") and
arg = 0
or
m.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
m.hasName("read") and
arg = 0
or
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
m.hasName("read") and
arg = 0
}
/** Access to a method that passes taint from the qualifier. */
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
tracked = sink.getQualifier()
}
/**
* Methods that return tainted data when called on tainted data.
*/
private predicate taintPreservingQualifierToMethod(Method m) {
m.getDeclaringType() instanceof TypeString and
(
m.getName() = "concat" or
m.getName() = "endsWith" or
m.getName() = "getBytes" or
m.getName() = "split" or
m.getName() = "substring" or
m.getName() = "toCharArray" or
m.getName() = "toLowerCase" or
m.getName() = "toString" or
m.getName() = "toUpperCase" or
m.getName() = "trim"
)
or
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
hasSubtype*(c, m.getDeclaringType())
) and
(
m.getName().matches("to%String") or
m.getName() = "toByteArray" or
m.getName().matches("%Value")
)
or
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
(
m.getName() = "read" and m.getNumberOfParameters() = 0
or
m.getName() = "readLine"
)
or
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
m.getName() = "toString"
or
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
m.getName().matches("next%")
or
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
(m.getName() = "toByteArray" or m.getName() = "toString")
or
m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and
m.getName().matches("read%")
or
(
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
) and
(m.getName() = "toString" or m.getName() = "append")
or
m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
m.hasName("getInputSource")
or
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
m.hasName("getInputStream")
or
m instanceof IntentGetExtraMethod
or
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
m.hasName("get")
or
m = any(GuiceProvider gp).getAnOverridingGetMethod()
or
m = any(ProtobufMessageLite p).getAGetterMethod()
}
private class StringReplaceMethod extends Method {
StringReplaceMethod() {
getDeclaringType() instanceof TypeString and
(
hasName("replace") or
hasName("replaceAll") or
hasName("replaceFirst")
)
}
}
private predicate unsafeEscape(MethodAccess ma) {
// Removing `<script>` tags using a string-replace method is
// unsafe if such a tag is embedded inside another one (e.g. `<scr<script>ipt>`).
exists(StringReplaceMethod m | ma.getMethod() = m |
ma.getArgument(0).(StringLiteral).getRepresentedString() = "(<script>)" and
ma.getArgument(1).(StringLiteral).getRepresentedString() = ""
)
}
/** Access to a method that passes taint from an argument. */
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
exists(Method m, int i |
m = sink.(MethodAccess).getMethod() and
taintPreservingArgumentToMethod(m, i) and
tracked = sink.(MethodAccess).getArgument(i)
)
}
/**
* Holds if `method` is a library method that return tainted data if its
* `arg`th argument is tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
method instanceof StringReplaceMethod and arg = 1
or
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
hasSubtype*(c, method.getDeclaringType())
) and
(
method.getName().matches("parse%") and arg = 0
or
method.getName().matches("valueOf%") and arg = 0
or
method.getName().matches("to%String") and arg = 0
)
or
method.getDeclaringType() instanceof TypeString and
method.getName() = "concat" and
arg = 0
or
(
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
) and
(
method.getName() = "append" and arg = 0
or
method.getName() = "insert" and arg = 1
or
method.getName() = "replace" and arg = 2
)
or
(
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
) and
(
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
or
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
or
method.getName() = "encodeToString" and arg = 0
or
method.getName() = "wrap" and arg = 0
)
or
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
(
method.getName() = "buffer" and arg = 0
or
method.getName() = "readLines" and arg = 0
or
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
or
method.getName() = "toBufferedInputStream" and arg = 0
or
method.getName() = "toBufferedReader" and arg = 0
or
method.getName() = "toByteArray" and arg = 0
or
method.getName() = "toCharArray" and arg = 0
or
method.getName() = "toInputStream" and arg = 0
or
method.getName() = "toString" and arg = 0
)
or
// A URI created from a tainted string is still tainted.
method.getDeclaringType().hasQualifiedName("java.net", "URI") and
method.hasName("create") and
arg = 0
or
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
method.hasName("sourceToInputSource") and
arg = 0
or
exists(ProtobufParser p | method = p.getAParseFromMethod()) and
arg = 0
or
exists(ProtobufMessageLite m | method = m.getAParseFromMethod()) and
arg = 0
}
/**
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
* between arguments.
*/
private predicate argToArgStep(Expr tracked, RValue sink) {
exists(MethodAccess ma, Method method, int input, int output |
taintPreservingArgToArg(method, input, output) and
ma.getMethod() = method and
ma.getArgument(input) = tracked and
ma.getArgument(output) = sink
)
}
/**
* Holds if `method` is a library method that writes tainted data to the
* `output`th argument if the `input`th argument is tainted.
*/
private predicate taintPreservingArgToArg(Method method, int input, int output) {
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
(
method.hasName("copy") and input = 0 and output = 1
or
method.hasName("copyLarge") and input = 0 and output = 1
or
method.hasName("read") and input = 0 and output = 1
or
method.hasName("readFully") and
input = 0 and
output = 1 and
not method.getParameterType(1).hasName("int")
or
method.hasName("write") and input = 0 and output = 1
or
method.hasName("writeChunked") and input = 0 and output = 1
or
method.hasName("writeLines") and input = 0 and output = 2
or
method.hasName("writeLines") and input = 1 and output = 2
)
or
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
method.hasName("arraycopy") and
input = 0 and
output = 2
}
/**
* Holds if `tracked` is the argument of a method that transfers taint
* from the argument to the qualifier and `sink` is the qualifier.
*/
private predicate argToQualifierStep(Expr tracked, Expr sink) {
exists(Method m, int i, MethodAccess ma |
taintPreservingArgumentToQualifier(m, i) and
ma.getMethod() = m and
tracked = ma.getArgument(i) and
sink = ma.getQualifier()
)
}
/**
* Holds if `method` is a method that transfers taint from argument to qualifier and
* `arg` is the index of the argument.
*/
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
method.hasName("write") and
arg = 0
}
/** A comparison or equality test with a constant. */
private predicate comparisonStep(Expr tracked, Expr sink) {
exists(Expr other |
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof EqualityTest |
e = sink and
e.hasOperands(tracked, other)
)
or
exists(MethodAccess m | m.getMethod() instanceof EqualsMethod |
m = sink and
(
m.getQualifier() = tracked and m.getArgument(0) = other
or
m.getQualifier() = other and m.getArgument(0) = tracked
)
)
|
other.isCompileTimeConstant() or other instanceof NullLiteral
)
}
/** Flow through a `StringBuilder`. */
private predicate stringBuilderStep(Expr tracked, Expr sink) {
exists(StringBuilderVar sbvar, MethodAccess input, int arg |
input = sbvar.getAnInput(arg) and
tracked = input.getArgument(arg) and
sink = sbvar.getToStringCall()
)
}
/** Flow through data serialization. */
private predicate serializationStep(Expr tracked, Expr sink) {
exists(ObjectOutputStreamVar v, VariableAssign def |
def = v.getADef() and
exists(MethodAccess ma, RValue use |
ma.getArgument(0) = tracked and
ma = v.getAWriteObjectMethodAccess() and
use = ma.getQualifier() and
defUsePair(def, use)
) and
exists(RValue outputstream, ClassInstanceExpr cie |
cie = def.getSource() and
outputstream = cie.getArgument(0) and
adjacentUseUse(outputstream, sink)
)
)
}
/**
* A local variable that is assigned an `ObjectOutputStream`.
* Writing tainted data to such a stream causes the underlying
* `OutputStream` to be tainted.
*/
class ObjectOutputStreamVar extends LocalVariableDecl {
ObjectOutputStreamVar() {
exists(ClassInstanceExpr cie | cie = this.getAnAssignedValue() |
cie.getType() instanceof TypeObjectOutputStream
)
}
VariableAssign getADef() {
result.getSource().(ClassInstanceExpr).getType() instanceof TypeObjectOutputStream and
result.getDestVar() = this
}
MethodAccess getAWriteObjectMethodAccess() {
result.getQualifier() = getAnAccess() and
result.getMethod().hasName("writeObject")
}
}
}
/**
* A local variable that is initialized to a `StringBuilder`
* or `StringBuffer`. Such variables are often used to
* build up a query using string concatenation.
*/
class StringBuilderVar extends LocalVariableDecl {
StringBuilderVar() {
this.getType() instanceof TypeStringBuilder or
this.getType() instanceof TypeStringBuffer
}
/**
* Gets a call that adds something to this string builder, from the argument at the given index.
*/
MethodAccess getAnInput(int arg) {
result.getQualifier() = getAChainedReference() and
(
result.getMethod().getName() = "append" and arg = 0
or
result.getMethod().getName() = "insert" and arg = 1
or
result.getMethod().getName() = "replace" and arg = 2
)
}
/**
* Gets a call that appends something to this string builder.
*/
MethodAccess getAnAppend() {
result.getQualifier() = getAChainedReference() and
result.getMethod().getName() = "append"
}
MethodAccess getNextAppend(MethodAccess append) {
result = getAnAppend() and
append = getAnAppend() and
(
result.getQualifier() = append
or
not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and
exists(RValue sbva1, RValue sbva2 |
adjacentUseUse(sbva1, sbva2) and
append.getQualifier() = getAChainedReference(sbva1) and
result.getQualifier() = sbva2
)
)
}
/**
* Gets a call that converts this string builder to a string.
*/
MethodAccess getToStringCall() {
result.getQualifier() = getAChainedReference() and
result.getMethod().getName() = "toString"
}
private Expr getAChainedReference(VarAccess sbva) {
// All the methods on `StringBuilder` that return the same type return
// the same object.
result = callReturningSameType+(sbva) and sbva = this.getAnAccess()
or
result = sbva and sbva = this.getAnAccess()
}
/**
* Gets an expression that refers to this `StringBuilder`, possibly after some chained calls.
*/
Expr getAChainedReference() { result = getAChainedReference(_) }
}
private MethodAccess callReturningSameType(Expr ref) {
ref = result.getQualifier() and
result.getMethod().getReturnType() = ref.getType()
deprecated
class Configuration2 = TaintTracking2::Configuration;
}

View File

@@ -0,0 +1,7 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
module TaintTracking2 {
import semmle.code.java.dataflow.internal.tainttracking2.TaintTrackingImpl
}

View File

@@ -0,0 +1,635 @@
private import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.Collections
private import semmle.code.java.dataflow.SSA
private import semmle.code.java.dataflow.DefUse
private import semmle.code.java.security.SecurityTests
private import semmle.code.java.security.Validation
private import semmle.code.java.frameworks.android.Intent
private import semmle.code.java.frameworks.Guice
private import semmle.code.java.frameworks.Protobuf
private import semmle.code.java.Maps
private import semmle.code.java.dataflow.internal.ContainerFlow
/**
* Holds if taint can flow from `src` to `sink` in zero or more
* local (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
/**
* Holds if taint can flow in one local step from `src` to `sink`.
*/
predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
DataFlow::localFlowStep(src, sink) or
localAdditionalTaintStep(src, sink)
}
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
* different objects.
*/
predicate localAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintExprStep(src.asExpr(), sink.asExpr())
or
exists(Argument arg |
src.asExpr() = arg and
arg.isVararg() and
sink.(DataFlow::ImplicitVarargsArray).getCall() = arg.getCall()
)
}
/**
* Holds if `node` should be a barrier in all global taint flow configurations
* but not in local taint.
*/
predicate defaultTaintBarrier(DataFlow::Node node) {
// Ignore paths through test code.
node.getEnclosingCallable().getDeclaringType() instanceof NonSecurityTestClass or
node.asExpr() instanceof ValidatedVariableAccess
}
/**
* Holds if taint can flow in one local step from `src` to `sink` excluding
* local data flow steps. That is, `src` and `sink` are likely to represent
* different objects.
*/
private predicate localAdditionalTaintExprStep(Expr src, Expr sink) {
sink.(AddExpr).getAnOperand() = src and sink.getType() instanceof TypeString
or
sink.(AssignAddExpr).getSource() = src and sink.getType() instanceof TypeString
or
sink.(ArrayCreationExpr).getInit() = src
or
sink.(ArrayInit).getAnInit() = src
or
sink.(ArrayAccess).getArray() = src
or
sink.(LogicExpr).getAnOperand() = src
or
exists(Assignment assign | assign.getSource() = src |
sink = assign.getDest().(ArrayAccess).getArray()
)
or
exists(EnhancedForStmt for, SsaExplicitUpdate v |
for.getExpr() = src and
v.getDefiningExpr() = for.getVariable() and
v.getAFirstUse() = sink
)
or
containerStep(src, sink)
or
constructorStep(src, sink)
or
qualifierToMethodStep(src, sink)
or
qualifierToArgumentStep(src, sink)
or
argToMethodStep(src, sink)
or
argToArgStep(src, sink)
or
argToQualifierStep(src, sink)
or
comparisonStep(src, sink)
or
stringBuilderStep(src, sink)
or
serializationStep(src, sink)
}
private class BulkData extends RefType {
BulkData() {
this.(Array).getElementType().(PrimitiveType).getName().regexpMatch("byte|char")
or
exists(RefType t | this.getASourceSupertype*() = t |
t.hasQualifiedName("java.io", "InputStream") or
t.hasQualifiedName("java.nio", "ByteBuffer") or
t.hasQualifiedName("java.lang", "Readable") or
t.hasQualifiedName("java.io", "DataInput") or
t.hasQualifiedName("java.nio.channels", "ReadableByteChannel")
)
}
}
/**
* Holds if `c` is a constructor for a subclass of `java.io.InputStream` that
* wraps an underlying data source. The underlying data source is given as a
* the `argi`'th parameter to the constructor.
*
* An object construction of such a wrapper is likely to preserve the data flow
* status of its argument.
*/
private predicate inputStreamWrapper(Constructor c, int argi) {
c.getParameterType(argi) instanceof BulkData and
c.getDeclaringType().getASourceSupertype().hasQualifiedName("java.io", "InputStream")
}
/** An object construction that preserves the data flow status of any of its arguments. */
private predicate constructorStep(Expr tracked, ConstructorCall sink) {
exists(int argi | sink.getArgument(argi) = tracked |
exists(string s | sink.getConstructedType().getQualifiedName() = s |
// String constructor does nothing to data
s = "java.lang.String" and argi = 0
or
// some readers preserve the content of streams
s = "java.io.InputStreamReader" and argi = 0
or
s = "java.io.BufferedReader" and argi = 0
or
s = "java.io.CharArrayReader" and argi = 0
or
s = "java.io.StringReader" and argi = 0
or
// data preserved through streams
s = "java.io.ObjectInputStream" and argi = 0
or
s = "java.io.ByteArrayInputStream" and argi = 0
or
s = "java.io.DataInputStream" and argi = 0
or
s = "java.io.BufferedInputStream" and argi = 0
or
s = "com.esotericsoftware.kryo.io.Input" and argi = 0
or
s = "java.beans.XMLDecoder" and argi = 0
or
// a tokenizer preserves the content of a string
s = "java.util.StringTokenizer" and argi = 0
or
// unzipping the stream preserves content
s = "java.util.zip.ZipInputStream" and argi = 0
or
s = "java.util.zip.GZIPInputStream" and argi = 0
or
// string builders and buffers
s = "java.lang.StringBuilder" and argi = 0
or
s = "java.lang.StringBuffer" and argi = 0
or
// a cookie with tainted ingredients is tainted
s = "javax.servlet.http.Cookie" and argi = 0
or
s = "javax.servlet.http.Cookie" and argi = 1
or
// various xml stream source constructors.
s = "org.xml.sax.InputSource" and argi = 0
or
s = "javax.xml.transform.sax.SAXSource" and argi = 0 and sink.getNumArgument() = 1
or
s = "javax.xml.transform.sax.SAXSource" and argi = 1 and sink.getNumArgument() = 2
or
s = "javax.xml.transform.stream.StreamSource" and argi = 0
or
//a URI constructed from a tainted string is tainted.
s = "java.net.URI" and argi = 0 and sink.getNumArgument() = 1
)
or
exists(RefType t | t.getQualifiedName() = "java.lang.Number" |
hasSubtype*(t, sink.getConstructedType())
) and
argi = 0
or
// wrappers constructed by extension
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
c = sink.getConstructor() and
p = c.getParameter(argi) and
sup.getEnclosingCallable() = c and
constructorStep(p.getAnAccess(), sup)
)
or
// a custom InputStream that wraps a tainted data source is tainted
inputStreamWrapper(sink.getConstructor(), argi)
)
}
/** Access to a method that passes taint from qualifier to argument. */
private predicate qualifierToArgumentStep(Expr tracked, RValue sink) {
exists(MethodAccess ma, int arg |
taintPreservingQualifierToArgument(ma.getMethod(), arg) and
tracked = ma.getQualifier() and
sink = ma.getArgument(arg)
)
}
/** Methods that passes tainted data from qualifier to argument. */
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
m.hasName("writeTo") and
arg = 0
or
m.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
m.hasName("read") and
arg = 0
or
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
m.hasName("read") and
arg = 0
}
/** Access to a method that passes taint from the qualifier. */
private predicate qualifierToMethodStep(Expr tracked, MethodAccess sink) {
(taintPreservingQualifierToMethod(sink.getMethod()) or unsafeEscape(sink)) and
tracked = sink.getQualifier()
}
/**
* Methods that return tainted data when called on tainted data.
*/
private predicate taintPreservingQualifierToMethod(Method m) {
m.getDeclaringType() instanceof TypeString and
(
m.getName() = "concat" or
m.getName() = "endsWith" or
m.getName() = "getBytes" or
m.getName() = "split" or
m.getName() = "substring" or
m.getName() = "toCharArray" or
m.getName() = "toLowerCase" or
m.getName() = "toString" or
m.getName() = "toUpperCase" or
m.getName() = "trim"
)
or
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
hasSubtype*(c, m.getDeclaringType())
) and
(
m.getName().matches("to%String") or
m.getName() = "toByteArray" or
m.getName().matches("%Value")
)
or
m.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Reader") and
(
m.getName() = "read" and m.getNumberOfParameters() = 0
or
m.getName() = "readLine"
)
or
m.getDeclaringType().getQualifiedName().matches("%StringWriter") and
m.getName() = "toString"
or
m.getDeclaringType().hasQualifiedName("java.util", "StringTokenizer") and
m.getName().matches("next%")
or
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
(m.getName() = "toByteArray" or m.getName() = "toString")
or
m.getDeclaringType().hasQualifiedName("java.io", "ObjectInputStream") and
m.getName().matches("read%")
or
(
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
m.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
) and
(m.getName() = "toString" or m.getName() = "append")
or
m.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
m.hasName("getInputSource")
or
m.getDeclaringType().hasQualifiedName("javax.xml.transform.stream", "StreamSource") and
m.hasName("getInputStream")
or
m instanceof IntentGetExtraMethod
or
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
m.hasName("get")
or
m = any(GuiceProvider gp).getAnOverridingGetMethod()
or
m = any(ProtobufMessageLite p).getAGetterMethod()
}
private class StringReplaceMethod extends Method {
StringReplaceMethod() {
getDeclaringType() instanceof TypeString and
(
hasName("replace") or
hasName("replaceAll") or
hasName("replaceFirst")
)
}
}
private predicate unsafeEscape(MethodAccess ma) {
// Removing `<script>` tags using a string-replace method is
// unsafe if such a tag is embedded inside another one (e.g. `<scr<script>ipt>`).
exists(StringReplaceMethod m | ma.getMethod() = m |
ma.getArgument(0).(StringLiteral).getRepresentedString() = "(<script>)" and
ma.getArgument(1).(StringLiteral).getRepresentedString() = ""
)
}
/** Access to a method that passes taint from an argument. */
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
exists(Method m, int i |
m = sink.(MethodAccess).getMethod() and
taintPreservingArgumentToMethod(m, i) and
tracked = sink.(MethodAccess).getArgument(i)
)
}
/**
* Holds if `method` is a library method that return tainted data if its
* `arg`th argument is tainted.
*/
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
method instanceof StringReplaceMethod and arg = 1
or
exists(Class c | c.getQualifiedName() = "java.lang.Number" |
hasSubtype*(c, method.getDeclaringType())
) and
(
method.getName().matches("parse%") and arg = 0
or
method.getName().matches("valueOf%") and arg = 0
or
method.getName().matches("to%String") and arg = 0
)
or
method.getDeclaringType() instanceof TypeString and
method.getName() = "concat" and
arg = 0
or
(
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") or
method.getDeclaringType().hasQualifiedName("java.lang", "StringBuffer")
) and
(
method.getName() = "append" and arg = 0
or
method.getName() = "insert" and arg = 1
or
method.getName() = "replace" and arg = 2
)
or
(
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Encoder") or
method.getDeclaringType().hasQualifiedName("java.util", "Base64$Decoder")
) and
(
method.getName() = "encode" and arg = 0 and method.getNumberOfParameters() = 1
or
method.getName() = "decode" and arg = 0 and method.getNumberOfParameters() = 1
or
method.getName() = "encodeToString" and arg = 0
or
method.getName() = "wrap" and arg = 0
)
or
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
(
method.getName() = "buffer" and arg = 0
or
method.getName() = "readLines" and arg = 0
or
method.getName() = "readFully" and arg = 0 and method.getParameterType(1).hasName("int")
or
method.getName() = "toBufferedInputStream" and arg = 0
or
method.getName() = "toBufferedReader" and arg = 0
or
method.getName() = "toByteArray" and arg = 0
or
method.getName() = "toCharArray" and arg = 0
or
method.getName() = "toInputStream" and arg = 0
or
method.getName() = "toString" and arg = 0
)
or
// A URI created from a tainted string is still tainted.
method.getDeclaringType().hasQualifiedName("java.net", "URI") and
method.hasName("create") and
arg = 0
or
method.getDeclaringType().hasQualifiedName("javax.xml.transform.sax", "SAXSource") and
method.hasName("sourceToInputSource") and
arg = 0
or
exists(ProtobufParser p | method = p.getAParseFromMethod()) and
arg = 0
or
exists(ProtobufMessageLite m | method = m.getAParseFromMethod()) and
arg = 0
}
/**
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
* between arguments.
*/
private predicate argToArgStep(Expr tracked, RValue sink) {
exists(MethodAccess ma, Method method, int input, int output |
taintPreservingArgToArg(method, input, output) and
ma.getMethod() = method and
ma.getArgument(input) = tracked and
ma.getArgument(output) = sink
)
}
/**
* Holds if `method` is a library method that writes tainted data to the
* `output`th argument if the `input`th argument is tainted.
*/
private predicate taintPreservingArgToArg(Method method, int input, int output) {
method.getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils") and
(
method.hasName("copy") and input = 0 and output = 1
or
method.hasName("copyLarge") and input = 0 and output = 1
or
method.hasName("read") and input = 0 and output = 1
or
method.hasName("readFully") and
input = 0 and
output = 1 and
not method.getParameterType(1).hasName("int")
or
method.hasName("write") and input = 0 and output = 1
or
method.hasName("writeChunked") and input = 0 and output = 1
or
method.hasName("writeLines") and input = 0 and output = 2
or
method.hasName("writeLines") and input = 1 and output = 2
)
or
method.getDeclaringType().hasQualifiedName("java.lang", "System") and
method.hasName("arraycopy") and
input = 0 and
output = 2
}
/**
* Holds if `tracked` is the argument of a method that transfers taint
* from the argument to the qualifier and `sink` is the qualifier.
*/
private predicate argToQualifierStep(Expr tracked, Expr sink) {
exists(Method m, int i, MethodAccess ma |
taintPreservingArgumentToQualifier(m, i) and
ma.getMethod() = m and
tracked = ma.getArgument(i) and
sink = ma.getQualifier()
)
}
/**
* Holds if `method` is a method that transfers taint from argument to qualifier and
* `arg` is the index of the argument.
*/
private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
method.hasName("write") and
arg = 0
}
/** A comparison or equality test with a constant. */
private predicate comparisonStep(Expr tracked, Expr sink) {
exists(Expr other |
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof EqualityTest |
e = sink and
e.hasOperands(tracked, other)
)
or
exists(MethodAccess m | m.getMethod() instanceof EqualsMethod |
m = sink and
(
m.getQualifier() = tracked and m.getArgument(0) = other
or
m.getQualifier() = other and m.getArgument(0) = tracked
)
)
|
other.isCompileTimeConstant() or other instanceof NullLiteral
)
}
/** Flow through a `StringBuilder`. */
private predicate stringBuilderStep(Expr tracked, Expr sink) {
exists(StringBuilderVar sbvar, MethodAccess input, int arg |
input = sbvar.getAnInput(arg) and
tracked = input.getArgument(arg) and
sink = sbvar.getToStringCall()
)
}
/** Flow through data serialization. */
private predicate serializationStep(Expr tracked, Expr sink) {
exists(ObjectOutputStreamVar v, VariableAssign def |
def = v.getADef() and
exists(MethodAccess ma, RValue use |
ma.getArgument(0) = tracked and
ma = v.getAWriteObjectMethodAccess() and
use = ma.getQualifier() and
defUsePair(def, use)
) and
exists(RValue outputstream, ClassInstanceExpr cie |
cie = def.getSource() and
outputstream = cie.getArgument(0) and
adjacentUseUse(outputstream, sink)
)
)
}
/**
* A local variable that is assigned an `ObjectOutputStream`.
* Writing tainted data to such a stream causes the underlying
* `OutputStream` to be tainted.
*/
class ObjectOutputStreamVar extends LocalVariableDecl {
ObjectOutputStreamVar() {
exists(ClassInstanceExpr cie | cie = this.getAnAssignedValue() |
cie.getType() instanceof TypeObjectOutputStream
)
}
VariableAssign getADef() {
result.getSource().(ClassInstanceExpr).getType() instanceof TypeObjectOutputStream and
result.getDestVar() = this
}
MethodAccess getAWriteObjectMethodAccess() {
result.getQualifier() = getAnAccess() and
result.getMethod().hasName("writeObject")
}
}
private import StringBuilderVarModule
module StringBuilderVarModule {
/**
* A local variable that is initialized to a `StringBuilder`
* or `StringBuffer`. Such variables are often used to
* build up a query using string concatenation.
*/
class StringBuilderVar extends LocalVariableDecl {
StringBuilderVar() {
this.getType() instanceof TypeStringBuilder or
this.getType() instanceof TypeStringBuffer
}
/**
* Gets a call that adds something to this string builder, from the argument at the given index.
*/
MethodAccess getAnInput(int arg) {
result.getQualifier() = getAChainedReference() and
(
result.getMethod().getName() = "append" and arg = 0
or
result.getMethod().getName() = "insert" and arg = 1
or
result.getMethod().getName() = "replace" and arg = 2
)
}
/**
* Gets a call that appends something to this string builder.
*/
MethodAccess getAnAppend() {
result.getQualifier() = getAChainedReference() and
result.getMethod().getName() = "append"
}
MethodAccess getNextAppend(MethodAccess append) {
result = getAnAppend() and
append = getAnAppend() and
(
result.getQualifier() = append
or
not exists(MethodAccess chainAccess | chainAccess.getQualifier() = append) and
exists(RValue sbva1, RValue sbva2 |
adjacentUseUse(sbva1, sbva2) and
append.getQualifier() = getAChainedReference(sbva1) and
result.getQualifier() = sbva2
)
)
}
/**
* Gets a call that converts this string builder to a string.
*/
MethodAccess getToStringCall() {
result.getQualifier() = getAChainedReference() and
result.getMethod().getName() = "toString"
}
private Expr getAChainedReference(VarAccess sbva) {
// All the methods on `StringBuilder` that return the same type return
// the same object.
result = callReturningSameType+(sbva) and sbva = this.getAnAccess()
or
result = sbva and sbva = this.getAnAccess()
}
/**
* Gets an expression that refers to this `StringBuilder`, possibly after some chained calls.
*/
Expr getAChainedReference() { result = getAChainedReference(_) }
}
}
private MethodAccess callReturningSameType(Expr ref) {
ref = result.getQualifier() and
result.getMethod().getReturnType() = ref.getType()
}

View File

@@ -0,0 +1,88 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
localAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,5 @@
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
module Private {
import semmle.code.java.dataflow.DataFlow::DataFlow as DataFlow
}

View File

@@ -0,0 +1,88 @@
import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* A taint tracking configuration.
*
* A taint tracking configuration is a special dataflow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values, but are still relevant from a taint tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* Each use of the taint tracking library must define its own unique extension
* of this abstract class. A configuration defines a set of relevant sources
* (`isSource`) and sinks (`isSink`), and may additionally treat intermediate
* nodes as "sanitizers" (`isSanitizer`) as well as add custom taint flow steps
* (`isAdditionalTaintStep()`).
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
localAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

@@ -0,0 +1,5 @@
import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
module Private {
import semmle.code.java.dataflow.DataFlow2::DataFlow2 as DataFlow
}