Skip to content

Commit d83a9ef

Browse files
committed
Python: Fix global field-flow for validTest.py
1 parent abe4d8d commit d83a9ef

File tree

2 files changed

+190
-121
lines changed

2 files changed

+190
-121
lines changed

python/ql/test/experimental/dataflow/fieldflow/test.py

Lines changed: 12 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -164,127 +164,18 @@ def test_nested_obj_method():
164164
# Global scope
165165
# ------------------------------------------------------------------------------
166166

167-
def func_defined_before():
168-
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:+3 -> global_obj.foo"
169-
170-
global_obj = MyObj(NONSOURCE)
171-
global_obj.foo = SOURCE
172-
SINK(global_obj.foo) # $ flow="SOURCE, l:-1 -> global_obj.foo"
173-
174-
def func_defined_after():
175-
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:-4 -> global_obj.foo"
176-
177-
@expects(2)
178-
def test_global_funcs():
179-
func_defined_before()
180-
func_defined_after()
181-
182-
# ------------------------------------------------------------------------------
183-
# All the other tests, but also in global scope.
167+
# since these are defined on global scope, and we still want to run them with
168+
# `validTest.py`, we have them defined in a different file, and have hardcoded this
169+
# number that reflects how many OK we expect to see ... Not an ideal solution, but at
170+
# least we know that the tests are actually valid.
184171
#
185-
# You might think that these are just the same... but it turns out they are not :O
186-
# ------------------------------------------------------------------------------
187-
188-
189-
myobj = MyObj("OK")
190-
191-
setFoo(myobj, SOURCE)
192-
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
193-
194-
195-
196-
myobj = MyObj("OK")
197-
198-
myobj.setFoo(SOURCE)
199-
SINK(myobj.foo) # $ MISSING: flow
200-
201-
202-
203-
myobj = MyObj(NONSOURCE)
204-
myobj.foo = SOURCE
205-
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
206-
207-
208-
209-
myobj = MyObj(NONSOURCE)
210-
myobj.foo = SOURCE
211-
myobj.foo = NONSOURCE
212-
SINK_F(myobj.foo)
213-
214-
215-
216-
myobj = MyObj(NONSOURCE)
217-
myobj.foo = SOURCE
218-
if cond:
219-
myobj.foo = NONSOURCE
220-
SINK_F(myobj.foo)
221-
# SPLITTING happens here, so in one version there is flow, and in the other there isn't
222-
# that's why it has both a flow and a MISSING: flow annotation
223-
SINK(myobj.foo) # $ flow="SOURCE, l:-6 -> myobj.foo" MISSING: flow
224-
225-
226-
227-
myobj = MyObj(NONSOURCE)
228-
myobj.foo = SOURCE
229-
if cond:
230-
myobj.foo = NONSOURCE
231-
SINK_F(myobj.foo)
232-
else:
233-
myobj.foo = NONSOURCE
234-
SINK_F(myobj.foo)
235-
SINK_F(myobj.foo)
236-
237-
238-
239-
myobj = MyObj(NONSOURCE)
240-
myobj.foo = SOURCE
241-
SINK(getattr(myobj, "foo")) # $ flow="SOURCE, l:-1 -> getattr(..)"
242-
243-
244-
245-
myobj = MyObj(NONSOURCE)
246-
setattr(myobj, "foo", SOURCE)
247-
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
248-
249-
250-
251-
myobj = MyObj(NONSOURCE)
252-
setattr(myobj, "foo", SOURCE)
253-
SINK(getattr(myobj, "foo")) # $ flow="SOURCE, l:-1 -> getattr(..)"
254-
255-
256-
257-
myobj = MyObj(NONSOURCE)
258-
setattr(myobj, "foo", SOURCE)
259-
setattr(myobj, "foo", NONSOURCE)
260-
SINK_F(getattr(myobj, "foo"))
261-
262-
263-
264-
obj2 = MyObj(SOURCE)
265-
SINK(obj2.foo) # $ MISSING: flow="SOURCE, l:-1 -> obj2.foo"
266-
267-
268-
269-
obj3 = MyObj(foo=SOURCE)
270-
SINK(obj3.foo) # $ MISSING: flow="SOURCE, l:-1 -> obj3.foo"
271-
272-
273-
SINK(fields_with_local_flow(SOURCE)) # $ MISSING: flow="SOURCE -> fields_with_local_flow(..)"
274-
275-
# ------------------------------------------------------------------------------
276-
# Nested Object
277-
# ------------------------------------------------------------------------------
278-
279-
280-
x = SOURCE
281-
a = NestedObj()
282-
a.obj.foo = x
283-
SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo"
284-
172+
# Notice that since the tests are run in a random order, we cannot split the global
173+
# scope tests into multiple functions, since we wouldn't know which one did the initial
174+
# import that does all the printing :|
285175

176+
@expects(18 + 2)
177+
def test_global_scope():
178+
import fieldflow.test_global
286179

287-
x = SOURCE
288-
a = NestedObj()
289-
a.getObj().foo = x
290-
SINK(a.obj.foo) # $ MISSING: flow
180+
fieldflow.test_global.func_defined_before()
181+
fieldflow.test_global.func_defined_after()
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""
2+
This file contains a copy of the tests from `test.py` along with some cases that check
3+
the interaction between global variables and assignment on global scope.
4+
5+
You might think that these are a bit useless since field-flow should work just the same
6+
on global or non-global scope, but then you would be wrong!
7+
"""
8+
9+
# These are defined so that we can evaluate the test code.
10+
NONSOURCE = "not a source"
11+
SOURCE = "source"
12+
13+
14+
def is_source(x):
15+
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
16+
17+
18+
def SINK(x):
19+
if is_source(x):
20+
print("OK")
21+
else:
22+
print("Unexpected flow", x)
23+
24+
25+
def SINK_F(x):
26+
if is_source(x):
27+
print("Unexpected flow", x)
28+
else:
29+
print("OK")
30+
31+
32+
# ------------------------------------------------------------------------------
33+
# Actual tests
34+
# ------------------------------------------------------------------------------
35+
36+
class MyObj(object):
37+
def __init__(self, foo):
38+
self.foo = foo
39+
40+
def setFoo(self, foo):
41+
self.foo = foo
42+
43+
def setFoo(obj, x):
44+
SINK_F(obj.foo)
45+
obj.foo = x
46+
47+
48+
myobj = MyObj("OK")
49+
50+
setFoo(myobj, SOURCE)
51+
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
52+
53+
54+
55+
myobj = MyObj("OK")
56+
57+
myobj.setFoo(SOURCE)
58+
SINK(myobj.foo) # $ MISSING: flow
59+
60+
61+
62+
myobj = MyObj(NONSOURCE)
63+
myobj.foo = SOURCE
64+
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
65+
66+
67+
68+
myobj = MyObj(NONSOURCE)
69+
myobj.foo = SOURCE
70+
myobj.foo = NONSOURCE
71+
SINK_F(myobj.foo)
72+
73+
74+
# this way, our analysis isn't able to understand that `cond` is just False,
75+
# and therefore isn't able to determine that the if below will not hold.
76+
cond = eval("False")
77+
78+
myobj = MyObj(NONSOURCE)
79+
myobj.foo = SOURCE
80+
if cond:
81+
myobj.foo = NONSOURCE
82+
SINK_F(myobj.foo)
83+
# SPLITTING happens here, so in one version there is flow, and in the other there isn't
84+
# that's why it has both a flow and a MISSING: flow annotation
85+
SINK(myobj.foo) # $ flow="SOURCE, l:-6 -> myobj.foo" MISSING: flow
86+
87+
88+
89+
myobj = MyObj(NONSOURCE)
90+
myobj.foo = SOURCE
91+
if cond:
92+
myobj.foo = NONSOURCE
93+
SINK_F(myobj.foo)
94+
else:
95+
myobj.foo = NONSOURCE
96+
SINK_F(myobj.foo)
97+
SINK_F(myobj.foo)
98+
99+
100+
101+
myobj = MyObj(NONSOURCE)
102+
myobj.foo = SOURCE
103+
SINK(getattr(myobj, "foo")) # $ flow="SOURCE, l:-1 -> getattr(..)"
104+
105+
106+
107+
myobj = MyObj(NONSOURCE)
108+
setattr(myobj, "foo", SOURCE)
109+
SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo"
110+
111+
112+
113+
myobj = MyObj(NONSOURCE)
114+
setattr(myobj, "foo", SOURCE)
115+
SINK(getattr(myobj, "foo")) # $ flow="SOURCE, l:-1 -> getattr(..)"
116+
117+
118+
119+
myobj = MyObj(NONSOURCE)
120+
setattr(myobj, "foo", SOURCE)
121+
setattr(myobj, "foo", NONSOURCE)
122+
SINK_F(getattr(myobj, "foo"))
123+
124+
125+
126+
obj2 = MyObj(SOURCE)
127+
SINK(obj2.foo) # $ MISSING: flow="SOURCE, l:-1 -> obj2.foo"
128+
129+
130+
131+
obj3 = MyObj(foo=SOURCE)
132+
SINK(obj3.foo) # $ MISSING: flow="SOURCE, l:-1 -> obj3.foo"
133+
134+
135+
136+
def fields_with_local_flow(x):
137+
obj = MyObj(x)
138+
a = obj.foo
139+
return a
140+
141+
SINK(fields_with_local_flow(SOURCE)) # $ MISSING: flow="SOURCE -> fields_with_local_flow(..)"
142+
143+
# ------------------------------------------------------------------------------
144+
# Nested Object
145+
# ------------------------------------------------------------------------------
146+
147+
class NestedObj(object):
148+
def __init__(self):
149+
self.obj = MyObj("OK")
150+
151+
def getObj(self):
152+
return self.obj
153+
154+
x = SOURCE
155+
a = NestedObj()
156+
a.obj.foo = x
157+
SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo"
158+
159+
160+
161+
x = SOURCE
162+
a = NestedObj()
163+
a.getObj().foo = x
164+
SINK(a.obj.foo) # $ MISSING: flow
165+
166+
# ------------------------------------------------------------------------------
167+
# Global scope interaction
168+
# ------------------------------------------------------------------------------
169+
170+
def func_defined_before():
171+
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:+3 -> global_obj.foo"
172+
173+
global_obj = MyObj(NONSOURCE)
174+
global_obj.foo = SOURCE
175+
SINK(global_obj.foo) # $ flow="SOURCE, l:-1 -> global_obj.foo"
176+
177+
def func_defined_after():
178+
SINK(global_obj.foo) # $ MISSING: flow="SOURCE, l:-4 -> global_obj.foo"

0 commit comments

Comments
 (0)