mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #14274 from alexrford/rb/dataflow-tidy
Ruby: Minor fixes for dataflow queries
This commit is contained in:
@@ -1,402 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` 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
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* 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) { none() }
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
|
||||
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) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -1,402 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Global` and `GlobalWithState` 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
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* 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) { none() }
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* 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) }
|
||||
}
|
||||
|
||||
module PathGraph = I::PathGraph;
|
||||
|
||||
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) {
|
||||
I::flowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -9,13 +9,19 @@ private import codeql.ruby.TaintTracking
|
||||
private import codeql.ruby.ApiGraphs
|
||||
import UnicodeBypassValidationCustomizations::UnicodeBypassValidation
|
||||
|
||||
/** A state signifying that a logical validation has not been performed. */
|
||||
class PreValidation extends DataFlow::FlowState {
|
||||
/**
|
||||
* A state signifying that a logical validation has not been performed.
|
||||
* DEPRECATED: Use `PreValidationState()`
|
||||
*/
|
||||
deprecated class PreValidation extends DataFlow::FlowState {
|
||||
PreValidation() { this = "PreValidation" }
|
||||
}
|
||||
|
||||
/** A state signifying that a logical validation has been performed. */
|
||||
class PostValidation extends DataFlow::FlowState {
|
||||
/**
|
||||
* A state signifying that a logical validation has been performed.
|
||||
* DEPRECATED: Use `PostValidationState()`
|
||||
*/
|
||||
deprecated class PostValidation extends DataFlow::FlowState {
|
||||
PostValidation() { this = "PostValidation" }
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `Excon`.
|
||||
@@ -72,8 +71,7 @@ class ExconHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(ExconDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
ExconDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
or
|
||||
// We set `Excon.defaults[:ssl_verify_peer]` or `Excon.ssl_verify_peer` = false`
|
||||
@@ -114,17 +112,13 @@ class ExconHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for Excon. */
|
||||
private class ExconDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
ExconDisablesCertificateValidationConfiguration() {
|
||||
this = "ExconDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
private module ExconDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.asExpr().getExpr().(BooleanLiteral).isFalse() }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().getExpr().(BooleanLiteral).isFalse()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(ExconHttpRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module ExconDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<ExconDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -7,7 +7,6 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `Faraday`.
|
||||
@@ -78,8 +77,7 @@ class FaradayHttpRequest extends Http::Client::Request::Range, DataFlow::CallNod
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(FaradayDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
FaradayDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue(_)
|
||||
}
|
||||
|
||||
@@ -87,15 +85,10 @@ class FaradayHttpRequest extends Http::Client::Request::Range, DataFlow::CallNod
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for Faraday. */
|
||||
private class FaradayDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
FaradayDisablesCertificateValidationConfiguration() {
|
||||
this = "FaradayDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
private module FaradayDisablesCertificateValidationConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = string;
|
||||
|
||||
override predicate isSource(
|
||||
DataFlow::Node source, DataFlowImplForHttpClientLibraries::FlowState state
|
||||
) {
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
source.asExpr().getExpr().(BooleanLiteral).isFalse() and
|
||||
state = "verify"
|
||||
or
|
||||
@@ -103,7 +96,10 @@ private class FaradayDisablesCertificateValidationConfiguration extends DataFlow
|
||||
state = "verify_mode"
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlowImplForHttpClientLibraries::FlowState state) {
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
sink = any(FaradayHttpRequest req).getCertificateValidationControllingValue(state)
|
||||
}
|
||||
}
|
||||
|
||||
private module FaradayDisablesCertificateValidationFlow =
|
||||
DataFlow::GlobalWithState<FaradayDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -6,7 +6,6 @@ private import codeql.ruby.AST
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `HTTPClient`.
|
||||
@@ -65,8 +64,7 @@ class HttpClientRequest extends Http::Client::Request::Range, DataFlow::CallNode
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(HttpClientDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
HttpClientDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -74,17 +72,15 @@ class HttpClientRequest extends Http::Client::Request::Range, DataFlow::CallNode
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for HttpClient. */
|
||||
private class HttpClientDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
HttpClientDisablesCertificateValidationConfiguration() {
|
||||
this = "HttpClientDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module HttpClientDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").asSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(HttpClientRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module HttpClientDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<HttpClientDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -7,7 +7,6 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `HTTParty`.
|
||||
@@ -57,8 +56,7 @@ class HttpartyRequest extends Http::Client::Request::Range, DataFlow::CallNode {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(HttpartyDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
HttpartyDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -66,17 +64,13 @@ class HttpartyRequest extends Http::Client::Request::Range, DataFlow::CallNode {
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for Httparty. */
|
||||
private class HttpartyDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
HttpartyDisablesCertificateValidationConfiguration() {
|
||||
this = "HttpartyDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
private module HttpartyDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.asExpr().getExpr().(BooleanLiteral).isFalse() }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().getExpr().(BooleanLiteral).isFalse()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(HttpartyRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module HttpartyDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<HttpartyDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -8,7 +8,6 @@ private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPublic
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A `Net::HTTP` call which initiates an HTTP request.
|
||||
@@ -88,8 +87,7 @@ class NetHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(NetHttpDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
NetHttpDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -97,17 +95,15 @@ class NetHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode {
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for NetHttp. */
|
||||
private class NetHttpDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
NetHttpDisablesCertificateValidationConfiguration() {
|
||||
this = "NetHttpDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module NetHttpDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").asSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(NetHttpRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module NetHttpDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<NetHttpDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -8,7 +8,6 @@ private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.frameworks.Core
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `OpenURI` via `URI.open` or
|
||||
@@ -46,8 +45,7 @@ class OpenUriRequest extends Http::Client::Request::Range, DataFlow::CallNode {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(OpenUriDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
OpenUriDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -94,8 +92,7 @@ class OpenUriKernelOpenRequest extends Http::Client::Request::Range, DataFlow::C
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(OpenUriDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
OpenUriDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -103,19 +100,17 @@ class OpenUriKernelOpenRequest extends Http::Client::Request::Range, DataFlow::C
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for OpenURI. */
|
||||
private class OpenUriDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
OpenUriDisablesCertificateValidationConfiguration() {
|
||||
this = "OpenUriDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module OpenUriDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").asSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(OpenUriRequest req).getCertificateValidationControllingValue()
|
||||
or
|
||||
sink = any(OpenUriKernelOpenRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module OpenUriDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<OpenUriDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -7,7 +7,6 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `RestClient`.
|
||||
@@ -58,8 +57,7 @@ class RestClientHttpRequest extends Http::Client::Request::Range, DataFlow::Call
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(RestClientDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
RestClientDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -67,17 +65,15 @@ class RestClientHttpRequest extends Http::Client::Request::Range, DataFlow::Call
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for RestClient. */
|
||||
private class RestClientDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
RestClientDisablesCertificateValidationConfiguration() {
|
||||
this = "RestClientDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module RestClientDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").asSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(RestClientHttpRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module RestClientDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<RestClientDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -7,7 +7,6 @@ private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries as DataFlowImplForHttpClientLibraries
|
||||
|
||||
/**
|
||||
* A call that makes an HTTP request using `Typhoeus`.
|
||||
@@ -38,8 +37,7 @@ class TyphoeusHttpRequest extends Http::Client::Request::Range, DataFlow::CallNo
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
any(TyphoeusDisablesCertificateValidationConfiguration config)
|
||||
.hasFlow(argumentOrigin, disablingNode) and
|
||||
TyphoeusDisablesCertificateValidationFlow::flow(argumentOrigin, disablingNode) and
|
||||
disablingNode = this.getCertificateValidationControllingValue()
|
||||
}
|
||||
|
||||
@@ -47,17 +45,13 @@ class TyphoeusHttpRequest extends Http::Client::Request::Range, DataFlow::CallNo
|
||||
}
|
||||
|
||||
/** A configuration to track values that can disable certificate validation for Typhoeus. */
|
||||
private class TyphoeusDisablesCertificateValidationConfiguration extends DataFlowImplForHttpClientLibraries::Configuration
|
||||
{
|
||||
TyphoeusDisablesCertificateValidationConfiguration() {
|
||||
this = "TyphoeusDisablesCertificateValidationConfiguration"
|
||||
}
|
||||
private module TyphoeusDisablesCertificateValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.asExpr().getExpr().(BooleanLiteral).isFalse() }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().getExpr().(BooleanLiteral).isFalse()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(TyphoeusHttpRequest req).getCertificateValidationControllingValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module TyphoeusDisablesCertificateValidationFlow =
|
||||
DataFlow::Global<TyphoeusDisablesCertificateValidationConfig>;
|
||||
|
||||
@@ -5,7 +5,6 @@ private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplForPathname
|
||||
|
||||
/**
|
||||
* Modeling of the `Pathname` class from the Ruby standard library.
|
||||
@@ -28,13 +27,11 @@ module Pathname {
|
||||
*/
|
||||
class PathnameInstance extends FileNameSource {
|
||||
cached
|
||||
PathnameInstance() { any(PathnameConfiguration c).hasFlowTo(this) }
|
||||
PathnameInstance() { PathnameFlow::flowTo(this) }
|
||||
}
|
||||
|
||||
private class PathnameConfiguration extends Configuration {
|
||||
PathnameConfiguration() { this = "PathnameConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
private module PathnameConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// A call to `Pathname.new`.
|
||||
source = API::getTopLevelMember("Pathname").getAnInstantiation()
|
||||
or
|
||||
@@ -42,9 +39,9 @@ module Pathname {
|
||||
source = API::getTopLevelMember("Pathname").getAMethodCall(["getwd", "pwd",])
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
predicate isSink(DataFlow::Node sink) { any() }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node2 =
|
||||
any(DataFlow::CallNode c |
|
||||
c.getReceiver() = node1 and
|
||||
@@ -57,6 +54,8 @@ module Pathname {
|
||||
}
|
||||
}
|
||||
|
||||
private module PathnameFlow = DataFlow::Global<PathnameConfig>;
|
||||
|
||||
/** A call where the receiver is a `Pathname`. */
|
||||
class PathnameCall extends DataFlow::CallNode {
|
||||
PathnameCall() { this.getReceiver() instanceof PathnameInstance }
|
||||
|
||||
@@ -9,8 +9,9 @@ private import ImproperLdapAuthCustomizations::ImproperLdapAuth
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting improper LDAP authentication vulnerabilities.
|
||||
* DEPRECATED: Use `ImproperLdapAuthFlow` instead
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
deprecated class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "ImproperLdapAuth" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -19,3 +20,16 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
private module ImproperLdapAuthConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking for detecting improper LDAP authentication vulnerabilities.
|
||||
*/
|
||||
module ImproperLdapAuthFlow = TaintTracking::Global<ImproperLdapAuthConfig>;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.DataFlow2
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
|
||||
private import codeql.ruby.Concepts as Concepts
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.security.ImproperLdapAuthQuery
|
||||
import codeql.ruby.Concepts
|
||||
import DataFlow::PathGraph
|
||||
import ImproperLdapAuthFlow::PathGraph
|
||||
|
||||
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
from ImproperLdapAuthFlow::PathNode source, ImproperLdapAuthFlow::PathNode sink
|
||||
where ImproperLdapAuthFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This LDAP authencation depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
|
||||
Reference in New Issue
Block a user