mirror of
https://github.com/github/codeql.git
synced 2025-12-18 09:43:15 +01:00
Merge branch 'main' into amammad-python-WebAppsConstatntSecretKeys
This commit is contained in:
@@ -1,3 +1,24 @@
|
||||
## 0.10.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.10.1
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Data flow configurations can now include a predicate `neverSkip(Node node)`
|
||||
in order to ensure inclusion of certain nodes in the path explanations. The
|
||||
predicate defaults to the end-points of the additional flow steps provided in
|
||||
the configuration, which means that such steps now always are visible by
|
||||
default in path explanations.
|
||||
* Add support for Models as Data for Reflected XSS query
|
||||
* Parameters with a default value are now considered a `DefinitionNode`. This improvement was motivated by allowing type-tracking and API graphs to follow flow from such a default value to a use by a captured variable.
|
||||
|
||||
## 0.10.0
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improvements of the `aiohttp` models including remote-flow-sources from type annotations, new path manipulation, and SSRF sinks.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
16
python/ql/lib/change-notes/released/0.10.1.md
Normal file
16
python/ql/lib/change-notes/released/0.10.1.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 0.10.1
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Data flow configurations can now include a predicate `neverSkip(Node node)`
|
||||
in order to ensure inclusion of certain nodes in the path explanations. The
|
||||
predicate defaults to the end-points of the additional flow steps provided in
|
||||
the configuration, which means that such steps now always are visible by
|
||||
default in path explanations.
|
||||
* Add support for Models as Data for Reflected XSS query
|
||||
* Parameters with a default value are now considered a `DefinitionNode`. This improvement was motivated by allowing type-tracking and API graphs to follow flow from such a default value to a use by a captured variable.
|
||||
3
python/ql/lib/change-notes/released/0.10.2.md
Normal file
3
python/ql/lib/change-notes/released/0.10.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.10.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.10.0
|
||||
lastReleaseVersion: 0.10.2
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
name: codeql/python-all
|
||||
version: 0.10.1-dev
|
||||
version: 0.10.3-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/regex: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
|
||||
@@ -640,12 +640,23 @@ class DefinitionNode extends ControlFlowNode {
|
||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
||||
or
|
||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
||||
or
|
||||
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
|
||||
}
|
||||
|
||||
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
||||
ControlFlowNode getValue() {
|
||||
result = assigned_value(this.getNode()).getAFlowNode() and
|
||||
(result.getBasicBlock().dominates(this.getBasicBlock()) or result.isImport())
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
result.isImport()
|
||||
or
|
||||
// since the default value for a parameter is evaluated in the same basic block as
|
||||
// the function definition, but the parameter belongs to the basic block of the function,
|
||||
// there is no dominance relationship between the two.
|
||||
exists(Parameter param | this = param.asName().getAFlowNode())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,6 +806,8 @@ private AstNode assigned_value(Expr lhs) {
|
||||
or
|
||||
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
||||
result.(For).getTarget() = lhs
|
||||
or
|
||||
exists(Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||
}
|
||||
|
||||
predicate nested_sequence_assign(
|
||||
|
||||
@@ -22,6 +22,8 @@ private import python
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow {
|
||||
import internal.DataFlow
|
||||
private import internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<PythonDataFlow>
|
||||
import internal.DataFlowImpl1
|
||||
}
|
||||
|
||||
@@ -1,414 +0,0 @@
|
||||
/**
|
||||
* 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 `Global` and `GlobalWithState` 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 `flowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `flowPath`. */
|
||||
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`.
|
||||
*/
|
||||
default predicate isBarrier(Node node, FlowState state) { 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 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`.
|
||||
*/
|
||||
default 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`.
|
||||
*/
|
||||
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 `flowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `flowPath`. */
|
||||
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 `partialFlow` and `partialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
signature int explorationLimitSig();
|
||||
|
||||
/**
|
||||
* The output of a global data flow computation.
|
||||
*/
|
||||
signature module GlobalFlowSig {
|
||||
/**
|
||||
* 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 flowPath(PathNode source, PathNode sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*/
|
||||
predicate flow(Node source, Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate flowTo(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate flowToExpr(DataFlowExpr sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global data flow computation.
|
||||
*/
|
||||
module Global<ConfigSig Config> implements GlobalFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `Global` instead. */
|
||||
deprecated module Make<ConfigSig Config> implements GlobalFlowSig {
|
||||
import Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global data flow computation using flow state.
|
||||
*/
|
||||
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `GlobalWithState` instead. */
|
||||
deprecated module MakeWithState<StateConfigSig Config> implements GlobalFlowSig {
|
||||
import GlobalWithState<Config>
|
||||
}
|
||||
|
||||
signature class PathNodeSig {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode();
|
||||
}
|
||||
|
||||
signature module PathGraphSig<PathNodeSig PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
predicate edges(PathNode a, PathNode b);
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
predicate nodes(PathNode n, string key, string val);
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `PathGraph` from two `PathGraph`s by disjoint union.
|
||||
*/
|
||||
module MergePathGraph<
|
||||
PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig<PathNode1> Graph1,
|
||||
PathGraphSig<PathNode2> Graph2>
|
||||
{
|
||||
private newtype TPathNode =
|
||||
TPathNode1(PathNode1 p) or
|
||||
TPathNode2(PathNode2 p)
|
||||
|
||||
/** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets this as a projection on the first given `PathGraph`. */
|
||||
PathNode1 asPathNode1() { this = TPathNode1(result) }
|
||||
|
||||
/** Gets this as a projection on the second given `PathGraph`. */
|
||||
PathNode2 asPathNode2() { this = TPathNode2(result) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asPathNode1().toString() or
|
||||
result = this.asPathNode2().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
|
||||
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() {
|
||||
result = this.asPathNode1().getNode() or
|
||||
result = this.asPathNode2().getNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
Graph1::edges(a.asPathNode1(), b.asPathNode1()) or
|
||||
Graph2::edges(a.asPathNode2(), b.asPathNode2())
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
Graph1::nodes(n.asPathNode1(), key, val) or
|
||||
Graph2::nodes(n.asPathNode2(), key, val)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or
|
||||
Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `PathGraph` from three `PathGraph`s by disjoint union.
|
||||
*/
|
||||
module MergePathGraph3<
|
||||
PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3,
|
||||
PathGraphSig<PathNode1> Graph1, PathGraphSig<PathNode2> Graph2, PathGraphSig<PathNode3> Graph3>
|
||||
{
|
||||
private module MergedInner = MergePathGraph<PathNode1, PathNode2, Graph1, Graph2>;
|
||||
|
||||
private module Merged =
|
||||
MergePathGraph<MergedInner::PathNode, PathNode3, MergedInner::PathGraph, Graph3>;
|
||||
|
||||
/** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */
|
||||
class PathNode instanceof Merged::PathNode {
|
||||
/** Gets this as a projection on the first given `PathGraph`. */
|
||||
PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() }
|
||||
|
||||
/** Gets this as a projection on the second given `PathGraph`. */
|
||||
PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() }
|
||||
|
||||
/** Gets this as a projection on the third given `PathGraph`. */
|
||||
PathNode3 asPathNode3() { result = super.asPathNode2() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph = Merged::PathGraph;
|
||||
}
|
||||
@@ -1561,7 +1561,8 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
|
||||
}
|
||||
|
||||
/** Gets a viable run-time target for the call `call`. */
|
||||
DataFlowCallable viableCallable(ExtractedDataFlowCall call) {
|
||||
DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
call instanceof ExtractedDataFlowCall and
|
||||
result = call.getCallable()
|
||||
or
|
||||
// A call to a library callable with a flow summary
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig {
|
||||
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
|
||||
@@ -313,6 +315,8 @@ private module Config implements FullStateConfigSig {
|
||||
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() }
|
||||
|
||||
@@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig {
|
||||
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
|
||||
@@ -313,6 +315,8 @@ private module Config implements FullStateConfigSig {
|
||||
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() }
|
||||
|
||||
@@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig {
|
||||
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
|
||||
@@ -313,6 +315,8 @@ private module Config implements FullStateConfigSig {
|
||||
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() }
|
||||
|
||||
@@ -276,6 +276,8 @@ private module Config implements FullStateConfigSig {
|
||||
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
|
||||
@@ -313,6 +315,8 @@ private module Config implements FullStateConfigSig {
|
||||
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() }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
* Provides Python-specific definitions for use in the data flow library.
|
||||
*/
|
||||
|
||||
private import codeql.dataflow.DataFlow
|
||||
// we need to export `Unit` for the DataFlowImpl* files
|
||||
private import python as Python
|
||||
|
||||
@@ -13,3 +14,12 @@ module Public {
|
||||
import DataFlowPublic
|
||||
import DataFlowUtil
|
||||
}
|
||||
|
||||
module PythonDataFlow implements InputSig {
|
||||
import Private
|
||||
import Public
|
||||
|
||||
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
|
||||
|
||||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ import DataFlowDispatch
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) {
|
||||
p.isParameterOf(c, pos)
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
||||
p.(ParameterNodeImpl).isParameterOf(c, pos)
|
||||
}
|
||||
|
||||
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
|
||||
@@ -473,6 +473,15 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
// Setting the possible values of the variable at the end of import time
|
||||
nodeFrom = nodeTo.(ModuleVariableNode).getADefiningWrite()
|
||||
or
|
||||
// a parameter with a default value, since the parameter will be in the scope of the
|
||||
// function, while the default value itself will be in the scope that _defines_ the
|
||||
// function.
|
||||
exists(ParameterDefinition param |
|
||||
// note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
|
||||
nodeFrom.asCfgNode() = param.getDefault() and
|
||||
nodeTo.asCfgNode() = param.getDefiningNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,9 +583,6 @@ predicate jumpStepSharedWithTypeTracker(Node nodeFrom, Node nodeTo) {
|
||||
r.getAttributeName(), nodeFrom) and
|
||||
nodeTo = r
|
||||
)
|
||||
or
|
||||
// Default value for parameter flows to that parameter
|
||||
defaultValueFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +608,7 @@ predicate jumpStepNotSharedWithTypeTracker(Node nodeFrom, Node nodeTo) {
|
||||
* Holds if data can flow from `nodeFrom` to `nodeTo` via an assignment to
|
||||
* content `c`.
|
||||
*/
|
||||
predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) {
|
||||
listStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
setStoreStep(nodeFrom, c, nodeTo)
|
||||
@@ -797,23 +803,10 @@ predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode n
|
||||
)
|
||||
}
|
||||
|
||||
predicate defaultValueFlowStep(CfgNode nodeFrom, CfgNode nodeTo) {
|
||||
exists(Function f, Parameter p, ParameterDefinition def |
|
||||
// `getArgByName` supports, unlike `getAnArg`, keyword-only parameters
|
||||
p = f.getArgByName(_) and
|
||||
nodeFrom.asExpr() = p.getDefault() and
|
||||
// The following expresses
|
||||
// nodeTo.(ParameterNode).getParameter() = p
|
||||
// without non-monotonic recursion
|
||||
def.getParameter() = p and
|
||||
nodeTo.getNode() = def.getDefiningNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`.
|
||||
*/
|
||||
predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) {
|
||||
subscriptReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
iterableUnpackingReadStep(nodeFrom, c, nodeTo)
|
||||
@@ -888,7 +881,7 @@ predicate attributeReadStep(Node nodeFrom, AttributeContent c, AttrRead nodeTo)
|
||||
* any value stored inside `f` is cleared at the pre-update node associated with `x`
|
||||
* in `x.f = newValue`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
matchClearStep(n, c)
|
||||
or
|
||||
attributeClearStep(n, c)
|
||||
@@ -940,8 +933,6 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { none() }
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
|
||||
@@ -24,6 +24,7 @@ private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> imp
|
||||
Config::allowImplicitRead(node, c)
|
||||
or
|
||||
(
|
||||
Config::isSink(node) or
|
||||
Config::isSink(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _, _, _)
|
||||
|
||||
@@ -20,7 +20,12 @@ module SsaSource {
|
||||
/** Holds if `v` is defined by assignment at `defn` and given `value`. */
|
||||
cached
|
||||
predicate assignment_definition(Variable v, ControlFlowNode defn, ControlFlowNode value) {
|
||||
defn.(NameNode).defines(v) and defn.(DefinitionNode).getValue() = value
|
||||
defn.(NameNode).defines(v) and
|
||||
defn.(DefinitionNode).getValue() = value and
|
||||
// since parameter will be considered a DefinitionNode, if it has a default value,
|
||||
// we need to exclude it here since it is already covered by parameter_definition
|
||||
// (and points-to was unhappy that it was included in both)
|
||||
not parameter_definition(v, defn)
|
||||
}
|
||||
|
||||
/** Holds if `v` is defined by assignment of the captured exception. */
|
||||
|
||||
@@ -468,6 +468,27 @@ module AiohttpWebModel {
|
||||
override string getSourceType() { result = "aiohttp.web.Request" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter that has a type annotation of `aiohttp.web.Request`, so with all
|
||||
* likelihood will receive an `aiohttp.web.Request` instance at some point when a
|
||||
* request handler is invoked.
|
||||
*/
|
||||
class AiohttpRequestParamFromTypeAnnotation extends Request::InstanceSource,
|
||||
DataFlow::ParameterNode, RemoteFlowSource::Range
|
||||
{
|
||||
AiohttpRequestParamFromTypeAnnotation() {
|
||||
not this instanceof AiohttpRequestHandlerRequestParam and
|
||||
this.getParameter().getAnnotation() =
|
||||
API::moduleImport("aiohttp")
|
||||
.getMember("web")
|
||||
.getMember("Request")
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "aiohttp.web.Request from type-annotation" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A read of the `request` attribute on an instance of an aiohttp.web View class,
|
||||
* which is the request being processed currently.
|
||||
@@ -498,14 +519,17 @@ module AiohttpWebModel {
|
||||
* - https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
|
||||
*/
|
||||
class AiohttpWebResponseInstantiation extends Http::Server::HttpResponse::Range,
|
||||
Response::InstanceSource, DataFlow::CallCfgNode
|
||||
Response::InstanceSource, API::CallNode
|
||||
{
|
||||
API::Node apiNode;
|
||||
|
||||
AiohttpWebResponseInstantiation() {
|
||||
this = apiNode.getACall() and
|
||||
(
|
||||
apiNode = API::moduleImport("aiohttp").getMember("web").getMember("Response")
|
||||
apiNode =
|
||||
API::moduleImport("aiohttp")
|
||||
.getMember("web")
|
||||
.getMember(["FileResponse", "Response", "StreamResponse"])
|
||||
or
|
||||
exists(string httpExceptionClassName |
|
||||
httpExceptionClassName in [
|
||||
@@ -545,6 +569,10 @@ module AiohttpWebModel {
|
||||
|
||||
override DataFlow::Node getMimetypeOrContentTypeArg() {
|
||||
result = this.getArgByName("content_type")
|
||||
or
|
||||
exists(string key | key.toLowerCase() = "content-type" |
|
||||
result = this.getKeywordParameter("headers").getSubscript(key).getAValueReachingSink()
|
||||
)
|
||||
}
|
||||
|
||||
override string getMimetypeDefault() {
|
||||
@@ -556,6 +584,37 @@ module AiohttpWebModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `aiohttp.web.FileResponse` constructor as a sink for Filesystem access.
|
||||
*/
|
||||
class FileResponseCall extends FileSystemAccess::Range, API::CallNode {
|
||||
FileResponseCall() {
|
||||
this = API::moduleImport("aiohttp").getMember("web").getMember("FileResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getParameter(0, "path").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instantiation of `aiohttp.web.StreamResponse`.
|
||||
*
|
||||
* See https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.StreamResponse
|
||||
*/
|
||||
class StreamResponse extends AiohttpWebResponseInstantiation {
|
||||
StreamResponse() {
|
||||
this = API::moduleImport("aiohttp").getMember("web").getMember("StreamResponse").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() {
|
||||
result =
|
||||
this.getReturn()
|
||||
.getMember(["write", "write_eof"])
|
||||
.getACall()
|
||||
.getParameter(0, "data")
|
||||
.asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets an HTTP response instance. */
|
||||
private API::Node aiohttpResponseInstance() {
|
||||
result = any(AiohttpWebResponseInstantiation call).getApiNode().getReturn()
|
||||
@@ -670,14 +729,14 @@ private module AiohttpClientModel {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
methodName in [Http::httpVerbLower(), "request"] and
|
||||
methodName in [Http::httpVerbLower(), "request", "ws_connect"] and
|
||||
this = instance().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
result = this.getArgByName("url")
|
||||
or
|
||||
not methodName = "request" and
|
||||
methodName in [Http::httpVerbLower(), "ws_connect"] and
|
||||
result = this.getArg(0)
|
||||
or
|
||||
methodName = "request" and
|
||||
|
||||
@@ -454,6 +454,14 @@ private API::Node getNodeFromPath(string type, AccessPath path, int n) {
|
||||
or
|
||||
// Apply a type step
|
||||
typeStep(getNodeFromPath(type, path, n), result)
|
||||
or
|
||||
// Apply a fuzzy step (without advancing 'n')
|
||||
path.getToken(n).getName() = "Fuzzy" and
|
||||
result = Specific::getAFuzzySuccessor(getNodeFromPath(type, path, n))
|
||||
or
|
||||
// Skip a fuzzy step (advance 'n' without changing the current node)
|
||||
path.getToken(n - 1).getName() = "Fuzzy" and
|
||||
result = getNodeFromPath(type, path, n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,6 +508,14 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath, int n)
|
||||
// will themselves find by following type-steps.
|
||||
n > 0 and
|
||||
n < subPath.getNumToken()
|
||||
or
|
||||
// Apply a fuzzy step (without advancing 'n')
|
||||
subPath.getToken(n).getName() = "Fuzzy" and
|
||||
result = Specific::getAFuzzySuccessor(getNodeFromSubPath(base, subPath, n))
|
||||
or
|
||||
// Skip a fuzzy step (advance 'n' without changing the current node)
|
||||
subPath.getToken(n - 1).getName() = "Fuzzy" and
|
||||
result = getNodeFromSubPath(base, subPath, n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,7 +577,7 @@ private Specific::InvokeNode getInvocationFromPath(string type, AccessPath path)
|
||||
*/
|
||||
bindingset[name]
|
||||
private predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"]
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar", "Fuzzy"]
|
||||
or
|
||||
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
|
||||
}
|
||||
@@ -572,7 +588,7 @@ private predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
*/
|
||||
bindingset[name]
|
||||
private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name = "ReturnValue"
|
||||
name = ["ReturnValue", "Fuzzy"]
|
||||
or
|
||||
Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name)
|
||||
}
|
||||
|
||||
@@ -108,6 +108,23 @@ API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
API::Node getAFuzzySuccessor(API::Node node) {
|
||||
result = node.getAMember()
|
||||
or
|
||||
result = node.getParameter(_)
|
||||
or
|
||||
result = node.getKeywordParameter(_)
|
||||
or
|
||||
result = node.getReturn()
|
||||
or
|
||||
result = node.getASubscript()
|
||||
or
|
||||
result = node.getAwaited()
|
||||
or
|
||||
result = node.getASubclass()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `invoke` matches the PY-specific call site filter in `token`.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
@@ -43,6 +44,15 @@ module ReflectedXss {
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "reflected cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
private class SinkFromModel extends Sink {
|
||||
SinkFromModel() {
|
||||
this = ModelOutput::getASinkNode(["html-injection", "js-injection"]).asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The body of a HTTP response that will be returned from a server, considered as a flow sink.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 0.8.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed modeling of `aiohttp.ClientSession` so we properly handle `async with` uses. This can impact results of server-side request forgery queries (`py/full-ssrf`, `py/partial-ssrf`).
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.8.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed modeling of `aiohttp.ClientSession` so we properly handle `async with` uses. This can impact results of server-side request forgery queries (`py/full-ssrf`, `py/partial-ssrf`).
|
||||
3
python/ql/src/change-notes/released/0.8.2.md
Normal file
3
python/ql/src/change-notes/released/0.8.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.0
|
||||
lastReleaseVersion: 0.8.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.8.1-dev
|
||||
version: 0.8.3-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -65,6 +65,19 @@ def to_inner_scope():
|
||||
also_x = foo() # $ tracked
|
||||
print(also_x) # $ tracked
|
||||
|
||||
|
||||
def from_parameter_default():
|
||||
x_alias = tracked # $tracked
|
||||
def outer(x=tracked): # $tracked
|
||||
print(x) # $tracked
|
||||
def inner():
|
||||
print(x) # $ tracked
|
||||
print(x_alias) # $tracked
|
||||
return x # $tracked
|
||||
also_x = outer() # $tracked
|
||||
print(also_x) # $tracked
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Function decorator
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
@@ -292,10 +292,11 @@ module HttpServerHttpResponseTest implements TestSig {
|
||||
exists(DedicatedResponseTest d | d.isDedicatedFile(file))
|
||||
) and
|
||||
(
|
||||
exists(Http::Server::HttpResponse response |
|
||||
location = response.getLocation() and
|
||||
element = response.toString() and
|
||||
value = prettyNodeForInlineTest(response.getBody()) and
|
||||
exists(Http::Server::HttpResponse response, DataFlow::Node body |
|
||||
body = response.getBody() and
|
||||
location = body.getLocation() and
|
||||
element = body.toString() and
|
||||
value = prettyNodeForInlineTest(body) and
|
||||
tag = "responseBody"
|
||||
)
|
||||
or
|
||||
|
||||
@@ -3,23 +3,30 @@ edges
|
||||
| test.py:3:1:3:3 | GSSA Variable BSC | test.py:35:19:35:21 | ControlFlowNode for BSC |
|
||||
| test.py:3:1:3:3 | GSSA Variable BSC | test.py:66:19:66:21 | ControlFlowNode for BSC |
|
||||
| test.py:3:7:3:51 | ControlFlowNode for Attribute() | test.py:3:1:3:3 | GSSA Variable BSC |
|
||||
| test.py:7:19:7:21 | ControlFlowNode for BSC | test.py:8:5:8:15 | ControlFlowNode for blob_client |
|
||||
| test.py:7:19:7:21 | ControlFlowNode for BSC | test.py:7:19:7:42 | ControlFlowNode for Attribute() |
|
||||
| test.py:7:19:7:42 | ControlFlowNode for Attribute() | test.py:8:5:8:15 | ControlFlowNode for blob_client |
|
||||
| test.py:8:5:8:15 | ControlFlowNode for blob_client | test.py:9:5:9:15 | ControlFlowNode for blob_client |
|
||||
| test.py:9:5:9:15 | ControlFlowNode for blob_client | test.py:9:5:9:15 | [post] ControlFlowNode for blob_client |
|
||||
| test.py:9:5:9:15 | [post] ControlFlowNode for blob_client | test.py:11:9:11:19 | ControlFlowNode for blob_client |
|
||||
| test.py:15:27:15:71 | ControlFlowNode for Attribute() | test.py:16:5:16:23 | ControlFlowNode for blob_service_client |
|
||||
| test.py:16:5:16:23 | ControlFlowNode for blob_service_client | test.py:17:5:17:23 | ControlFlowNode for blob_service_client |
|
||||
| test.py:17:5:17:23 | ControlFlowNode for blob_service_client | test.py:17:5:17:23 | [post] ControlFlowNode for blob_service_client |
|
||||
| test.py:17:5:17:23 | [post] ControlFlowNode for blob_service_client | test.py:21:9:21:19 | ControlFlowNode for blob_client |
|
||||
| test.py:17:5:17:23 | [post] ControlFlowNode for blob_service_client | test.py:19:19:19:37 | ControlFlowNode for blob_service_client |
|
||||
| test.py:19:19:19:37 | ControlFlowNode for blob_service_client | test.py:19:19:19:58 | ControlFlowNode for Attribute() |
|
||||
| test.py:19:19:19:58 | ControlFlowNode for Attribute() | test.py:21:9:21:19 | ControlFlowNode for blob_client |
|
||||
| test.py:25:24:25:66 | ControlFlowNode for Attribute() | test.py:26:5:26:20 | ControlFlowNode for container_client |
|
||||
| test.py:26:5:26:20 | ControlFlowNode for container_client | test.py:27:5:27:20 | ControlFlowNode for container_client |
|
||||
| test.py:27:5:27:20 | ControlFlowNode for container_client | test.py:27:5:27:20 | [post] ControlFlowNode for container_client |
|
||||
| test.py:27:5:27:20 | [post] ControlFlowNode for container_client | test.py:31:9:31:19 | ControlFlowNode for blob_client |
|
||||
| test.py:35:19:35:21 | ControlFlowNode for BSC | test.py:36:5:36:15 | ControlFlowNode for blob_client |
|
||||
| test.py:27:5:27:20 | [post] ControlFlowNode for container_client | test.py:29:19:29:34 | ControlFlowNode for container_client |
|
||||
| test.py:29:19:29:34 | ControlFlowNode for container_client | test.py:29:19:29:55 | ControlFlowNode for Attribute() |
|
||||
| test.py:29:19:29:55 | ControlFlowNode for Attribute() | test.py:31:9:31:19 | ControlFlowNode for blob_client |
|
||||
| test.py:35:19:35:21 | ControlFlowNode for BSC | test.py:35:19:35:42 | ControlFlowNode for Attribute() |
|
||||
| test.py:35:19:35:42 | ControlFlowNode for Attribute() | test.py:36:5:36:15 | ControlFlowNode for blob_client |
|
||||
| test.py:36:5:36:15 | ControlFlowNode for blob_client | test.py:37:5:37:15 | ControlFlowNode for blob_client |
|
||||
| test.py:37:5:37:15 | ControlFlowNode for blob_client | test.py:37:5:37:15 | [post] ControlFlowNode for blob_client |
|
||||
| test.py:37:5:37:15 | [post] ControlFlowNode for blob_client | test.py:43:9:43:19 | ControlFlowNode for blob_client |
|
||||
| test.py:66:19:66:21 | ControlFlowNode for BSC | test.py:67:5:67:15 | ControlFlowNode for blob_client |
|
||||
| test.py:66:19:66:21 | ControlFlowNode for BSC | test.py:66:19:66:42 | ControlFlowNode for Attribute() |
|
||||
| test.py:66:19:66:42 | ControlFlowNode for Attribute() | test.py:67:5:67:15 | ControlFlowNode for blob_client |
|
||||
| test.py:67:5:67:15 | ControlFlowNode for blob_client | test.py:68:5:68:15 | ControlFlowNode for blob_client |
|
||||
| test.py:68:5:68:15 | ControlFlowNode for blob_client | test.py:68:5:68:15 | [post] ControlFlowNode for blob_client |
|
||||
| test.py:68:5:68:15 | [post] ControlFlowNode for blob_client | test.py:69:12:69:22 | ControlFlowNode for blob_client |
|
||||
@@ -29,6 +36,7 @@ nodes
|
||||
| test.py:3:1:3:3 | GSSA Variable BSC | semmle.label | GSSA Variable BSC |
|
||||
| test.py:3:7:3:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:7:19:7:21 | ControlFlowNode for BSC | semmle.label | ControlFlowNode for BSC |
|
||||
| test.py:7:19:7:42 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:8:5:8:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:9:5:9:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:9:5:9:15 | [post] ControlFlowNode for blob_client | semmle.label | [post] ControlFlowNode for blob_client |
|
||||
@@ -37,18 +45,24 @@ nodes
|
||||
| test.py:16:5:16:23 | ControlFlowNode for blob_service_client | semmle.label | ControlFlowNode for blob_service_client |
|
||||
| test.py:17:5:17:23 | ControlFlowNode for blob_service_client | semmle.label | ControlFlowNode for blob_service_client |
|
||||
| test.py:17:5:17:23 | [post] ControlFlowNode for blob_service_client | semmle.label | [post] ControlFlowNode for blob_service_client |
|
||||
| test.py:19:19:19:37 | ControlFlowNode for blob_service_client | semmle.label | ControlFlowNode for blob_service_client |
|
||||
| test.py:19:19:19:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:21:9:21:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:25:24:25:66 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:26:5:26:20 | ControlFlowNode for container_client | semmle.label | ControlFlowNode for container_client |
|
||||
| test.py:27:5:27:20 | ControlFlowNode for container_client | semmle.label | ControlFlowNode for container_client |
|
||||
| test.py:27:5:27:20 | [post] ControlFlowNode for container_client | semmle.label | [post] ControlFlowNode for container_client |
|
||||
| test.py:29:19:29:34 | ControlFlowNode for container_client | semmle.label | ControlFlowNode for container_client |
|
||||
| test.py:29:19:29:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:31:9:31:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:35:19:35:21 | ControlFlowNode for BSC | semmle.label | ControlFlowNode for BSC |
|
||||
| test.py:35:19:35:42 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:36:5:36:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:37:5:37:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:37:5:37:15 | [post] ControlFlowNode for blob_client | semmle.label | [post] ControlFlowNode for blob_client |
|
||||
| test.py:43:9:43:19 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:66:19:66:21 | ControlFlowNode for BSC | semmle.label | ControlFlowNode for BSC |
|
||||
| test.py:66:19:66:42 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:67:5:67:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:68:5:68:15 | ControlFlowNode for blob_client | semmle.label | ControlFlowNode for blob_client |
|
||||
| test.py:68:5:68:15 | [post] ControlFlowNode for blob_client | semmle.label | [post] ControlFlowNode for blob_client |
|
||||
|
||||
@@ -1,2 +1,19 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
import MakeInlineTaintTest<TestTaintTrackingConfig>
|
||||
|
||||
predicate isSafe(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
g.(CallNode).getFunction().(NameNode).getId() = "is_safe" and
|
||||
node = g.(CallNode).getArg(_) and
|
||||
branch = true
|
||||
}
|
||||
|
||||
module CustomSanitizerOverridesConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource = TestTaintTrackingConfig::isSource/1;
|
||||
|
||||
predicate isSink = TestTaintTrackingConfig::isSink/1;
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::BarrierGuard<isSafe/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
import MakeInlineTaintTest<CustomSanitizerOverridesConfig>
|
||||
|
||||
@@ -33,3 +33,5 @@ async def test():
|
||||
assert context.verify_mode == ssl.VerifyMode.CERT_NONE
|
||||
|
||||
s.get("url", ssl=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled
|
||||
|
||||
s.ws_connect("url") # $ clientRequestUrlPart="url"
|
||||
|
||||
@@ -23,6 +23,9 @@ async def html_text(request): # $ requestHandler
|
||||
async def html_body(request): # $ requestHandler
|
||||
return web.Response(body=b"foo", content_type="text/html") # $ HttpResponse mimetype=text/html responseBody=b"foo"
|
||||
|
||||
@routes.get("/html_body_header") # $ routeSetup="/html_body_header"
|
||||
async def html_body_header(request): # $ requestHandler
|
||||
return web.Response(headers={"content-type": "text/html"}, text="foo") # $ HttpResponse mimetype=text/html responseBody="foo"
|
||||
|
||||
@routes.get("/html_body_set_later") # $ routeSetup="/html_body_set_later"
|
||||
async def html_body_set_later(request): # $ requestHandler
|
||||
@@ -65,6 +68,26 @@ async def redirect_302(request): # $ requestHandler
|
||||
else:
|
||||
raise web.HTTPFound(location="/logout") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/logout"
|
||||
|
||||
|
||||
@routes.get("/file_response") # $ routeSetup="/file_response"
|
||||
async def file_response(request): # $ requestHandler
|
||||
filename = "foo.txt"
|
||||
resp = web.FileResponse(filename) # $ HttpResponse mimetype=application/octet-stream getAPathArgument=filename
|
||||
resp = web.FileResponse(path=filename) # $ HttpResponse mimetype=application/octet-stream getAPathArgument=filename
|
||||
return resp
|
||||
|
||||
|
||||
@routes.get("/streaming_response") # $ routeSetup="/streaming_response"
|
||||
async def streaming_response(request): # $ requestHandler
|
||||
resp = web.StreamResponse() # $ HttpResponse mimetype=application/octet-stream
|
||||
await resp.prepare(request)
|
||||
|
||||
await resp.write(b"foo") # $ responseBody=b"foo"
|
||||
await resp.write(data=b"bar") # $ responseBody=b"bar"
|
||||
await resp.write_eof(b"baz") # $ responseBody=b"baz"
|
||||
|
||||
return resp
|
||||
|
||||
################################################################################
|
||||
# Cookies
|
||||
################################################################################
|
||||
|
||||
@@ -142,10 +142,36 @@ class TaintTestClass(web.View):
|
||||
self.request.url # $ tainted
|
||||
)
|
||||
|
||||
# not a request handler, and not called, but since we have type-annotation, should be a
|
||||
# remote-flow-source.
|
||||
async def test_source_from_type_annotation(request: web.Request):
|
||||
# picking out just a few of the tests from `test_taint` above, to show that we have
|
||||
# the same taint-steps :)
|
||||
ensure_tainted(
|
||||
request, # $ tainted
|
||||
request.url, # $ tainted
|
||||
await request.content.read(), # $ tainted
|
||||
)
|
||||
|
||||
# Test that since we can reach the `request` object in the helper function, we don't
|
||||
# introduce a new remote-flow-source, but instead use the one from the caller. (which is
|
||||
# checked to not be tainted)
|
||||
async def test_sanitizer(request): # $ requestHandler
|
||||
ensure_tainted(request, request.url, await request.content.read()) # $ tainted
|
||||
|
||||
if (is_safe(request)):
|
||||
ensure_not_tainted(request, request.url, await request.content.read())
|
||||
test_safe_helper_function_no_route_with_type(request)
|
||||
|
||||
|
||||
async def test_safe_helper_function_no_route_with_type(request: web.Request):
|
||||
ensure_not_tainted(request, request.url, await request.content.read()) # $ SPURIOUS: tainted
|
||||
|
||||
|
||||
app = web.Application()
|
||||
app.router.add_get(r"/test_taint/{name}/{number:\d+}", test_taint) # $ routeSetup="/test_taint/{name}/{number:\d+}"
|
||||
app.router.add_view(r"/test_taint_class", TaintTestClass) # $ routeSetup="/test_taint_class"
|
||||
app.router.add_view(r"/test_sanitizer", test_sanitizer) # $ routeSetup="/test_sanitizer"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -11,6 +11,11 @@ taintFlow
|
||||
| test.py:83:50:83:60 | ControlFlowNode for getSource() | test.py:83:8:83:61 | ControlFlowNode for Attribute() |
|
||||
| test.py:86:49:86:59 | ControlFlowNode for getSource() | test.py:86:8:86:60 | ControlFlowNode for Attribute() |
|
||||
| test.py:87:56:87:66 | ControlFlowNode for getSource() | test.py:87:8:87:67 | ControlFlowNode for Attribute() |
|
||||
| test.py:114:19:114:29 | ControlFlowNode for getSource() | test.py:114:19:114:29 | ControlFlowNode for getSource() |
|
||||
| test.py:115:20:115:30 | ControlFlowNode for getSource() | test.py:115:20:115:30 | ControlFlowNode for getSource() |
|
||||
| test.py:116:31:116:41 | ControlFlowNode for getSource() | test.py:116:31:116:41 | ControlFlowNode for getSource() |
|
||||
| test.py:117:31:117:41 | ControlFlowNode for getSource() | test.py:117:31:117:41 | ControlFlowNode for getSource() |
|
||||
| test.py:118:35:118:45 | ControlFlowNode for getSource() | test.py:118:35:118:45 | ControlFlowNode for getSource() |
|
||||
isSink
|
||||
| test.py:4:8:4:8 | ControlFlowNode for x | test-sink |
|
||||
| test.py:7:17:7:17 | ControlFlowNode for x | test-sink |
|
||||
@@ -50,6 +55,11 @@ isSink
|
||||
| test.py:91:21:91:23 | ControlFlowNode for one | test-sink |
|
||||
| test.py:91:30:91:32 | ControlFlowNode for two | test-sink |
|
||||
| test.py:98:6:98:9 | ControlFlowNode for baz2 | test-sink |
|
||||
| test.py:114:19:114:29 | ControlFlowNode for getSource() | test-sink |
|
||||
| test.py:115:20:115:30 | ControlFlowNode for getSource() | test-sink |
|
||||
| test.py:116:31:116:41 | ControlFlowNode for getSource() | test-sink |
|
||||
| test.py:117:31:117:41 | ControlFlowNode for getSource() | test-sink |
|
||||
| test.py:118:35:118:45 | ControlFlowNode for getSource() | test-sink |
|
||||
isSource
|
||||
| test.py:3:5:3:15 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:9:8:9:14 | ControlFlowNode for alias() | test-source |
|
||||
@@ -89,6 +99,12 @@ isSource
|
||||
| test.py:104:32:104:37 | ControlFlowNode for param2 | test-source |
|
||||
| test.py:107:24:107:28 | ControlFlowNode for name1 | test-source |
|
||||
| test.py:107:31:107:35 | ControlFlowNode for name2 | test-source |
|
||||
| test.py:114:19:114:29 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:115:20:115:30 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:116:31:116:41 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:117:31:117:41 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:118:35:118:45 | ControlFlowNode for getSource() | test-source |
|
||||
| test.py:119:20:119:30 | ControlFlowNode for getSource() | test-source |
|
||||
syntaxErrors
|
||||
| Member[foo |
|
||||
| Member[foo] .Member[bar] |
|
||||
|
||||
@@ -60,7 +60,7 @@ class SubClass (ArgPos.MyClass):
|
||||
def foo(self, arg, named=2, otherName=3):
|
||||
pass
|
||||
|
||||
def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5):
|
||||
def secondAndAfter(self, arg1, arg2, arg3, arg4, arg5):
|
||||
pass
|
||||
|
||||
ArgPos.anyParam(arg1, arg2, name=namedThing)
|
||||
@@ -72,7 +72,7 @@ mySink(Steps.preserveTaint(getSource())) # FLOW
|
||||
mySink(Steps.preserveTaint("safe", getSource())) # NO FLOW
|
||||
|
||||
Steps.taintIntoCallback(
|
||||
getSource(),
|
||||
getSource(),
|
||||
lambda x: mySink(x), # FLOW
|
||||
lambda y: mySink(y), # FLOW
|
||||
lambda z: mySink(z) # NO FLOW
|
||||
@@ -106,3 +106,14 @@ class OtherSubClass (ArgPos.MyClass):
|
||||
|
||||
def anyNamed(self, name1, name2=2): # Parameter[any-named] matches all non-self named parameters
|
||||
pass
|
||||
|
||||
import testlib as testlib
|
||||
import testlib.nestedlib as testlib2
|
||||
import otherlib as otherlib
|
||||
|
||||
testlib.fuzzyCall(getSource()) # NOT OK
|
||||
testlib2.fuzzyCall(getSource()) # NOT OK
|
||||
testlib.foo.bar.baz.fuzzyCall(getSource()) # NOT OK
|
||||
testlib.foo().bar().fuzzyCall(getSource()) # NOT OK
|
||||
testlib.foo(lambda x: x.fuzzyCall(getSource())) # NOT OK
|
||||
otherlib.fuzzyCall(getSource()) # OK
|
||||
|
||||
@@ -51,6 +51,8 @@ class Sinks extends ModelInput::SinkModelCsv {
|
||||
// testing package syntax
|
||||
"foo1.bar;Member[baz1].Argument[any];test-sink", //
|
||||
"foo2;Member[bar].Member[baz2].Argument[any];test-sink", //
|
||||
// testing fuzzy
|
||||
"testlib;Fuzzy.Member[fuzzyCall].Argument[0];test-sink", //
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:3:5:3:9 | ControlFlowNode for fail5 | test.py:3:1:3:13 | ControlFlowNode for FunctionExpr |
|
||||
| test.py:4:5:4:8 | ControlFlowNode for Tuple | test.py:4:12:4:12 | ControlFlowNode for t |
|
||||
| test.py:7:5:7:26 | ControlFlowNode for default_value_in_param | test.py:7:1:7:33 | ControlFlowNode for FunctionExpr |
|
||||
| test.py:7:28:7:28 | ControlFlowNode for x | test.py:7:30:7:31 | ControlFlowNode for IntegerLiteral |
|
||||
@@ -0,0 +1,4 @@
|
||||
import python
|
||||
|
||||
from DefinitionNode d
|
||||
select d, d.getValue()
|
||||
@@ -1,4 +1,6 @@
|
||||
| 3 | 5 | ControlFlowNode for fail5 |
|
||||
| 4 | 5 | ControlFlowNode for Tuple |
|
||||
| 4 | 5 | ControlFlowNode for x |
|
||||
| 4 | 8 | ControlFlowNode for y |
|
||||
| 4 | 8 | ControlFlowNode for y |
|
||||
| 7 | 5 | ControlFlowNode for default_value_in_param |
|
||||
| 7 | 28 | ControlFlowNode for x |
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
def fail5(t):
|
||||
x, y = t
|
||||
return x
|
||||
|
||||
def default_value_in_param(x=42):
|
||||
print(x)
|
||||
|
||||
Reference in New Issue
Block a user