diff --git a/change-notes/1.22/analysis-cpp.md b/change-notes/1.22/analysis-cpp.md index b394935a15f0..75548c0290bb 100644 --- a/change-notes/1.22/analysis-cpp.md +++ b/change-notes/1.22/analysis-cpp.md @@ -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`. diff --git a/change-notes/1.22/analysis-java.md b/change-notes/1.22/analysis-java.md index aee6bfd14c35..d37b3cda602a 100644 --- a/change-notes/1.22/analysis-java.md +++ b/change-notes/1.22/analysis-java.md @@ -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. diff --git a/config/identical-files.json b/config/identical-files.json index 0e364c384c01..0f5bd3aafb21 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -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", diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll index 224e3858a2ac..92cd085927a5 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking.qll @@ -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; } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll new file mode 100644 index 000000000000..f67a0e711002 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll @@ -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 +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll new file mode 100644 index 000000000000..fec8e44be66f --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -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) + ) + ) +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll new file mode 100644 index 000000000000..0e8da75360fc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll new file mode 100644 index 000000000000..7fd632efb0ec --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -0,0 +1,5 @@ +import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import semmle.code.cpp.dataflow.DataFlow::DataFlow as DataFlow +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll new file mode 100644 index 000000000000..0e8da75360fc --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll new file mode 100644 index 000000000000..fb4862fd06bd --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -0,0 +1,5 @@ +import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import semmle.code.cpp.dataflow.DataFlow2::DataFlow2 as DataFlow +} diff --git a/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql index aeed7825a375..ef2bead91294 100644 --- a/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql +++ b/cpp/ql/test/library-tests/dataflow/recursion/chained_use.ql @@ -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 { diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking.qll b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking.qll index e5cf80e764cf..97f5073cab0d 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking.qll @@ -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 } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking2.qll index 37a843e6df63..9ee798ed9fd5 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking2.qll @@ -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 } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking3.qll index 3e507e6b8d2f..476d7bf7dd77 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking3.qll @@ -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 } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking4.qll index 8aef491d8c00..45b8f12be5cf 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking4.qll @@ -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 } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking5.qll index 1a1bd4138b76..7a75ce532bcb 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/TaintTracking5.qll @@ -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 } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 7f67b2d2f9fb..36053eeccd61 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -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 diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll index 650a96823ab8..165172be2a6a 100755 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/TaintTrackingPublic.qll @@ -1,4 +1,4 @@ -import csharp +private import csharp private import TaintTrackingPrivate /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll new file mode 100644 index 000000000000..6bb1e17558db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll new file mode 100644 index 000000000000..275149a07d4d --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -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 +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll new file mode 100644 index 000000000000..6bb1e17558db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll new file mode 100644 index 000000000000..6a8fa23ef31c --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -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 +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll new file mode 100644 index 000000000000..6bb1e17558db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingParameter.qll new file mode 100644 index 000000000000..6c73b6ceda63 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingParameter.qll @@ -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 +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll new file mode 100644 index 000000000000..6bb1e17558db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingParameter.qll new file mode 100644 index 000000000000..e394e27a50c2 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingParameter.qll @@ -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 +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll new file mode 100644 index 000000000000..6bb1e17558db --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll @@ -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) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingParameter.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingParameter.qll new file mode 100644 index 000000000000..2668be3b376e --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingParameter.qll @@ -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 +} diff --git a/csharp/ql/test/library-tests/cil/dataflow/TaintTracking.ql b/csharp/ql/test/library-tests/cil/dataflow/TaintTracking.ql index d0dbafc06589..98427634868d 100644 --- a/csharp/ql/test/library-tests/cil/dataflow/TaintTracking.ql +++ b/csharp/ql/test/library-tests/cil/dataflow/TaintTracking.ql @@ -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" } diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.ql b/java/ql/src/Security/CWE/CWE-079/XSS.ql index 5b1bfb2ab168..009c8fa69355 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSS.ql +++ b/java/ql/src/Security/CWE/CWE-079/XSS.ql @@ -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 } diff --git a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql index d0e2a04b681c..3c6691986e16 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql +++ b/java/ql/src/Security/CWE/CWE-079/XSSLocal.ql @@ -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 } diff --git a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql index 87153e48244a..5c353958bc22 100644 --- a/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql +++ b/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql @@ -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" } diff --git a/java/ql/src/Security/CWE/CWE-611/XXE.ql b/java/ql/src/Security/CWE/CWE-611/XXE.ql index 35764f88bc8a..b572beb4d042 100644 --- a/java/ql/src/Security/CWE/CWE-611/XXE.ql +++ b/java/ql/src/Security/CWE/CWE-611/XXE.ql @@ -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 } diff --git a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll index 6d79359bb362..fa8564f182f5 100644 --- a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll +++ b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll @@ -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 - -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) - } - } - - /** - * 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 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 - } +import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule - /** 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 `