mirror of
https://github.com/github/codeql.git
synced 2026-04-24 16:25:15 +02:00
Merge pull request #13113 from yoff/python/test-container-steps
python: Add tests for container steps
This commit is contained in:
357
python/ql/test/experimental/dataflow/coverage/test_builtins.py
Normal file
357
python/ql/test/experimental/dataflow/coverage/test_builtins.py
Normal file
@@ -0,0 +1,357 @@
|
||||
# This tests some of the common built-in functions and methods.
|
||||
# We need a decent model of data flow through these in order to
|
||||
# analyse most programs.
|
||||
#
|
||||
# All functions starting with "test_" should run and execute `print("OK")` exactly once.
|
||||
# This can be checked by running validTest.py.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from testlib import expects
|
||||
|
||||
# These are defined so that we can evaluate the test code.
|
||||
NONSOURCE = "not a source"
|
||||
SOURCE = "source"
|
||||
|
||||
def is_source(x):
|
||||
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
|
||||
|
||||
def SINK(x):
|
||||
if is_source(x):
|
||||
print("OK")
|
||||
else:
|
||||
print("Unexpected flow", x)
|
||||
|
||||
def SINK_F(x):
|
||||
if is_source(x):
|
||||
print("Unexpected flow", x)
|
||||
else:
|
||||
print("OK")
|
||||
|
||||
|
||||
# Actual tests
|
||||
|
||||
## Container constructors
|
||||
|
||||
### List
|
||||
|
||||
@expects(2)
|
||||
def test_list_from_list():
|
||||
l1 = [SOURCE, NONSOURCE]
|
||||
l2 = list(l1)
|
||||
SINK(l2[0]) #$ MISSING: flow="SOURCE, l:-2 -> l2[0]"
|
||||
SINK_F(l2[1]) # expecting FP due to imprecise flow
|
||||
|
||||
# -- skip list_from_string
|
||||
|
||||
@expects(2)
|
||||
def test_list_from_tuple():
|
||||
t = (SOURCE, NONSOURCE)
|
||||
l = list(t)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
SINK_F(l[1]) # expecting FP due to imprecise flow
|
||||
|
||||
def test_list_from_set():
|
||||
s = {SOURCE}
|
||||
l = list(s)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
@expects(2)
|
||||
def test_list_from_dict():
|
||||
d = {SOURCE: 'v', NONSOURCE: 'v2'}
|
||||
l = list(d)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
SINK_F(l[1]) # expecting FP due to imprecise flow
|
||||
|
||||
### Tuple
|
||||
|
||||
@expects(2)
|
||||
def test_tuple_from_list():
|
||||
l = [SOURCE, NONSOURCE]
|
||||
t = tuple(l)
|
||||
SINK(t[0]) #$ MISSING: flow="SOURCE, l:-2 -> t[0]"
|
||||
SINK_F(t[1])
|
||||
|
||||
@expects(2)
|
||||
def test_tuple_from_tuple():
|
||||
t0 = (SOURCE, NONSOURCE)
|
||||
t = tuple(t0)
|
||||
SINK(t[0]) #$ MISSING: flow="SOURCE, l:-2 -> t[0]"
|
||||
SINK_F(t[1])
|
||||
|
||||
def test_tuple_from_set():
|
||||
s = {SOURCE}
|
||||
t = tuple(s)
|
||||
SINK(t[0]) #$ MISSING: flow="SOURCE, l:-2 -> t[0]"
|
||||
|
||||
@expects(2)
|
||||
def test_tuple_from_dict():
|
||||
d = {SOURCE: "v1", NONSOURCE: "v2"}
|
||||
t = tuple(d)
|
||||
SINK(t[0]) #$ MISSING: flow="SOURCE, l:-2 -> t[0]"
|
||||
SINK_F(t[1])
|
||||
|
||||
|
||||
### Set
|
||||
|
||||
def test_set_from_list():
|
||||
l = [SOURCE]
|
||||
s = set(l)
|
||||
v = s.pop()
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-3 -> v"
|
||||
|
||||
def test_set_from_tuple():
|
||||
t = (SOURCE,)
|
||||
s = set(t)
|
||||
v = s.pop()
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-3 -> v"
|
||||
|
||||
def test_set_from_set():
|
||||
s0 = {SOURCE}
|
||||
s = set(s0)
|
||||
v = s.pop()
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-3 -> v"
|
||||
|
||||
def test_set_from_dict():
|
||||
d = {SOURCE: "val"}
|
||||
s = set(d)
|
||||
v = s.pop()
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-3 -> v"
|
||||
|
||||
|
||||
### Dict
|
||||
|
||||
@expects(2)
|
||||
def test_dict_from_keyword():
|
||||
d = dict(k = SOURCE, k1 = NONSOURCE)
|
||||
SINK(d["k"]) #$ MISSING: flow="SOURCE, l:-1 -> d[k]"
|
||||
SINK_F(d["k1"])
|
||||
|
||||
@expects(2)
|
||||
def test_dict_from_list():
|
||||
d = dict([("k", SOURCE), ("k1", NONSOURCE)])
|
||||
SINK(d["k"]) #$ MISSING: flow="SOURCE, l:-1 -> d[k]"
|
||||
SINK_F(d["k1"])
|
||||
|
||||
@expects(2)
|
||||
def test_dict_from_dict():
|
||||
d1 = {'k': SOURCE, 'k1': NONSOURCE}
|
||||
d2 = dict(d1)
|
||||
SINK(d2["k"]) #$ MISSING: flow="SOURCE, l:-2 -> d[k]"
|
||||
SINK_F(d2["k1"])
|
||||
|
||||
## Container methods
|
||||
|
||||
### List
|
||||
|
||||
def test_list_pop():
|
||||
l = [SOURCE]
|
||||
v = l.pop()
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
|
||||
def test_list_pop_index():
|
||||
l = [SOURCE]
|
||||
v = l.pop(0)
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-2 -> v"
|
||||
|
||||
def test_list_pop_index_imprecise():
|
||||
l = [SOURCE, NONSOURCE]
|
||||
v = l.pop(1)
|
||||
SINK_F(v)
|
||||
|
||||
@expects(2)
|
||||
def test_list_copy():
|
||||
l0 = [SOURCE, NONSOURCE]
|
||||
l = l0.copy()
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
SINK_F(l[1])
|
||||
|
||||
def test_list_append():
|
||||
l = [NONSOURCE]
|
||||
l.append(SOURCE)
|
||||
SINK(l[1]) #$ MISSING: flow="SOURCE, l:-1 -> l[1]"
|
||||
|
||||
### Set
|
||||
|
||||
def test_set_pop():
|
||||
s = {SOURCE}
|
||||
v = s.pop()
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
|
||||
def test_set_copy():
|
||||
s0 = {SOURCE}
|
||||
s = s0.copy()
|
||||
SINK(s.pop()) #$ MISSING: flow="SOURCE, l:-2 -> s.pop()"
|
||||
|
||||
def test_set_add():
|
||||
s = set([])
|
||||
s.add(SOURCE)
|
||||
SINK(s.pop()) #$ MISSING: flow="SOURCE, l:-2 -> s.pop()"
|
||||
|
||||
### Dict
|
||||
|
||||
def test_dict_keys():
|
||||
d = {SOURCE: "value"}
|
||||
keys = d.keys()
|
||||
key_list = list(keys)
|
||||
SINK(key_list[0]) #$ MISSING: flow="SOURCE, l:-3 -> key_list[0]"
|
||||
|
||||
def test_dict_values():
|
||||
d = {'k': SOURCE}
|
||||
vals = d.values()
|
||||
val_list = list(vals)
|
||||
SINK(val_list[0]) #$ MISSING: flow="SOURCE, l:-3 -> val_list[0]"
|
||||
|
||||
@expects(4)
|
||||
def test_dict_items():
|
||||
d = {'k': SOURCE, SOURCE: "value"}
|
||||
items = d.items()
|
||||
item_list = list(items)
|
||||
SINK_F(item_list[0][0]) # expecting FP due to imprecise flow
|
||||
SINK(item_list[0][1]) #$ MISSING: flow="SOURCE, l:-4 -> item_list[0][1]"
|
||||
SINK(item_list[1][0]) #$ MISSING: flow="SOURCE, l:-5 -> item_list[1][0]"
|
||||
SINK_F(item_list[1][1]) # expecting FP due to imprecise flow
|
||||
|
||||
@expects(3)
|
||||
def test_dict_pop():
|
||||
d = {'k': SOURCE}
|
||||
v = d.pop("k")
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
v1 = d.pop("k", NONSOURCE)
|
||||
SINK_F(v1) #$ SPURIOUS: flow="SOURCE, l:-4 -> v1"
|
||||
v2 = d.pop("non-existing", SOURCE)
|
||||
SINK(v2) #$ MISSING: flow="SOURCE, l:-1 -> v2"
|
||||
|
||||
@expects(2)
|
||||
def test_dict_get():
|
||||
d = {'k': SOURCE}
|
||||
v = d.get("k")
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
v1 = d.get("non-existing", SOURCE)
|
||||
SINK(v1) #$ MISSING: flow="SOURCE, l:-1 -> v1"
|
||||
|
||||
@expects(2)
|
||||
def test_dict_popitem():
|
||||
d = {'k': SOURCE}
|
||||
t = d.popitem() # could be any pair (before 3.7), but we only have one
|
||||
SINK_F(t[0])
|
||||
SINK(t[1]) #$ MISSING: flow="SOURCE, l:-3 -> t[1]"
|
||||
|
||||
@expects(2)
|
||||
def test_dict_copy():
|
||||
d = {'k': SOURCE, 'k1': NONSOURCE}
|
||||
d1 = d.copy()
|
||||
SINK(d1["k"]) #$ MISSING: flow="SOURCE, l:-2 -> d[k]"
|
||||
SINK_F(d1["k1"])
|
||||
|
||||
|
||||
## Functions on containers
|
||||
|
||||
### sorted
|
||||
|
||||
def test_sorted_list():
|
||||
l0 = [SOURCE]
|
||||
l = sorted(l0)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
def test_sorted_tuple():
|
||||
t = (SOURCE,)
|
||||
l = sorted(t)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
def test_sorted_set():
|
||||
s = {SOURCE}
|
||||
l = sorted(s)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
def test_sorted_dict():
|
||||
d = {SOURCE: "val"}
|
||||
l = sorted(d)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
### reversed
|
||||
|
||||
@expects(2)
|
||||
def test_reversed_list():
|
||||
l0 = [SOURCE, NONSOURCE]
|
||||
r = reversed(l0)
|
||||
l = list(r)
|
||||
SINK_F(l[0])
|
||||
SINK(l[1]) #$ MISSING: flow="SOURCE, l:-4 -> l[1]"
|
||||
|
||||
@expects(2)
|
||||
def test_reversed_tuple():
|
||||
t = (SOURCE, NONSOURCE)
|
||||
r = reversed(t)
|
||||
l = list(r)
|
||||
SINK_F(l[0])
|
||||
SINK(l[1]) #$ MISSING: flow="SOURCE, l:-4 -> l[1]"
|
||||
|
||||
@expects(2)
|
||||
def test_reversed_dict():
|
||||
d = {SOURCE: "v1", NONSOURCE: "v2"}
|
||||
r = reversed(d)
|
||||
l = list(r)
|
||||
SINK_F(l[0])
|
||||
SINK(l[1]) #$ MISSING: flow="SOURCE, l:-4 -> l[1]"
|
||||
|
||||
### iter
|
||||
|
||||
def test_iter_list():
|
||||
l0 = [SOURCE]
|
||||
i = iter(l0)
|
||||
l = list(i)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-3 -> l[0]"
|
||||
|
||||
def test_iter_tuple():
|
||||
t = (SOURCE,)
|
||||
i = iter(t)
|
||||
l = list(i)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-3 -> l[0]"
|
||||
|
||||
def test_iter_set():
|
||||
t = {SOURCE}
|
||||
i = iter(t)
|
||||
l = list(i)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-3 -> l[0]"
|
||||
|
||||
def test_iter_dict():
|
||||
d = {SOURCE: "val"}
|
||||
i = iter(d)
|
||||
l = list(i)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-3 -> l[0]"
|
||||
|
||||
def test_iter_iter():
|
||||
# applying iter() to the result of iter() is basically a no-op
|
||||
l0 = [SOURCE]
|
||||
i = iter(iter(l0))
|
||||
l = list(i)
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-3 -> l[0]"
|
||||
|
||||
### next
|
||||
|
||||
def test_next_list():
|
||||
l = [SOURCE]
|
||||
i = iter(l)
|
||||
n = next(i)
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
|
||||
def test_next_tuple():
|
||||
t = (SOURCE,)
|
||||
i = iter(t)
|
||||
n = next(i)
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
|
||||
def test_next_set():
|
||||
s = {SOURCE}
|
||||
i = iter(s)
|
||||
n = next(i)
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
|
||||
def test_next_dict():
|
||||
d = {SOURCE: "val"}
|
||||
i = iter(d)
|
||||
n = next(i)
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
@@ -25,13 +25,17 @@ uniqueParameterNodePosition
|
||||
uniqueContentApprox
|
||||
identityLocalStep
|
||||
| test_async.py:48:9:48:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:56:10:56:21 | ControlFlowNode for tainted_list | Node steps to itself |
|
||||
| test_collections.py:63:9:63:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:65:9:65:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:79:9:79:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:81:9:81:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:64:10:64:21 | ControlFlowNode for tainted_list | Node steps to itself |
|
||||
| test_collections.py:71:9:71:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:73:9:73:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:88:10:88:21 | ControlFlowNode for tainted_list | Node steps to itself |
|
||||
| test_collections.py:89:10:89:23 | ControlFlowNode for TAINTED_STRING | Node steps to itself |
|
||||
| test_collections.py:97:9:97:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:99:9:99:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:112:9:112:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:114:9:114:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:116:9:116:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:213:9:213:15 | ControlFlowNode for my_dict | Node steps to itself |
|
||||
| test_collections.py:213:22:213:33 | ControlFlowNode for tainted_dict | Node steps to itself |
|
||||
| test_collections.py:147:9:147:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:149:9:149:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
| test_collections.py:246:9:246:15 | ControlFlowNode for my_dict | Node steps to itself |
|
||||
| test_collections.py:246:22:246:33 | ControlFlowNode for tainted_dict | Node steps to itself |
|
||||
| test_for.py:24:9:24:22 | ControlFlowNode for ensure_tainted | Node steps to itself |
|
||||
|
||||
@@ -37,6 +37,14 @@ def test_construction():
|
||||
tuple(tainted_list), # $ tainted
|
||||
set(tainted_list), # $ tainted
|
||||
frozenset(tainted_list), # $ tainted
|
||||
dict(tainted_dict), # $ tainted
|
||||
dict(k = tainted_string)["k"], # $ MISSING: tainted
|
||||
dict(dict(k = tainted_string))["k"], # $ MISSING: tainted
|
||||
dict(["k", tainted_string]), # $ tainted
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
dict(k = tainted_string)["k1"]
|
||||
)
|
||||
|
||||
|
||||
@@ -64,6 +72,31 @@ def test_access(x, y, z):
|
||||
for i in reversed(tainted_list):
|
||||
ensure_tainted(i) # $ tainted
|
||||
|
||||
def test_access_explicit(x, y, z):
|
||||
tainted_list = [TAINTED_STRING]
|
||||
|
||||
ensure_tainted(
|
||||
tainted_list[0], # $ tainted
|
||||
tainted_list[x], # $ tainted
|
||||
tainted_list[y:z], # $ tainted
|
||||
|
||||
sorted(tainted_list)[0], # $ tainted
|
||||
reversed(tainted_list)[0], # $ tainted
|
||||
iter(tainted_list), # $ tainted
|
||||
next(iter(tainted_list)), # $ tainted
|
||||
[i for i in tainted_list], # $ tainted
|
||||
[tainted_list for i in [1,2,3]], # $ MISSING: tainted
|
||||
[TAINTED_STRING for i in [1,2,3]], # $ tainted
|
||||
[tainted_list], # $ tainted
|
||||
)
|
||||
|
||||
a, b, c = tainted_list[0:3]
|
||||
ensure_tainted(a, b, c) # $ tainted
|
||||
|
||||
for h in tainted_list:
|
||||
ensure_tainted(h) # $ tainted
|
||||
for i in reversed(tainted_list):
|
||||
ensure_tainted(i) # $ tainted
|
||||
|
||||
def test_dict_access(x):
|
||||
tainted_dict = TAINTED_DICT
|
||||
|
||||
@@ -64,6 +64,7 @@ if __name__ == "__main__":
|
||||
check_tests_valid("coverage.test")
|
||||
check_tests_valid("coverage.argumentPassing")
|
||||
check_tests_valid("coverage.datamodel")
|
||||
check_tests_valid("coverage.test_builtins")
|
||||
check_tests_valid("coverage-py2.classes")
|
||||
check_tests_valid("coverage-py3.classes")
|
||||
check_tests_valid("variable-capture.in")
|
||||
|
||||
Reference in New Issue
Block a user