Skip to content

Commit 77da4b0

Browse files
committed
Python: Remove absolute line numbers
- Use relative line numbers in flow test - Elide line numbers in routing test (new concept)
1 parent 42fa3bd commit 77da4b0

File tree

8 files changed

+482
-440
lines changed

8 files changed

+482
-440
lines changed

python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@ abstract class FlowTest extends InlineExpectationsTest {
2626

2727
pragma[inline]
2828
private string lineStr(DataFlow::Node fromNode, DataFlow::Node toNode) {
29-
if fromNode.getLocation().getStartLine() = toNode.getLocation().getStartLine()
30-
then result = ""
31-
else result = ", l:" + fromNode.getLocation().getStartLine()
29+
exists(int delta |
30+
delta = fromNode.getLocation().getStartLine() - toNode.getLocation().getStartLine()
31+
|
32+
if delta = 0
33+
then result = ""
34+
else
35+
if delta > 0
36+
then result = ", l:+" + delta.toString()
37+
else result = ", l:" + delta.toString()
38+
)
3239
}
3340
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import python
2+
import semmle.python.dataflow.new.DataFlow
3+
import TestUtilities.InlineExpectationsTest
4+
import experimental.dataflow.TestUtil.PrintNode
5+
6+
/**
7+
* A routing test is designed to test that vlues are routed to the
8+
* correct arguments of the correct functions. It is assumed that
9+
* the functions tested sink their arguments sequentially, that is
10+
* `SINK1(arg1)`, etc.
11+
*/
12+
abstract class RoutingTest extends InlineExpectationsTest {
13+
bindingset[this]
14+
RoutingTest() { any() }
15+
16+
abstract string flowTag();
17+
18+
abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode);
19+
20+
override string getARelevantTag() { result in ["func", this.flowTag()] }
21+
22+
override predicate hasActualResult(Location location, string element, string tag, string value) {
23+
exists(DataFlow::Node fromNode, DataFlow::Node toNode | this.relevantFlow(fromNode, toNode) |
24+
location = fromNode.getLocation() and
25+
element = fromNode.toString() and
26+
(
27+
tag = this.flowTag() and
28+
value = "\"" + prettyNode(fromNode).replaceAll("\"", "'") + "\""
29+
or
30+
tag = "func" and
31+
value = toNode.getEnclosingCallable().getCallableValue().getScope().getQualifiedName() // TODO: More robust pretty printing?
32+
)
33+
)
34+
}
35+
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
def obfuscated_id(x): #$ step="FunctionExpr -> GSSA Variable obfuscated_id" step="x -> SSA variable x"
2-
y = x #$ step="x -> SSA variable y" step="SSA variable x, l:1 -> x"
3-
z = y #$ step="y -> SSA variable z" step="SSA variable y, l:2 -> y"
4-
return z #$ flow="42, l:6 -> z" step="SSA variable z, l:3 -> z"
2+
y = x #$ step="x -> SSA variable y" step="SSA variable x, l:-1 -> x"
3+
z = y #$ step="y -> SSA variable z" step="SSA variable y, l:-1 -> y"
4+
return z #$ flow="42, l:+2 -> z" step="SSA variable z, l:-1 -> z"
55

66
a = 42 #$ step="42 -> GSSA Variable a"
7-
b = obfuscated_id(a) #$ flow="42, l:6 -> GSSA Variable b" flow="FunctionExpr, l:1 -> obfuscated_id" step="obfuscated_id(..) -> GSSA Variable b" step="GSSA Variable obfuscated_id, l:1 -> obfuscated_id" step="GSSA Variable a, l:6 -> a"
7+
b = obfuscated_id(a) #$ flow="42, l:-1 -> GSSA Variable b" flow="FunctionExpr, l:-6 -> obfuscated_id" step="obfuscated_id(..) -> GSSA Variable b" step="GSSA Variable obfuscated_id, l:-6 -> obfuscated_id" step="GSSA Variable a, l:-1 -> a"

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

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -72,78 +72,78 @@ def argument_passing(
7272
f,
7373
**g,
7474
):
75-
SINK1(a) #$ arg1="arg1, l:89 -> a" arg1="arg1, l:94 -> a"
76-
SINK2(b) #$ arg2="arg2, l:94 -> b" MISSING:arg2="arg2, l:89 -> b"
77-
SINK3(c) #$ arg3="arg3, l:94 -> c" MISSING: arg3="arg3, l:89 -> c"
78-
SINK4(d) #$ MISSING: arg4="arg4, l:89 -> d"
79-
SINK5(e) #$ MISSING: arg5="arg5, l:89 -> e"
80-
SINK6(f) #$ MISSING: arg6="arg6, l:89 -> f"
75+
SINK1(a)
76+
SINK2(b)
77+
SINK3(c)
78+
SINK4(d)
79+
SINK5(e)
80+
SINK6(f)
8181
try:
82-
SINK7(g["g"]) #$ arg7="arg7, l:89 -> g['g']"
82+
SINK7(g["g"])
8383
except:
8484
print("OK")
8585

8686

8787
@expects(7)
8888
def test_argument_passing1():
89-
argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7})
89+
argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7}) #$ arg1="arg1" arg7="arg7" func=argument_passing MISSING: arg2="arg2" arg3="arg3 arg4="arg4" arg5="arg5" arg6="arg6"
9090

9191

9292
@expects(7)
9393
def test_argument_passing2():
94-
argument_passing(arg1, arg2, arg3, f=arg6)
94+
argument_passing(arg1, arg2, arg3, f=arg6) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=argument_passing
9595

9696

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

101101

102102
@expects(6)
103103
def test_pos_only():
104-
with_pos_only(arg1, arg2)
105-
with_pos_only(arg1, b=arg2)
106-
with_pos_only(arg1, *(arg2,))
104+
with_pos_only(arg1, arg2) #$ arg1="arg1" arg2="arg2" func=with_pos_only
105+
with_pos_only(arg1, b=arg2) #$ arg1="arg1" arg2="arg2" func=with_pos_only
106+
with_pos_only(arg1, *(arg2,)) #$ arg1="arg1" func=with_pos_only MISSING: arg2="arg2"
107107

108108

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

114114

115115
@expects(9)
116116
def test_multiple_kw_args():
117-
with_multiple_kw_args(b=arg2, c=arg3, a=arg1)
118-
with_multiple_kw_args(arg1, *(arg2,), arg3)
119-
with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2)
120-
with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1})
117+
with_multiple_kw_args(b=arg2, c=arg3, a=arg1) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=with_multiple_kw_args
118+
with_multiple_kw_args(arg1, *(arg2,), arg3) #$ arg1="arg1" func=with_multiple_kw_args MISSING: arg2="arg2" arg3="arg3"
119+
with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2) #$ arg1="arg1" arg3="arg3" func=with_multiple_kw_args MISSING: arg2="arg2"
120+
with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1}) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=with_multiple_kw_args
121121

122122

123-
def with_default_arguments(a=arg1, b=arg2, c=arg3):
124-
SINK1(a) #$ arg1="arg1, l:132 -> a" MISSING:arg1="arg1, l:123 -> a"
125-
SINK2(b) #$ arg2="arg2, l:133 -> b" MISSING: arg2="arg2, l:123 -> b"
126-
SINK3(c) #$ arg3="arg3, l:134 -> c" MISSING: arg3="arg3, l:123 -> c"
123+
def with_default_arguments(a=arg1, b=arg2, c=arg3): # Need a mechanism to test default arguments
124+
SINK1(a)
125+
SINK2(b)
126+
SINK3(c)
127127

128128

129129
@expects(12)
130130
def test_default_arguments():
131131
with_default_arguments()
132-
with_default_arguments(arg1)
133-
with_default_arguments(b=arg2)
134-
with_default_arguments(**{"c": arg3})
132+
with_default_arguments(arg1) #$ arg1="arg1" func=with_default_arguments
133+
with_default_arguments(b=arg2) #$ arg2="arg2" func=with_default_arguments
134+
with_default_arguments(**{"c": arg3}) #$ arg3="arg3" func=with_default_arguments
135135

136136

137137
# Nested constructor pattern
138138
def grab_foo_bar_baz(foo, **kwargs):
139-
SINK1(foo) #$ arg1="arg1, l:160 -> foo"
139+
SINK1(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) #$ arg2="arg2, l:160 -> bar"
146+
SINK2(bar)
147147
try:
148148
SINK2_F(kwargs["bar"])
149149
except:
@@ -152,60 +152,60 @@ def grab_bar_baz(bar, **kwargs):
152152

153153

154154
def grab_baz(baz):
155-
SINK3(baz) #$ arg3="arg3, l:160 -> baz"
155+
SINK3(baz)
156156

157157

158158
@expects(4)
159159
def test_grab():
160-
grab_foo_bar_baz(baz=arg3, bar=arg2, foo=arg1)
160+
grab_foo_bar_baz(baz=arg3, bar=arg2, foo=arg1) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=grab_foo_bar_baz func=grab_bar_baz func=grab_baz
161161

162162

163163
# All combinations
164164
def test_pos_pos():
165165
def with_pos(a):
166-
SINK1(a) #$ arg1="arg1, l:168 -> a"
166+
SINK1(a)
167167

168-
with_pos(arg1)
168+
with_pos(arg1) #$ arg1="arg1" func=test_pos_pos.with_pos
169169

170170

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

175-
with_pos_only(arg1)
175+
with_pos_only(arg1) #$ arg1="arg1" func=test_pos_pos_only.with_pos_only
176176

177177

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

183-
with_star(arg1)
183+
with_star(arg1) #$ arg1="arg1" func=test_pos_star.with_star
184184

185185

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

190-
with_kw(arg1)
190+
with_kw(arg1) #$ arg1="arg1" func=test_pos_kw.with_kw
191191

192192

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

197-
with_pos(a=arg1)
197+
with_pos(a=arg1) #$ arg1="arg1" func=test_kw_pos.with_pos
198198

199199

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

204-
with_kw(a=arg1)
204+
with_kw(a=arg1) #$ arg1="arg1" func=test_kw_kw.with_kw
205205

206206

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

211-
with_doublestar(a=arg1)
211+
with_doublestar(a=arg1) #$ arg1="arg1" func=test_kw_doublestar.with_doublestar

python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import python
22
import semmle.python.dataflow.new.DataFlow
33
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
4-
import experimental.dataflow.TestUtil.FlowTest
4+
import experimental.dataflow.TestUtil.RoutingTest
55

6-
class Argument1RoutingTest extends FlowTest {
6+
class Argument1RoutingTest extends RoutingTest {
77
Argument1RoutingTest() { this = "Argument1RoutingTest" }
88

99
override string flowTag() { result = "arg1" }
@@ -46,7 +46,7 @@ class Argument1RoutingConfig extends DataFlow::Configuration {
4646
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
4747
}
4848

49-
class Argument2RoutingTest extends FlowTest {
49+
class Argument2RoutingTest extends RoutingTest {
5050
Argument2RoutingTest() { this = "Argument2RoutingTest" }
5151

5252
override string flowTag() { result = "arg2" }
@@ -82,7 +82,7 @@ class Argument2RoutingConfig extends DataFlow::Configuration {
8282
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
8383
}
8484

85-
class Argument3RoutingTest extends FlowTest {
85+
class Argument3RoutingTest extends RoutingTest {
8686
Argument3RoutingTest() { this = "Argument3RoutingTest" }
8787

8888
override string flowTag() { result = "arg3" }
@@ -118,7 +118,7 @@ class Argument3RoutingConfig extends DataFlow::Configuration {
118118
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
119119
}
120120

121-
class Argument4RoutingTest extends FlowTest {
121+
class Argument4RoutingTest extends RoutingTest {
122122
Argument4RoutingTest() { this = "Argument4RoutingTest" }
123123

124124
override string flowTag() { result = "arg4" }
@@ -154,7 +154,7 @@ class Argument4RoutingConfig extends DataFlow::Configuration {
154154
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
155155
}
156156

157-
class Argument5RoutingTest extends FlowTest {
157+
class Argument5RoutingTest extends RoutingTest {
158158
Argument5RoutingTest() { this = "Argument5RoutingTest" }
159159

160160
override string flowTag() { result = "arg5" }
@@ -190,7 +190,7 @@ class Argument5RoutingConfig extends DataFlow::Configuration {
190190
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
191191
}
192192

193-
class Argument6RoutingTest extends FlowTest {
193+
class Argument6RoutingTest extends RoutingTest {
194194
Argument6RoutingTest() { this = "Argument6RoutingTest" }
195195

196196
override string flowTag() { result = "arg6" }
@@ -226,7 +226,7 @@ class Argument6RoutingConfig extends DataFlow::Configuration {
226226
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
227227
}
228228

229-
class Argument7RoutingTest extends FlowTest {
229+
class Argument7RoutingTest extends RoutingTest {
230230
Argument7RoutingTest() { this = "Argument7RoutingTest" }
231231

232232
override string flowTag() { result = "arg7" }

0 commit comments

Comments
 (0)