Skip to content

Commit 4fe2576

Browse files
committed
Python: start modernizing routing tests
1 parent 823ed44 commit 4fe2576

File tree

3 files changed

+132
-17
lines changed

3 files changed

+132
-17
lines changed

python/ql/test/experimental/dataflow/coverage/argumentPassing.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ def argument_passing(
7272
f,
7373
**g,
7474
):
75-
SINK1(a)
76-
SINK2(b)
75+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:89 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:94 -> ControlFlowNode for a"
76+
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:94 -> ControlFlowNode for b" MISSING:arg2="ControlFlowNode for arg2, l:89 -> ControlFlowNode for b"
7777
SINK3(c)
7878
SINK4(d)
7979
SINK5(e)
@@ -95,8 +95,8 @@ def test_argument_passing2():
9595

9696

9797
def with_pos_only(a, /, b):
98-
SINK1(a)
99-
SINK2(b)
98+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:104 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:105 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:106 -> ControlFlowNode for a"
99+
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:104 -> ControlFlowNode for b" arg2="ControlFlowNode for arg2, l:105 -> ControlFlowNode for b" MISSING: arg2="ControlFlowNode for arg2, l:106 -> ControlFlowNode for b"
100100

101101

102102
@expects(6)
@@ -107,8 +107,8 @@ def test_pos_only():
107107

108108

109109
def with_multiple_kw_args(a, b, c):
110-
SINK1(a)
111-
SINK2(b)
110+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:117 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:118 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:119 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:120 -> ControlFlowNode for a"
111+
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:117 -> ControlFlowNode for b" arg2="ControlFlowNode for arg2, l:120 -> ControlFlowNode for b" MISSING: arg2="ControlFlowNode for arg2, l:118 -> ControlFlowNode for b" arg2="ControlFlowNode for arg2, l:119 -> ControlFlowNode for b"
112112
SINK3(c)
113113

114114

@@ -121,8 +121,8 @@ def test_multiple_kw_args():
121121

122122

123123
def with_default_arguments(a=arg1, b=arg2, c=arg3):
124-
SINK1(a)
125-
SINK2(b)
124+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:132 -> ControlFlowNode for a" MISSING:arg1="ControlFlowNode for arg1, l:123 -> ControlFlowNode for a"
125+
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:133 -> ControlFlowNode for b" MISSING: arg2="ControlFlowNode for arg2, l:123 -> ControlFlowNode for b"
126126
SINK3(c)
127127

128128

@@ -136,14 +136,14 @@ def test_default_arguments():
136136

137137
# Nested constructor pattern
138138
def grab_foo_bar_baz(foo, **kwargs):
139-
SINK1(foo)
139+
SINK1(foo) #$ arg1="ControlFlowNode for arg1, l:160 -> ControlFlowNode for foo"
140140
grab_bar_baz(**kwargs)
141141

142142

143143
# It is not possible to pass `bar` into `kwargs`,
144144
# since `bar` is a valid keyword argument.
145145
def grab_bar_baz(bar, **kwargs):
146-
SINK2(bar)
146+
SINK2(bar) #$ arg2="ControlFlowNode for arg2, l:160 -> ControlFlowNode for bar"
147147
try:
148148
SINK2_F(kwargs["bar"])
149149
except:
@@ -163,49 +163,49 @@ def test_grab():
163163
# All combinations
164164
def test_pos_pos():
165165
def with_pos(a):
166-
SINK1(a)
166+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:168 -> ControlFlowNode for a"
167167

168168
with_pos(arg1)
169169

170170

171171
def test_pos_pos_only():
172172
def with_pos_only(a, /):
173-
SINK1(a)
173+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:175 -> ControlFlowNode for a"
174174

175175
with_pos_only(arg1)
176176

177177

178178
def test_pos_star():
179179
def with_star(*a):
180180
if len(a) > 0:
181-
SINK1(a[0])
181+
SINK1(a[0]) #$ arg1="ControlFlowNode for arg1, l:183 -> ControlFlowNode for Subscript"
182182

183183
with_star(arg1)
184184

185185

186186
def test_pos_kw():
187187
def with_kw(a=""):
188-
SINK1(a)
188+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:190 -> ControlFlowNode for a"
189189

190190
with_kw(arg1)
191191

192192

193193
def test_kw_pos():
194194
def with_pos(a):
195-
SINK1(a)
195+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:197 -> ControlFlowNode for a"
196196

197197
with_pos(a=arg1)
198198

199199

200200
def test_kw_kw():
201201
def with_kw(a=""):
202-
SINK1(a)
202+
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:204 -> ControlFlowNode for a"
203203

204204
with_kw(a=arg1)
205205

206206

207207
def test_kw_doublestar():
208208
def with_doublestar(**a):
209-
SINK1(a["a"])
209+
SINK1(a["a"]) #$ arg1="ControlFlowNode for arg1, l:211 -> ControlFlowNode for Subscript"
210210

211211
with_doublestar(a=arg1)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
| classes.py:556:15:556:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:565 -> ControlFlowNode for key" |
2+
| classes.py:557:15:557:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_getitem, l:563 -> ControlFlowNode for self" |
3+
| classes.py:572:15:572:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:581 -> ControlFlowNode for key" |
4+
| classes.py:573:15:573:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_setitem, l:578 -> ControlFlowNode for self" |
5+
| classes.py:587:15:587:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:595 -> ControlFlowNode for key" |
6+
| classes.py:588:15:588:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_delitem, l:593 -> ControlFlowNode for self" |
7+
| classes.py:658:15:658:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:667 -> ControlFlowNode for other" |
8+
| classes.py:659:15:659:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_add, l:665 -> ControlFlowNode for self" |
9+
| classes.py:673:15:673:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:682 -> ControlFlowNode for other" |
10+
| classes.py:674:15:674:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_sub, l:680 -> ControlFlowNode for self" |
11+
| classes.py:688:15:688:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:697 -> ControlFlowNode for other" |
12+
| classes.py:689:15:689:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_mul, l:695 -> ControlFlowNode for self" |
13+
| classes.py:703:15:703:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:712 -> ControlFlowNode for other" |
14+
| classes.py:704:15:704:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_matmul, l:710 -> ControlFlowNode for self" |
15+
| classes.py:718:15:718:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:727 -> ControlFlowNode for other" |
16+
| classes.py:719:15:719:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_truediv, l:725 -> ControlFlowNode for self" |
17+
| classes.py:733:15:733:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:742 -> ControlFlowNode for other" |
18+
| classes.py:734:15:734:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_floordiv, l:740 -> ControlFlowNode for self" |
19+
| classes.py:748:15:748:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:757 -> ControlFlowNode for other" |
20+
| classes.py:749:15:749:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_mod, l:755 -> ControlFlowNode for self" |
21+
| classes.py:778:15:778:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:793 -> ControlFlowNode for other" |
22+
| classes.py:779:15:779:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_pow, l:791 -> ControlFlowNode for self" |
23+
| classes.py:799:15:799:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:808 -> ControlFlowNode for other" |
24+
| classes.py:800:15:800:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_lshift, l:806 -> ControlFlowNode for self" |
25+
| classes.py:814:15:814:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:823 -> ControlFlowNode for other" |
26+
| classes.py:815:15:815:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_rshift, l:821 -> ControlFlowNode for self" |
27+
| classes.py:829:15:829:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:838 -> ControlFlowNode for other" |
28+
| classes.py:830:15:830:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_and, l:836 -> ControlFlowNode for self" |
29+
| classes.py:844:15:844:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:853 -> ControlFlowNode for other" |
30+
| classes.py:845:15:845:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_xor, l:851 -> ControlFlowNode for self" |
31+
| classes.py:859:15:859:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:868 -> ControlFlowNode for other" |
32+
| classes.py:860:15:860:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_or, l:866 -> ControlFlowNode for self" |
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import python
2+
import semmle.python.dataflow.new.DataFlow
3+
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
4+
import experimental.dataflow.FlowTestUtil.FlowTest
5+
6+
class Argument1RoutingTest extends FlowTest {
7+
Argument1RoutingTest() { this = "Argument1RoutingTest" }
8+
9+
override string flowTag() { result = "arg1" }
10+
11+
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
12+
exists(Argument1RoutingConfig cfg | cfg.hasFlow(source, sink))
13+
}
14+
}
15+
16+
/**
17+
* A configuration to check routing of arguments through magic methods.
18+
*/
19+
class Argument1RoutingConfig extends DataFlow::Configuration {
20+
Argument1RoutingConfig() { this = "Argument1RoutingConfig" }
21+
22+
override predicate isSource(DataFlow::Node node) {
23+
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg1"
24+
or
25+
exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call |
26+
def.getVariable() = node.(DataFlow::EssaNode).getVar() and
27+
def.getValue() = call.getNode() and
28+
call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%")
29+
) and
30+
node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%")
31+
}
32+
33+
override predicate isSink(DataFlow::Node node) {
34+
exists(CallNode call |
35+
call.getFunction().(NameNode).getId() = "SINK1" and
36+
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
37+
)
38+
}
39+
40+
/**
41+
* We want to be able to use `arg` in a sequence of calls such as `func(kw=arg); ... ; func(arg)`.
42+
* Use-use flow lets the argument to the first call reach the sink inside the second call,
43+
* making it seem like we handle all cases even if we only handle the last one.
44+
* We make the test honest by preventing flow into source nodes.
45+
*/
46+
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
47+
}
48+
49+
class Argument2RoutingTest extends FlowTest {
50+
Argument2RoutingTest() { this = "Argument2RoutingTest" }
51+
52+
override string flowTag() { result = "arg2" }
53+
54+
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
55+
exists(Argument2RoutingConfig cfg | cfg.hasFlow(source, sink))
56+
}
57+
}
58+
59+
/**
60+
* A configuration to check routing of arguments through magic methods.
61+
*/
62+
class Argument2RoutingConfig extends DataFlow::Configuration {
63+
Argument2RoutingConfig() { this = "Argument2RoutingConfig" }
64+
65+
override predicate isSource(DataFlow::Node node) {
66+
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg2"
67+
}
68+
69+
override predicate isSink(DataFlow::Node node) {
70+
exists(CallNode call |
71+
call.getFunction().(NameNode).getId() = "SINK2" and
72+
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
73+
)
74+
}
75+
76+
/**
77+
* We want to be able to use `arg` in a sequence of calls such as `func(kw=arg); ... ; func(arg)`.
78+
* Use-use flow lets the argument to the first call reach the sink inside the second call,
79+
* making it seem like we handle all cases even if we only handle the last one.
80+
* We make the test honest by preventing flow into source nodes.
81+
*/
82+
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
83+
}

0 commit comments

Comments
 (0)