Skip to content

Commit df3ac49

Browse files
authored
Merge pull request #2700 from RasmusWL/python-taint-iterable-unpacking
Python: Handle iterable unpacking in taint tracking
2 parents 990d1c1 + 1558cf2 commit df3ac49

31 files changed

+939
-546
lines changed

python/ql/src/semmle/python/Flow.qll

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -753,9 +753,8 @@ class DefinitionNode extends ControlFlowNode {
753753
or
754754
augstore(_, this)
755755
or
756-
exists(Assign a | a.getATarget().(Tuple).getAnElt().getAFlowNode() = this)
757-
or
758-
exists(Assign a | a.getATarget().(List).getAnElt().getAFlowNode() = this)
756+
// `x, y = 1, 2` where LHS is a combination of list or tuples
757+
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
759758
or
760759
exists(For for | for.getTarget().getAFlowNode() = this)
761760
}
@@ -768,6 +767,18 @@ class DefinitionNode extends ControlFlowNode {
768767
}
769768
}
770769

770+
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
771+
exists(Expr elt |
772+
elt = list_or_tuple.(Tuple).getAnElt()
773+
or
774+
elt = list_or_tuple.(List).getAnElt()
775+
|
776+
result = elt
777+
or
778+
result = list_or_tuple_nested_element(elt)
779+
)
780+
}
781+
771782
/** A control flow node corresponding to a deletion statement, such as `del x`.
772783
* There can be multiple `DeletionNode`s for each `Delete` such that each
773784
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
@@ -887,18 +898,38 @@ private AstNode assigned_value(Expr lhs) {
887898
/* lhs += x => result = (lhs + x) */
888899
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
889900
or
890-
/* ..., lhs, ... = ..., result, ... */
891-
exists(Assign a, Tuple target, Tuple values, int index |
892-
a.getATarget() = target and
893-
a.getValue() = values and
894-
lhs = target.getElt(index) and
895-
result = values.getElt(index)
896-
)
901+
/* ..., lhs, ... = ..., result, ...
902+
* or
903+
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
904+
*/
905+
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
897906
or
898907
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
899908
result.(For).getTarget() = lhs
900909
}
901910

911+
predicate nested_sequence_assign(Expr left_parent, Expr right_parent,
912+
Expr left_result, Expr right_result) {
913+
exists(int i, Expr left_elem, Expr right_elem
914+
|
915+
(
916+
left_elem = left_parent.(Tuple).getElt(i)
917+
or
918+
left_elem = left_parent.(List).getElt(i)
919+
)
920+
and
921+
(
922+
right_elem = right_parent.(Tuple).getElt(i)
923+
or
924+
right_elem = right_parent.(List).getElt(i)
925+
)
926+
|
927+
left_result = left_elem and right_result = right_elem
928+
or
929+
nested_sequence_assign(left_elem, right_elem, left_result, right_result)
930+
)
931+
}
932+
902933
/** A flow node for a `for` statement. */
903934
class ForNode extends ControlFlowNode {
904935

@@ -1037,6 +1068,17 @@ class NameConstantNode extends NameNode {
10371068
*/
10381069
}
10391070

1071+
/** A control flow node correspoinding to a starred expression, `*a`. */
1072+
class StarredNode extends ControlFlowNode {
1073+
StarredNode() {
1074+
toAst(this) instanceof Starred
1075+
}
1076+
1077+
ControlFlowNode getValue() {
1078+
toAst(result) = toAst(this).(Starred).getValue()
1079+
}
1080+
}
1081+
10401082
private module Scopes {
10411083

10421084
private predicate fast_local(NameNode n) {

python/ql/src/semmle/python/dataflow/Configuration.qll

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ private import semmle.python.objects.ObjectInternal
44
private import semmle.python.dataflow.Implementation
55

66
module TaintTracking {
7-
87
class Source = TaintSource;
98

109
class Sink = TaintSink;
@@ -16,13 +15,11 @@ module TaintTracking {
1615
class PathSink = TaintTrackingNode;
1716

1817
abstract class Configuration extends string {
19-
2018
/* Required to prevent compiler warning */
2119
bindingset[this]
2220
Configuration() { this = this }
2321

2422
/* Old implementation API */
25-
2623
predicate isSource(Source src) { none() }
2724

2825
predicate isSink(Sink sink) { none() }
@@ -32,7 +29,6 @@ module TaintTracking {
3229
predicate isExtension(Extension extension) { none() }
3330

3431
/* New implementation API */
35-
3632
/**
3733
* Holds if `src` is a source of taint of `kind` that is relevant
3834
* for this configuration.
@@ -66,7 +62,9 @@ module TaintTracking {
6662
/**
6763
* Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`.
6864
*/
69-
predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) {
65+
predicate isAdditionalFlowStep(
66+
DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind
67+
) {
7068
none()
7169
}
7270

@@ -79,9 +77,7 @@ module TaintTracking {
7977
* Holds if `node` should be considered as a barrier to flow of `kind`.
8078
*/
8179
predicate isBarrier(DataFlow::Node node, TaintKind kind) {
82-
exists(Sanitizer sanitizer |
83-
this.isSanitizer(sanitizer)
84-
|
80+
exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) |
8581
sanitizer.sanitizingNode(kind, node.asCfgNode())
8682
or
8783
sanitizer.sanitizingEdge(kind, node.asVariable())
@@ -112,16 +108,18 @@ module TaintTracking {
112108
* Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`.
113109
* Note that `srckind` and `destkind` can be the same.
114110
*/
115-
predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) { none() }
111+
predicate isBarrierEdge(
112+
DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind
113+
) {
114+
none()
115+
}
116116

117117
/* Common query API */
118-
119118
predicate hasFlowPath(PathSource src, PathSink sink) {
120119
this.(TaintTrackingImplementation).hasFlowPath(src, sink)
121120
}
122121

123122
/* Old query API */
124-
125123
/* deprecated */
126124
deprecated predicate hasFlow(Source src, Sink sink) {
127125
exists(PathSource psrc, PathSink psink |
@@ -132,15 +130,12 @@ module TaintTracking {
132130
}
133131

134132
/* New query API */
135-
136133
predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) {
137134
exists(PathSource psrc, PathSink psink |
138135
this.hasFlowPath(psrc, psink) and
139136
src = psrc.getNode() and
140137
sink = psink.getNode()
141138
)
142139
}
143-
144140
}
145-
146141
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import semmle.python.security.TaintTracking
1+
import semmle.python.security.TaintTracking

python/ql/src/semmle/python/dataflow/Files.qll

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,18 @@ import python
22
import semmle.python.security.TaintTracking
33

44
class OpenFile extends TaintKind {
5-
65
OpenFile() { this = "file.open" }
76

87
override string repr() { result = "an open file" }
9-
10-
118
}
129

1310
class OpenFileConfiguration extends TaintTracking::Configuration {
14-
15-
OpenFileConfiguration() { this = "Open file configuration" }
11+
OpenFileConfiguration() { this = "Open file configuration" }
1612

1713
override predicate isSource(DataFlow::Node src, TaintKind kind) {
1814
theOpenFunction().(FunctionObject).getACall() = src.asCfgNode() and
1915
kind instanceof OpenFile
2016
}
2117

22-
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
23-
none()
24-
}
25-
18+
override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() }
2619
}

0 commit comments

Comments
 (0)