Update taint-tracking libraries.

This brings `TaintTrackingImpl.qll` up-to-date with the other languages as of https://github.com/Semmle/ql/pull/2480.
This commit is contained in:
Max Schaefer
2019-12-06 14:07:35 +00:00
parent 53f5e13af1
commit 62a50bac2a
11 changed files with 250 additions and 153 deletions

View File

@@ -3,152 +3,8 @@
* global (inter-procedural) taint-tracking analyses.
*/
import go
import semmle.go.dataflow.DataFlow
module TaintTracking {
private import semmle.go.dataflow.internal.DataFlowPrivate
private import semmle.go.dataflow.FunctionInputsAndOutputs
/**
* Holds if taint propagates 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 taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Ordinary data flow
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
taintStep(nodeFrom, nodeTo)
}
/**
* A taint tracking configuration.
*
* 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.)
*
* 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
taintStep(pred, succ)
}
/**
* 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 flows from `pred` to `succ` via a reference or dereference. */
predicate referenceStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(AddressExpr).getOperand() = pred.asExpr()
or
succ.asExpr().(StarExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via a field read. */
predicate fieldReadStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.(DataFlow::FieldReadNode).getBase() = pred
}
/** Holds if taint flows from `pred` to `succ` via an array index operation. */
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(IndexExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via an extract tuple operation. */
predicate tupleStep(DataFlow::Node pred, DataFlow::Node succ) {
succ = DataFlow::extractTupleElement(pred, _)
}
/** Holds if taint flows from `pred` to `succ` via string concatenation. */
predicate stringConcatStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::BinaryOperationNode conc | conc.getOperator() = "+" |
succ = conc and conc.getAnOperand() = pred
)
}
/** Holds if taint flows from `pred` to `succ` via a slice operation. */
predicate sliceStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(SliceExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via a function model. */
predicate functionModelStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(FunctionModel m, DataFlow::CallNode c, FunctionInput inp, FunctionOutput outp |
c = m.getACall() and
m.hasTaintFlow(inp, outp) and
pred = inp.getNode(c) and
succ = outp.getNode(c)
)
}
/**
* Holds if taint flows from `pred` to `succ` in one step.
*/
predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) {
referenceStep(pred, succ) or
fieldReadStep(pred, succ) or
arrayStep(pred, succ) or
tupleStep(pred, succ) or
stringConcatStep(pred, succ) or
sliceStep(pred, succ) or
functionModelStep(pred, succ)
}
/**
* A model of a function specifying that the function propagates taint from
* a parameter or qualifier to a result.
*/
abstract class FunctionModel extends Function {
abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output);
}
import semmle.go.dataflow.internal.tainttracking1.TaintTrackingImpl
}

View File

@@ -0,0 +1,124 @@
private import go
/**
* 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 from `src` to `sink` in zero or more
* local (intra-procedural) steps.
*/
predicate localExprTaint(Expr src, Expr sink) {
localTaint(DataFlow::exprNode(src), DataFlow::exprNode(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)
}
private newtype TUnit = TMkUnit()
class Unit extends TUnit {
string toString() { result = "unit" }
}
/**
* A unit class for adding additional taint steps.
*
* Extend this class to add additional taint steps that should apply to all
* taint configurations.
*/
class AdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for all configurations.
*/
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
}
/**
* Holds if the additional step from `pred` to `succ` should be included in all
* global taint flow configurations.
*/
predicate localAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
referenceStep(pred, succ) or
fieldReadStep(pred, succ) or
arrayStep(pred, succ) or
tupleStep(pred, succ) or
stringConcatStep(pred, succ) or
sliceStep(pred, succ) or
functionModelStep(pred, succ) or
any(AdditionalTaintStep a).step(pred, succ)
}
/** Holds if taint flows from `pred` to `succ` via a reference or dereference. */
predicate referenceStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(AddressExpr).getOperand() = pred.asExpr()
or
succ.asExpr().(StarExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via a field read. */
predicate fieldReadStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.(DataFlow::FieldReadNode).getBase() = pred
}
/** Holds if taint flows from `pred` to `succ` via an array index operation. */
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(IndexExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via an extract tuple operation. */
predicate tupleStep(DataFlow::Node pred, DataFlow::Node succ) {
succ = DataFlow::extractTupleElement(pred, _)
}
/** Holds if taint flows from `pred` to `succ` via string concatenation. */
predicate stringConcatStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::BinaryOperationNode conc | conc.getOperator() = "+" |
succ = conc and conc.getAnOperand() = pred
)
}
/** Holds if taint flows from `pred` to `succ` via a slice operation. */
predicate sliceStep(DataFlow::Node pred, DataFlow::Node succ) {
succ.asExpr().(SliceExpr).getBase() = pred.asExpr()
}
/** Holds if taint flows from `pred` to `succ` via a function model. */
predicate functionModelStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(FunctionModel m, DataFlow::CallNode c, FunctionInput inp, FunctionOutput outp |
c = m.getACall() and
m.hasTaintFlow(inp, outp) and
pred = inp.getNode(c) and
succ = outp.getNode(c)
)
}
/**
* A model of a function specifying that the function propagates taint from
* a parameter or qualifier to a result.
*/
abstract class FunctionModel extends Function {
abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output);
}
/**
* Holds if the additional step from `src` to `sink` should be included in all
* global taint flow configurations.
*/
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
localAdditionalTaintStep(src, sink)
}
/**
* Holds if `node` should be a barrier in all global taint flow configurations
* but not in local taint.
*/
predicate defaultTaintBarrier(DataFlow::Node node) { none() }

View File

@@ -0,0 +1,112 @@
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 `isSanitizerIn`.
* // Optionally override `isSanitizerOut`.
* // Optionally override `isSanitizerGuard`.
* // 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
* another `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
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }
/**
* Holds if `source` is a relevant taint source.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);
/**
* Holds if `sink` is a relevant taint sink.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
/** Holds if data flow out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
isAdditionalTaintStep(node1, node2) or
defaultAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may flow from `source` to `sink` for this configuration.
*/
// overridden to provide taint-tracking specific qldoc
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
super.hasFlow(source, sink)
}
}

View File

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

View File

@@ -35,7 +35,7 @@ module CleartextLogging {
exists(Write write | write.writesField(trg.getASuccessor*(), _, src))
or
// taint steps that do not include flow through fields
TaintTracking::taintStep(src, trg) and not TaintTracking::fieldReadStep(src, trg)
TaintTracking::localTaintStep(src, trg) and not TaintTracking::fieldReadStep(src, trg)
}
}
}

View File

@@ -27,6 +27,6 @@ module CommandInjection {
node instanceof Sanitizer
}
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
}
}

View File

@@ -36,7 +36,7 @@ module OpenUrlRedirect {
)
or
// taint steps that do not include flow through fields
TaintTracking::taintStep(pred, succ) and not TaintTracking::fieldReadStep(pred, succ)
TaintTracking::localTaintStep(pred, succ) and not TaintTracking::fieldReadStep(pred, succ)
}
override predicate isBarrierOut(DataFlow::Node node) { hostnameSanitizingPrefixEdge(node, _) }

View File

@@ -23,6 +23,6 @@ module ReflectedXss {
node instanceof Sanitizer
}
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
}
}

View File

@@ -25,6 +25,6 @@ module SqlInjection {
node instanceof Sanitizer
}
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
}
}

View File

@@ -25,6 +25,6 @@ module TaintedPath {
node instanceof Sanitizer
}
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
}
}

View File

@@ -25,6 +25,6 @@ module ZipSlip {
node instanceof Sanitizer
}
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { guard instanceof SanitizerGuard }
}
}