mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #12186 from aschackmull/dataflow/refactor-configuration
Data flow: Refactor configuration
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* The main data flow and taint tracking APIs have been changed. The old APIs
|
||||
remain in place for now and translate to the new through a
|
||||
backwards-compatible wrapper. If multiple configurations are in scope
|
||||
simultaneously, then this may affect results slightly. The new API is quite
|
||||
similar to the old, but makes use of a configuration module instead of a
|
||||
configuration class.
|
||||
@@ -6,5 +6,6 @@
|
||||
import java
|
||||
|
||||
module DataFlow {
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl
|
||||
import semmle.code.java.dataflow.internal.DataFlow
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl1
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ import semmle.code.java.dataflow.DataFlow2
|
||||
import semmle.code.java.dataflow.internal.TaintTrackingUtil::StringBuilderVarModule
|
||||
|
||||
module TaintTracking {
|
||||
import semmle.code.java.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.java.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
245
java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll
Normal file
245
java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Make` and `MakeWithState` modules.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
private import DataFlowImpl
|
||||
|
||||
/** An input configuration for data flow. */
|
||||
signature module ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
default predicate isBarrier(Node node) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
default predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
default predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
default FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (as it is in a `path-problem` query).
|
||||
*/
|
||||
default predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/** An input configuration for data flow using flow state. */
|
||||
signature module StateConfigSig {
|
||||
bindingset[this]
|
||||
class FlowState;
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
default predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state);
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
default predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
default predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
default FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (as it is in a `path-problem` query).
|
||||
*/
|
||||
default predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
signature int explorationLimitSig();
|
||||
|
||||
/**
|
||||
* The output of a data flow computation.
|
||||
*/
|
||||
signature module DataFlowSig {
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks) and an access path.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode;
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a standard data flow computation.
|
||||
*/
|
||||
module Make<ConfigSig Config> implements DataFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a data flow computation using flow state.
|
||||
*/
|
||||
module MakeWithState<StateConfigSig Config> implements DataFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
396
java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll
Normal file
396
java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll
Normal file
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Make` and `MakeWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
import I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
hasFlowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,18 @@ private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
/** A state value to track during data flow. */
|
||||
class FlowState = string;
|
||||
/** Provides `FlowState = string`. */
|
||||
module FlowStateString {
|
||||
/** A state value to track during data flow. */
|
||||
class FlowState = string;
|
||||
|
||||
/**
|
||||
* The default state, which is used when the state is unspecified for a source
|
||||
* or a sink.
|
||||
*/
|
||||
class FlowStateEmpty extends FlowState {
|
||||
FlowStateEmpty() { this = "" }
|
||||
/**
|
||||
* The default state, which is used when the state is unspecified for a source
|
||||
* or a sink.
|
||||
*/
|
||||
class FlowStateEmpty extends FlowState {
|
||||
FlowStateEmpty() { this = "" }
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TFlowFeature =
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
Config::isBarrier(node) or defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
Config::isAdditionalFlowStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
Config::allowImplicitRead(node, c)
|
||||
or
|
||||
(
|
||||
Config::isSink(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a standard taint tracking computation.
|
||||
*/
|
||||
module Make<DataFlow::ConfigSig Config> implements DataFlow::DataFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import DataFlowInternal::DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a taint tracking computation using flow state.
|
||||
*/
|
||||
module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::DataFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
@@ -2,4 +2,5 @@ import semmle.code.java.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import semmle.code.java.dataflow.DataFlow::DataFlow as DataFlow
|
||||
import semmle.code.java.dataflow.internal.DataFlowImpl as DataFlowInternal
|
||||
}
|
||||
|
||||
@@ -47,6 +47,20 @@ private predicate defaultSource(DataFlow::Node src) {
|
||||
src.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"]
|
||||
}
|
||||
|
||||
module DefaultFlowConf implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { defaultSource(n) }
|
||||
|
||||
predicate isSink(DataFlow::Node n) {
|
||||
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = 1000 }
|
||||
}
|
||||
|
||||
private module DefaultValueFlow = DataFlow::Make<DefaultFlowConf>;
|
||||
|
||||
private module DefaultTaintFlow = TaintTracking::Make<DefaultFlowConf>;
|
||||
|
||||
class DefaultValueFlowConf extends DataFlow::Configuration {
|
||||
DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" }
|
||||
|
||||
@@ -76,6 +90,8 @@ private string getSourceArgString(DataFlow::Node src) {
|
||||
src.asExpr().(MethodAccess).getAnArgument().(StringLiteral).getValue() = result
|
||||
}
|
||||
|
||||
abstract class EnableLegacyConfiguration extends Unit { }
|
||||
|
||||
class InlineFlowTest extends InlineExpectationsTest {
|
||||
InlineFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
@@ -83,7 +99,7 @@ class InlineFlowTest extends InlineExpectationsTest {
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasValueFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink | getValueFlowConfig().hasFlow(src, sink) |
|
||||
exists(DataFlow::Node src, DataFlow::Node sink | hasValueFlow(src, sink) |
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = ""
|
||||
@@ -91,7 +107,7 @@ class InlineFlowTest extends InlineExpectationsTest {
|
||||
or
|
||||
tag = "hasTaintFlow" and
|
||||
exists(DataFlow::Node src, DataFlow::Node sink |
|
||||
getTaintFlowConfig().hasFlow(src, sink) and not getValueFlowConfig().hasFlow(src, sink)
|
||||
hasTaintFlow(src, sink) and not hasValueFlow(src, sink)
|
||||
|
|
||||
sink.getLocation() = location and
|
||||
element = sink.toString() and
|
||||
@@ -99,6 +115,18 @@ class InlineFlowTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) {
|
||||
if exists(EnableLegacyConfiguration e)
|
||||
then getValueFlowConfig().hasFlow(src, sink)
|
||||
else DefaultValueFlow::hasFlow(src, sink)
|
||||
}
|
||||
|
||||
predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) {
|
||||
if exists(EnableLegacyConfiguration e)
|
||||
then getTaintFlowConfig().hasFlow(src, sink)
|
||||
else DefaultTaintFlow::hasFlow(src, sink)
|
||||
}
|
||||
|
||||
DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) }
|
||||
|
||||
DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) }
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() { none() }
|
||||
}
|
||||
|
||||
@@ -42,31 +42,31 @@ public class Test {
|
||||
public static void test1() {
|
||||
Test t = new Test();
|
||||
t.fluentNoop().fluentSet(source()).fluentNoop();
|
||||
sink(t.get()); // $hasTaintFlow
|
||||
sink(t.get()); // $hasValueFlow
|
||||
}
|
||||
|
||||
public static void test2() {
|
||||
Test t = new Test();
|
||||
Test.identity(t).fluentNoop().fluentSet(source()).fluentNoop();
|
||||
sink(t.get()); // $hasTaintFlow
|
||||
sink(t.get()); // $hasValueFlow
|
||||
}
|
||||
|
||||
public static void test3() {
|
||||
Test t = new Test();
|
||||
t.indirectlyFluentNoop().fluentSet(source()).fluentNoop();
|
||||
sink(t.get()); // $hasTaintFlow
|
||||
sink(t.get()); // $hasValueFlow
|
||||
}
|
||||
|
||||
public static void testModel1() {
|
||||
Test t = new Test();
|
||||
t.indirectlyFluentNoop().modelledFluentMethod().fluentSet(source()).fluentNoop();
|
||||
sink(t.get()); // $hasTaintFlow
|
||||
sink(t.get()); // $hasValueFlow
|
||||
}
|
||||
|
||||
public static void testModel2() {
|
||||
Test t = new Test();
|
||||
Test.modelledIdentity(t).indirectlyFluentNoop().modelledFluentMethod().fluentSet(source()).fluentNoop();
|
||||
sink(t.get()); // $hasTaintFlow
|
||||
sink(t.get()); // $hasValueFlow
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,3 @@ class IdentityModel extends ValuePreservingMethod {
|
||||
|
||||
override predicate returnsValue(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getValueFlowConfig() { none() }
|
||||
}
|
||||
|
||||
@@ -14,54 +14,46 @@ predicate sink0(Node n) {
|
||||
)
|
||||
}
|
||||
|
||||
class Conf1 extends Configuration {
|
||||
Conf1() { this = "inoutbarriers1" }
|
||||
module Conf1 implements ConfigSig {
|
||||
predicate isSource(Node n) { src0(n) }
|
||||
|
||||
override predicate isSource(Node n) { src0(n) }
|
||||
|
||||
override predicate isSink(Node n) { sink0(n) }
|
||||
predicate isSink(Node n) { sink0(n) }
|
||||
}
|
||||
|
||||
class Conf2 extends Configuration {
|
||||
Conf2() { this = "inoutbarriers2" }
|
||||
module Conf2 implements ConfigSig {
|
||||
predicate isSource(Node n) { src0(n) }
|
||||
|
||||
override predicate isSource(Node n) { src0(n) }
|
||||
predicate isSink(Node n) { sink0(n) }
|
||||
|
||||
override predicate isSink(Node n) { sink0(n) }
|
||||
|
||||
override predicate isBarrierIn(Node n) { src0(n) }
|
||||
predicate isBarrierIn(Node n) { src0(n) }
|
||||
}
|
||||
|
||||
class Conf3 extends Configuration {
|
||||
Conf3() { this = "inoutbarriers3" }
|
||||
module Conf3 implements ConfigSig {
|
||||
predicate isSource(Node n) { src0(n) }
|
||||
|
||||
override predicate isSource(Node n) { src0(n) }
|
||||
predicate isSink(Node n) { sink0(n) }
|
||||
|
||||
override predicate isSink(Node n) { sink0(n) }
|
||||
|
||||
override predicate isBarrierOut(Node n) { sink0(n) }
|
||||
predicate isBarrierOut(Node n) { sink0(n) }
|
||||
}
|
||||
|
||||
class Conf4 extends Configuration {
|
||||
Conf4() { this = "inoutbarriers4" }
|
||||
module Conf4 implements ConfigSig {
|
||||
predicate isSource(Node n) { src0(n) }
|
||||
|
||||
override predicate isSource(Node n) { src0(n) }
|
||||
predicate isSink(Node n) { sink0(n) }
|
||||
|
||||
override predicate isSink(Node n) { sink0(n) }
|
||||
predicate isBarrierIn(Node n) { src0(n) }
|
||||
|
||||
override predicate isBarrierIn(Node n) { src0(n) }
|
||||
|
||||
override predicate isBarrierOut(Node n) { sink0(n) }
|
||||
predicate isBarrierOut(Node n) { sink0(n) }
|
||||
}
|
||||
|
||||
predicate flow(Node src, Node sink, string s) {
|
||||
any(Conf1 c).hasFlow(src, sink) and s = "nobarrier"
|
||||
Make<Conf1>::hasFlow(src, sink) and s = "nobarrier"
|
||||
or
|
||||
any(Conf2 c).hasFlow(src, sink) and s = "srcbarrier"
|
||||
Make<Conf2>::hasFlow(src, sink) and s = "srcbarrier"
|
||||
or
|
||||
any(Conf3 c).hasFlow(src, sink) and s = "sinkbarrier"
|
||||
Make<Conf3>::hasFlow(src, sink) and s = "sinkbarrier"
|
||||
or
|
||||
any(Conf4 c).hasFlow(src, sink) and s = "both"
|
||||
Make<Conf4>::hasFlow(src, sink) and s = "both"
|
||||
}
|
||||
|
||||
from Node src, Node sink, string s
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import DataFlow
|
||||
import PartialPathGraph
|
||||
|
||||
class Conf extends Configuration {
|
||||
Conf() { this = "partial flow" }
|
||||
module Config implements ConfigSig {
|
||||
predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("src") }
|
||||
|
||||
override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("src") }
|
||||
|
||||
override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
|
||||
|
||||
override int explorationLimit() { result = 10 }
|
||||
predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
|
||||
}
|
||||
|
||||
from PartialPathNode n, int dist
|
||||
where any(Conf c).hasPartialFlow(_, n, dist)
|
||||
int explorationLimit() { result = 10 }
|
||||
|
||||
module PartialFlow = Make<Config>::FlowExploration<explorationLimit/0>;
|
||||
|
||||
import PartialFlow::PartialPathGraph
|
||||
|
||||
from PartialFlow::PartialPathNode n, int dist
|
||||
where PartialFlow::hasPartialFlow(_, n, dist)
|
||||
select dist, n
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import DataFlow
|
||||
import PartialPathGraph
|
||||
|
||||
class Conf extends Configuration {
|
||||
Conf() { this = "partial flow" }
|
||||
module Config implements ConfigSig {
|
||||
predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("src") }
|
||||
|
||||
override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("src") }
|
||||
|
||||
override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
|
||||
|
||||
override int explorationLimit() { result = 10 }
|
||||
predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") }
|
||||
}
|
||||
|
||||
from PartialPathNode n, int dist
|
||||
where any(Conf c).hasPartialFlowRev(n, _, dist)
|
||||
int explorationLimit() { result = 10 }
|
||||
|
||||
module PartialFlow = Make<Config>::FlowExploration<explorationLimit/0>;
|
||||
|
||||
import PartialFlow::PartialPathGraph
|
||||
|
||||
from PartialFlow::PartialPathNode n, int dist
|
||||
where PartialFlow::hasPartialFlowRev(n, _, dist)
|
||||
select dist, n
|
||||
|
||||
@@ -39,22 +39,26 @@ predicate step(Node n1, Node n2, string s1, string s2) {
|
||||
|
||||
predicate checkNode(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("check") }
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:state" }
|
||||
module Conf implements DataFlow::StateConfigSig {
|
||||
class FlowState = string;
|
||||
|
||||
override predicate isSource(Node n, FlowState s) { src(n, s) }
|
||||
predicate isSource(Node n, FlowState s) { src(n, s) }
|
||||
|
||||
override predicate isSink(Node n, FlowState s) { sink(n, s) }
|
||||
predicate isSink(Node n, FlowState s) { sink(n, s) }
|
||||
|
||||
override predicate isSanitizer(Node n, FlowState s) { bar(n, s) }
|
||||
predicate isBarrier(Node n, FlowState s) { bar(n, s) }
|
||||
|
||||
override predicate isAdditionalTaintStep(Node n1, FlowState s1, Node n2, FlowState s2) {
|
||||
predicate isAdditionalFlowStep(Node n1, FlowState s1, Node n2, FlowState s2) {
|
||||
step(n1, n2, s1, s2)
|
||||
}
|
||||
|
||||
override int explorationLimit() { result = 0 }
|
||||
}
|
||||
|
||||
int explorationLimit() { result = 0 }
|
||||
|
||||
module Flow = TaintTracking::MakeWithState<Conf>;
|
||||
|
||||
module PartialFlow = Flow::FlowExploration<explorationLimit/0>;
|
||||
|
||||
class HasFlowTest extends InlineExpectationsTest {
|
||||
HasFlowTest() { this = "HasFlowTest" }
|
||||
|
||||
@@ -62,16 +66,16 @@ class HasFlowTest extends InlineExpectationsTest {
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "flow" and
|
||||
exists(PathNode src, PathNode sink, Conf conf |
|
||||
conf.hasFlowPath(src, sink) and
|
||||
exists(Flow::PathNode src, Flow::PathNode sink |
|
||||
Flow::hasFlowPath(src, sink) and
|
||||
sink.getNode().getLocation() = location and
|
||||
element = sink.toString() and
|
||||
value = src.getState()
|
||||
)
|
||||
or
|
||||
tag = "pFwd" and
|
||||
exists(PartialPathNode src, PartialPathNode node, Conf conf |
|
||||
conf.hasPartialFlow(src, node, _) and
|
||||
exists(PartialFlow::PartialPathNode src, PartialFlow::PartialPathNode node |
|
||||
PartialFlow::hasPartialFlow(src, node, _) and
|
||||
checkNode(node.getNode()) and
|
||||
node.getNode().getLocation() = location and
|
||||
element = node.toString() and
|
||||
@@ -79,8 +83,8 @@ class HasFlowTest extends InlineExpectationsTest {
|
||||
)
|
||||
or
|
||||
tag = "pRev" and
|
||||
exists(PartialPathNode node, PartialPathNode sink, Conf conf |
|
||||
conf.hasPartialFlowRev(node, sink, _) and
|
||||
exists(PartialFlow::PartialPathNode node, PartialFlow::PartialPathNode sink |
|
||||
PartialFlow::hasPartialFlowRev(node, sink, _) and
|
||||
checkNode(node.getNode()) and
|
||||
node.getNode().getLocation() = location and
|
||||
element = node.toString() and
|
||||
|
||||
@@ -6,42 +6,44 @@ class A {
|
||||
return "tainted";
|
||||
}
|
||||
|
||||
public static void sink(Object o) { }
|
||||
|
||||
public static void test1() {
|
||||
String bad = source(); // $ hasTaintFlow
|
||||
String bad = source();
|
||||
String good = "hi";
|
||||
|
||||
bad.formatted(good); // $ hasTaintFlow
|
||||
good.formatted("a", bad, "b", good); // $ hasTaintFlow
|
||||
String.format("%s%s", bad, good); // $ hasTaintFlow
|
||||
String.format("%s", good);
|
||||
String.format("%s %s %s %s %s %s %s %s %s %s ", "a", "a", "a", "a", "a", "a", "a", "a", "a", bad); // $ hasTaintFlow
|
||||
sink(bad.formatted(good)); // $ hasTaintFlow
|
||||
sink(good.formatted("a", bad, "b", good)); // $ hasTaintFlow
|
||||
sink(String.format("%s%s", bad, good)); // $ hasTaintFlow
|
||||
sink(String.format("%s", good));
|
||||
sink(String.format("%s %s %s %s %s %s %s %s %s %s ", "a", "a", "a", "a", "a", "a", "a", "a", "a", bad)); // $ hasTaintFlow
|
||||
}
|
||||
|
||||
public static void test2() {
|
||||
String bad = source(); // $ hasTaintFlow
|
||||
String bad = source();
|
||||
Formatter f = new Formatter();
|
||||
|
||||
f.toString();
|
||||
f.format("%s", bad); // $ hasTaintFlow
|
||||
f.toString(); // $ hasTaintFlow
|
||||
sink(f.toString());
|
||||
sink(f.format("%s", bad)); // $ hasTaintFlow
|
||||
sink(f.toString()); // $ hasTaintFlow
|
||||
}
|
||||
|
||||
public static void test3() {
|
||||
String bad = source(); // $ hasTaintFlow
|
||||
String bad = source();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter f = new Formatter(sb);
|
||||
|
||||
sb.toString(); // $ hasTaintFlow false positive
|
||||
f.format("%s", bad); // $ hasTaintFlow
|
||||
sb.toString(); // $ hasTaintFlow
|
||||
sink(sb.toString()); // $ SPURIOUS: hasTaintFlow
|
||||
sink(f.format("%s", bad)); // $ hasTaintFlow
|
||||
sink(sb.toString()); // $ hasTaintFlow
|
||||
}
|
||||
|
||||
public static void test4() {
|
||||
String bad = source(); // $ hasTaintFlow
|
||||
String bad = source();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(bad); // $ hasTaintFlow
|
||||
sink(sb.append(bad)); // $ hasTaintFlow
|
||||
|
||||
new Formatter(sb).format("ok").toString(); // $ hasTaintFlow
|
||||
sink(new Formatter(sb).format("ok").toString()); // $ hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class TaintFlowConf extends DefaultTaintFlowConf {
|
||||
override predicate isSink(DataFlow::Node n) { n instanceof DataFlow::ExprNode }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class TaintFlowConf extends DefaultTaintFlowConf {
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
super.isSource(n)
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class ProviderTaintFlowConf extends DefaultTaintFlowConf {
|
||||
override predicate isSource(DataFlow::Node n) { n instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "test:AndroidExternalFlowConf" }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class SliceValueFlowConf extends DefaultValueFlowConf {
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
super.isSource(source) or source instanceof RemoteFlowSource
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class SourceValueFlowConf extends DefaultValueFlowConf {
|
||||
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import semmle.code.java.security.XSS
|
||||
import semmle.code.java.security.UrlRedirect
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:frameworks:apache-http" }
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class FlowConf extends DefaultValueFlowConf {
|
||||
override predicate isSink(DataFlow::Node n) { super.isSink(n) or sinkNode(n, "open-url") }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class Conf extends TaintTracking::Configuration {
|
||||
Conf() { this = "qltest:frameworks:rabbitmq" }
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class FlowConf extends DefaultValueFlowConf {
|
||||
override predicate isSink(DataFlow::Node n) { super.isSink(n) or sinkNode(n, "open-url") }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class ValueFlowConf extends DataFlow::Configuration {
|
||||
ValueFlowConf() { this = "ValueFlowConf" }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.security.PathSanitizer
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class PathSanitizerConf extends DefaultTaintFlowConf {
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer instanceof PathInjectionSanitizer
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.PartialPathTraversalQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class TestRemoteSource extends RemoteFlowSource {
|
||||
TestRemoteSource() { this.asParameter().hasName(["dir", "path"]) }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.security.LogInjectionQuery
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
private class TestSource extends RemoteFlowSource {
|
||||
TestSource() { this.asExpr().(MethodAccess).getMethod().hasName("source") }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.IntentUriPermissionManipulationQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class IntentUriPermissionManipulationTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getValueFlowConfig() { none() }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.security.InsecureTrustManagerQuery
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class InsecureTrustManagerTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getValueFlowConfig() {
|
||||
result = any(InsecureTrustManagerConfiguration c)
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.UnsafeContentUriResolutionQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class Test extends InlineFlowTest {
|
||||
override DataFlow::Configuration getValueFlowConfig() { none() }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import semmle.code.java.security.FragmentInjectionQuery
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class Test extends InlineFlowTest {
|
||||
override DataFlow::Configuration getValueFlowConfig() { none() }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.WebviewDubuggingEnabledQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() { none() }
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.SensitiveLoggingQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() {
|
||||
result instanceof SensitiveLoggerConfiguration
|
||||
|
||||
@@ -3,6 +3,10 @@ import TestUtilities.InlineExpectationsTest
|
||||
import TestUtilities.InlineFlowTest
|
||||
import semmle.code.java.security.RsaWithoutOaepQuery
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() { result instanceof RsaWithoutOaepConfig }
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ import semmle.code.java.security.AndroidSensitiveCommunicationQuery
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
class EnableLegacy extends EnableLegacyConfiguration {
|
||||
EnableLegacy() { exists(this) }
|
||||
}
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() {
|
||||
result = any(SensitiveCommunicationConfig c)
|
||||
|
||||
Reference in New Issue
Block a user