From d388be7d3ba58ceef89ba3274b98559c08837600 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Fri, 16 Aug 2019 14:21:49 +0200 Subject: [PATCH 1/9] C++: Use pyrameterized modules for TaintTracking --- config/identical-files.json | 4 + .../code/cpp/dataflow/TaintTracking.qll | 255 +----------------- .../code/cpp/dataflow/TaintTracking2.qll | 12 + .../dataflow/internal/TaintTrackingUtil.qll | 123 +++++++++ .../tainttracking1/TaintTrackingImpl.qll | 98 +++++++ .../tainttracking1/TaintTrackingParameter.qll | 2 + .../tainttracking2/TaintTrackingImpl.qll | 98 +++++++ .../tainttracking2/TaintTrackingParameter.qll | 2 + .../dataflow/recursion/chained_use.ql | 2 + 9 files changed, 346 insertions(+), 250 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/TaintTracking2.qll create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll create mode 100644 cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll diff --git a/config/identical-files.json b/config/identical-files.json index 0e364c384c01..d4fa5c219d33 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -25,6 +25,10 @@ "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" + ], "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..bd583d5c33d3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.dataflow.DataFlow as Private +import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public 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..bd583d5c33d3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.dataflow.DataFlow as Private +import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public 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 { From b1cd64bbf48bd4a5926ddc0630b366fe6ba3c1e0 Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Fri, 16 Aug 2019 16:23:35 +0200 Subject: [PATCH 2/9] C++: Fix mismatch between taint and dataflow copy --- .../dataflow/internal/tainttracking2/TaintTrackingParameter.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index bd583d5c33d3..ea048717bd5f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -1,2 +1,2 @@ -import semmle.code.cpp.dataflow.DataFlow as Private +import semmle.code.cpp.dataflow.DataFlow2 as Private import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public From d65b09d94af72f43f4c20e0c031b7e1f1f980bfb Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Mon, 19 Aug 2019 09:19:51 +0200 Subject: [PATCH 3/9] C++: Proper fix for TaintTracking2 parameter --- .../internal/tainttracking1/TaintTrackingParameter.qll | 5 ++++- .../internal/tainttracking2/TaintTrackingParameter.qll | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) 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 index bd583d5c33d3..7fd632efb0ec 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -1,2 +1,5 @@ -import semmle.code.cpp.dataflow.DataFlow as Private 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/TaintTrackingParameter.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll index ea048717bd5f..fb4862fd06bd 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -1,2 +1,5 @@ -import semmle.code.cpp.dataflow.DataFlow2 as Private import semmle.code.cpp.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import semmle.code.cpp.dataflow.DataFlow2::DataFlow2 as DataFlow +} From aeb2323128c552d9c541730e5d5968618bafb83b Mon Sep 17 00:00:00 2001 From: Jonas Jensen Date: Tue, 20 Aug 2019 12:02:24 +0200 Subject: [PATCH 4/9] Java: Use pyrameterized modules for TaintTracking --- config/identical-files.json | 4 + .../code/java/dataflow/TaintTracking.qll | 804 +----------------- .../code/java/dataflow/TaintTracking2.qll | 12 + .../dataflow/internal/TaintTrackingUtil.qll | 635 ++++++++++++++ .../tainttracking1/TaintTrackingImpl.qll | 88 ++ .../tainttracking1/TaintTrackingParameter.qll | 5 + .../tainttracking2/TaintTrackingImpl.qll | 88 ++ .../tainttracking2/TaintTrackingParameter.qll | 5 + 8 files changed, 849 insertions(+), 792 deletions(-) create mode 100644 java/ql/src/semmle/code/java/dataflow/TaintTracking2.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingParameter.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll create mode 100644 java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingParameter.qll diff --git a/config/identical-files.json b/config/identical-files.json index d4fa5c219d33..5e28ac94a9bd 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -29,6 +29,10 @@ "cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", "cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/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/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll index 6d79359bb362..8dd3e12735a3 100644 --- a/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll +++ b/java/ql/src/semmle/code/java/dataflow/TaintTracking.qll @@ -1,804 +1,24 @@ /** * 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 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 `