Python: Factor out prettyPrinter and update tests

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-11-24 02:17:38 +01:00
parent dc91406ff0
commit a19304a4a0
16 changed files with 190 additions and 174 deletions

View File

@@ -1,6 +1,7 @@
import python
import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
import experimental.dataflow.TestUtil.PrintNode
abstract class FlowTest extends InlineExpectationsTest {
bindingset[this]
@@ -17,7 +18,8 @@ abstract class FlowTest extends InlineExpectationsTest {
location = toNode.getLocation() and
tag = this.flowTag() and
value =
"\"" + fromNode.toString() + lineStr(fromNode, toNode) + " -> " + toNode.toString() + "\"" and
"\"" + prettyNode(fromNode).replaceAll("\"", "'") + lineStr(fromNode, toNode) + " -> " +
prettyNode(toNode).replaceAll("\"", "'") + "\"" and
element = toNode.toString()
)
}

View File

@@ -1,5 +1,6 @@
import python
import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate
import FlowTest
class MaximalFlowTest extends FlowTest {
@@ -21,14 +22,21 @@ class MaximalFlowsConfig extends DataFlow::Configuration {
MaximalFlowsConfig() { this = "MaximalFlowsConfig" }
override predicate isSource(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
not node.asCfgNode() instanceof CallNode and
not node.asCfgNode().getNode() instanceof Return and
not node instanceof DataFlow::ParameterNode and
not node instanceof DataFlow::PostUpdateNode and
// not node.asExpr() instanceof FunctionExpr and
// not node.asExpr() instanceof ClassExpr and
not exists(DataFlow::Node pred | DataFlow::localFlowStep(pred, node))
}
override predicate isSink(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and
not any(CallNode c).getArg(_) = node.asCfgNode() and
not node instanceof ArgumentNode and
not node.asCfgNode().(NameNode).getId().matches("SINK%") and
not exists(DataFlow::Node succ | DataFlow::localFlowStep(node, succ))
}
}

View File

@@ -0,0 +1,31 @@
import python
import semmle.python.dataflow.new.DataFlow
string prettyExp(Expr e) {
not e instanceof Num and
not e instanceof StrConst and
not e instanceof Subscript and
not e instanceof Call and
not e instanceof Attribute and
result = e.toString()
or
result = e.(Num).getN()
or
result =
e.(StrConst).getPrefix() + e.(StrConst).getText() +
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
or
result = prettyExp(e.(Subscript).getObject()) + "[" + prettyExp(e.(Subscript).getIndex()) + "]"
or
(
if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg())
then result = prettyExp(e.(Call).getFunc()) + "(..)"
else result = prettyExp(e.(Call).getFunc()) + "()"
)
or
result = prettyExp(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
}
string prettyNode(DataFlow::Node node) {
if exists(node.asExpr()) then result = prettyExp(node.asExpr()) else result = node.toString()
}

View File

@@ -1 +1 @@
import experimental.dataflow.FlowTestUtil.LocalFlowStepTest
import experimental.dataflow.TestUtil.LocalFlowStepTest

View File

@@ -1 +1 @@
import experimental.dataflow.FlowTestUtil.MaximalFlowTest
import experimental.dataflow.TestUtil.MaximalFlowTest

View File

@@ -1,7 +1,7 @@
def obfuscated_id(x): #$ step="ControlFlowNode for FunctionExpr -> GSSA Variable obfuscated_id"
y = x #$ step="ControlFlowNode for x -> SSA variable y" step="SSA variable x, l:1 -> ControlFlowNode for x"
z = y #$ step="ControlFlowNode for y -> SSA variable z" step="SSA variable y, l:2 -> ControlFlowNode for y"
return z #$ flow="ControlFlowNode for IntegerLiteral, l:6 -> ControlFlowNode for z" step="SSA variable z, l:3 -> ControlFlowNode for z"
def obfuscated_id(x): #$ step="FunctionExpr -> GSSA Variable obfuscated_id"
y = x #$ step="x -> SSA variable y" step="SSA variable x, l:1 -> x"
z = y #$ step="y -> SSA variable z" step="SSA variable y, l:2 -> y"
return z #$ flow="42, l:6 -> z" step="SSA variable z, l:3 -> z"
a = 42 #$ step="ControlFlowNode for IntegerLiteral -> GSSA Variable a"
b = obfuscated_id(a) #$ flow="ControlFlowNode for IntegerLiteral, l:6 -> GSSA Variable b" flow="ControlFlowNode for FunctionExpr, l:1 -> ControlFlowNode for obfuscated_id" step="ControlFlowNode for obfuscated_id() -> GSSA Variable b" step="GSSA Variable obfuscated_id, l:1 -> ControlFlowNode for obfuscated_id" step="GSSA Variable a, l:6 -> ControlFlowNode for a"
a = 42 #$ step="42 -> GSSA Variable a"
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"

View File

@@ -72,14 +72,14 @@ def argument_passing(
f,
**g,
):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:89 -> ControlFlowNode for a" arg1="ControlFlowNode for arg1, l:94 -> ControlFlowNode for a"
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:94 -> ControlFlowNode for b" MISSING:arg2="ControlFlowNode for arg2, l:89 -> ControlFlowNode for b"
SINK3(c) #$ arg3="ControlFlowNode for arg3, l:94 -> ControlFlowNode for c" MISSING: arg3="ControlFlowNode for arg3, l:89 -> ControlFlowNode for c"
SINK4(d) #$ MISSING: arg4="ControlFlowNode for arg4, l:89 -> ControlFlowNode for d"
SINK5(e) #$ MISSING: arg5="ControlFlowNode for arg5, l:89 -> ControlFlowNode for e"
SINK6(f) #$ MISSING: arg6="ControlFlowNode for arg6, l:89 -> ControlFlowNode for f"
SINK1(a) #$ arg1="arg1, l:89 -> a" arg1="arg1, l:94 -> a"
SINK2(b) #$ arg2="arg2, l:94 -> b" MISSING:arg2="arg2, l:89 -> b"
SINK3(c) #$ arg3="arg3, l:94 -> c" MISSING: arg3="arg3, l:89 -> c"
SINK4(d) #$ MISSING: arg4="arg4, l:89 -> d"
SINK5(e) #$ MISSING: arg5="arg5, l:89 -> e"
SINK6(f) #$ MISSING: arg6="arg6, l:89 -> f"
try:
SINK7(g["g"]) #$ arg7="ControlFlowNode for arg7, l:89 -> ControlFlowNode for Subscript"
SINK7(g["g"]) #$ arg7="arg7, l:89 -> g['g']"
except:
print("OK")
@@ -95,8 +95,8 @@ def test_argument_passing2():
def with_pos_only(a, /, b):
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"
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"
SINK1(a) #$ arg1="arg1, l:104 -> a" arg1="arg1, l:105 -> a" arg1="arg1, l:106 -> a"
SINK2(b) #$ arg2="arg2, l:104 -> b" arg2="arg2, l:105 -> b" MISSING: arg2="arg2, l:106 -> b"
@expects(6)
@@ -107,9 +107,9 @@ def test_pos_only():
def with_multiple_kw_args(a, b, c):
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"
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"
SINK3(c) #$ arg3="ControlFlowNode for arg3, l:117 -> ControlFlowNode for c" arg3="ControlFlowNode for arg3, l:119 -> ControlFlowNode for c" arg3="ControlFlowNode for arg3, l:120 -> ControlFlowNode for c" MISSING: arg3="ControlFlowNode for arg3, l:118 -> ControlFlowNode for c"
SINK1(a) #$ arg1="arg1, l:117 -> a" arg1="arg1, l:118 -> a" arg1="arg1, l:119 -> a" arg1="arg1, l:120 -> a"
SINK2(b) #$ arg2="arg2, l:117 -> b" arg2="arg2, l:120 -> b" MISSING: arg2="arg2, l:118 -> b" arg2="arg2, l:119 -> b"
SINK3(c) #$ arg3="arg3, l:117 -> c" arg3="arg3, l:119 -> c" arg3="arg3, l:120 -> c" MISSING: arg3="arg3, l:118 -> c"
@expects(9)
@@ -121,9 +121,9 @@ def test_multiple_kw_args():
def with_default_arguments(a=arg1, b=arg2, c=arg3):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:132 -> ControlFlowNode for a" MISSING:arg1="ControlFlowNode for arg1, l:123 -> ControlFlowNode for a"
SINK2(b) #$ arg2="ControlFlowNode for arg2, l:133 -> ControlFlowNode for b" MISSING: arg2="ControlFlowNode for arg2, l:123 -> ControlFlowNode for b"
SINK3(c) #$ arg3="ControlFlowNode for arg3, l:134 -> ControlFlowNode for c" MISSING: arg3="ControlFlowNode for arg3, l:123 -> ControlFlowNode for c"
SINK1(a) #$ arg1="arg1, l:132 -> a" MISSING:arg1="arg1, l:123 -> a"
SINK2(b) #$ arg2="arg2, l:133 -> b" MISSING: arg2="arg2, l:123 -> b"
SINK3(c) #$ arg3="arg3, l:134 -> c" MISSING: arg3="arg3, l:123 -> c"
@expects(12)
@@ -136,14 +136,14 @@ def test_default_arguments():
# Nested constructor pattern
def grab_foo_bar_baz(foo, **kwargs):
SINK1(foo) #$ arg1="ControlFlowNode for arg1, l:160 -> ControlFlowNode for foo"
SINK1(foo) #$ arg1="arg1, l:160 -> foo"
grab_bar_baz(**kwargs)
# It is not possible to pass `bar` into `kwargs`,
# since `bar` is a valid keyword argument.
def grab_bar_baz(bar, **kwargs):
SINK2(bar) #$ arg2="ControlFlowNode for arg2, l:160 -> ControlFlowNode for bar"
SINK2(bar) #$ arg2="arg2, l:160 -> bar"
try:
SINK2_F(kwargs["bar"])
except:
@@ -152,7 +152,7 @@ def grab_bar_baz(bar, **kwargs):
def grab_baz(baz):
SINK3(baz) #$ arg3="ControlFlowNode for arg3, l:160 -> ControlFlowNode for baz"
SINK3(baz) #$ arg3="arg3, l:160 -> baz"
@expects(4)
@@ -163,14 +163,14 @@ def test_grab():
# All combinations
def test_pos_pos():
def with_pos(a):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:168 -> ControlFlowNode for a"
SINK1(a) #$ arg1="arg1, l:168 -> a"
with_pos(arg1)
def test_pos_pos_only():
def with_pos_only(a, /):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:175 -> ControlFlowNode for a"
SINK1(a) #$ arg1="arg1, l:175 -> a"
with_pos_only(arg1)
@@ -178,34 +178,34 @@ def test_pos_pos_only():
def test_pos_star():
def with_star(*a):
if len(a) > 0:
SINK1(a[0]) #$ arg1="ControlFlowNode for arg1, l:183 -> ControlFlowNode for Subscript"
SINK1(a[0]) #$ arg1="arg1, l:183 -> a[0]"
with_star(arg1)
def test_pos_kw():
def with_kw(a=""):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:190 -> ControlFlowNode for a"
SINK1(a) #$ arg1="arg1, l:190 -> a"
with_kw(arg1)
def test_kw_pos():
def with_pos(a):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:197 -> ControlFlowNode for a"
SINK1(a) #$ arg1="arg1, l:197 -> a"
with_pos(a=arg1)
def test_kw_kw():
def with_kw(a=""):
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:204 -> ControlFlowNode for a"
SINK1(a) #$ arg1="arg1, l:204 -> a"
with_kw(a=arg1)
def test_kw_doublestar():
def with_doublestar(**a):
SINK1(a["a"]) #$ arg1="ControlFlowNode for arg1, l:211 -> ControlFlowNode for Subscript"
SINK1(a["a"]) #$ arg1="arg1, l:211 -> a['a']"
with_doublestar(a=arg1)

View File

@@ -1,7 +1,7 @@
import python
import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
import experimental.dataflow.FlowTestUtil.FlowTest
import experimental.dataflow.TestUtil.FlowTest
class Argument1RoutingTest extends FlowTest {
Argument1RoutingTest() { this = "Argument1RoutingTest" }

View File

@@ -553,8 +553,8 @@ def test_length_hint():
# object.__getitem__(self, key)
class With_getitem:
def __getitem__(self, key):
SINK2(key) #$ arg2="ControlFlowNode for arg2, l:565 -> ControlFlowNode for key"
SINK1(self) #$ arg1="SSA variable with_getitem, l:563 -> ControlFlowNode for self"
SINK2(key) #$ arg2="arg2, l:565 -> key"
SINK1(self) #$ arg1="SSA variable with_getitem, l:563 -> self"
OK()
return ""
@@ -568,9 +568,9 @@ def test_getitem():
# object.__setitem__(self, key, value)
class With_setitem:
def __setitem__(self, key, value):
SINK3(value) #$ arg3="ControlFlowNode for arg3, l:581 -> ControlFlowNode for value"
SINK2(key) #$ arg2="ControlFlowNode for arg2, l:581 -> ControlFlowNode for key"
SINK1(self) #$ arg1="SSA variable with_setitem, l:578 -> ControlFlowNode for self"
SINK3(value) #$ arg3="arg3, l:581 -> value"
SINK2(key) #$ arg2="arg2, l:581 -> key"
SINK1(self) #$ arg1="SSA variable with_setitem, l:578 -> self"
OK()
@@ -584,8 +584,8 @@ def test_setitem():
# object.__delitem__(self, key)
class With_delitem:
def __delitem__(self, key):
SINK2(key) #$ arg2="ControlFlowNode for arg2, l:595 -> ControlFlowNode for key"
SINK1(self) #$ arg1="SSA variable with_delitem, l:593 -> ControlFlowNode for self"
SINK2(key) #$ arg2="arg2, l:595 -> key"
SINK1(self) #$ arg1="SSA variable with_delitem, l:593 -> self"
OK()
@@ -655,8 +655,8 @@ def test_contains():
# object.__add__(self, other)
class With_add:
def __add__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:667 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_add, l:665 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:667 -> other"
SINK1(self) #$ arg1="SSA variable with_add, l:665 -> self"
OK()
return self
@@ -670,8 +670,8 @@ def test_add():
# object.__sub__(self, other)
class With_sub:
def __sub__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:682 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_sub, l:680 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:682 -> other"
SINK1(self) #$ arg1="SSA variable with_sub, l:680 -> self"
OK()
return self
@@ -685,8 +685,8 @@ def test_sub():
# object.__mul__(self, other)
class With_mul:
def __mul__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:697 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_mul, l:695 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:697 -> other"
SINK1(self) #$ arg1="SSA variable with_mul, l:695 -> self"
OK()
return self
@@ -700,8 +700,8 @@ def test_mul():
# object.__matmul__(self, other)
class With_matmul:
def __matmul__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:712 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_matmul, l:710 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:712 -> other"
SINK1(self) #$ arg1="SSA variable with_matmul, l:710 -> self"
OK()
return self
@@ -715,8 +715,8 @@ def test_matmul():
# object.__truediv__(self, other)
class With_truediv:
def __truediv__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:727 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_truediv, l:725 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:727 -> other"
SINK1(self) #$ arg1="SSA variable with_truediv, l:725 -> self"
OK()
return self
@@ -730,8 +730,8 @@ def test_truediv():
# object.__floordiv__(self, other)
class With_floordiv:
def __floordiv__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:742 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_floordiv, l:740 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:742 -> other"
SINK1(self) #$ arg1="SSA variable with_floordiv, l:740 -> self"
OK()
return self
@@ -745,8 +745,8 @@ def test_floordiv():
# object.__mod__(self, other)
class With_mod:
def __mod__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:757 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_mod, l:755 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:757 -> other"
SINK1(self) #$ arg1="SSA variable with_mod, l:755 -> self"
OK()
return self
@@ -775,8 +775,8 @@ def test_divmod():
# object.__pow__(self, other[, modulo])
class With_pow:
def __pow__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:793 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_pow, l:791 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:793 -> other"
SINK1(self) #$ arg1="SSA variable with_pow, l:791 -> self"
OK()
return self
@@ -796,8 +796,8 @@ def test_pow_op():
# object.__lshift__(self, other)
class With_lshift:
def __lshift__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:808 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_lshift, l:806 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:808 -> other"
SINK1(self) #$ arg1="SSA variable with_lshift, l:806 -> self"
OK()
return self
@@ -811,8 +811,8 @@ def test_lshift():
# object.__rshift__(self, other)
class With_rshift:
def __rshift__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:823 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_rshift, l:821 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:823 -> other"
SINK1(self) #$ arg1="SSA variable with_rshift, l:821 -> self"
OK()
return self
@@ -826,8 +826,8 @@ def test_rshift():
# object.__and__(self, other)
class With_and:
def __and__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:838 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_and, l:836 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:838 -> other"
SINK1(self) #$ arg1="SSA variable with_and, l:836 -> self"
OK()
return self
@@ -841,8 +841,8 @@ def test_and():
# object.__xor__(self, other)
class With_xor:
def __xor__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:853 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_xor, l:851 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:853 -> other"
SINK1(self) #$ arg1="SSA variable with_xor, l:851 -> self"
OK()
return self
@@ -856,8 +856,8 @@ def test_xor():
# object.__or__(self, other)
class With_or:
def __or__(self, other):
SINK2(other) #$ arg2="ControlFlowNode for arg2, l:868 -> ControlFlowNode for other"
SINK1(self) #$ arg1="SSA variable with_or, l:866 -> ControlFlowNode for self"
SINK2(other) #$ arg2="arg2, l:868 -> other"
SINK1(self) #$ arg1="SSA variable with_or, l:866 -> self"
OK()
return self

View File

@@ -1,5 +1,5 @@
import python
import experimental.dataflow.FlowTestUtil.FlowTest
import experimental.dataflow.TestUtil.FlowTest
import experimental.dataflow.testConfig
class DataFlowTest extends FlowTest {

View File

@@ -35,7 +35,7 @@ def SINK_F(x):
def f(a, b):
return a
SINK(f(SOURCE, 3)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f()" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for f()"
SINK(f(SOURCE, 3)) #$ flow="SOURCE -> f(..)" flow="'source', l:13 -> f(..)"
# Instance methods
# An instance method object combines a class, a class instance and any callable object (normally a user-defined function).
@@ -68,18 +68,18 @@ c = C()
func_obj = c.method.__func__
# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1).
SINK(c.method(SOURCE, C)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:38 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for Attribute()"
SINK(C.method(c, SOURCE, C)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:38 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:71 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for Attribute()"
SINK(func_obj(c, SOURCE, C))
SINK(c.method(SOURCE, C)) #$ flow="SOURCE -> c.method(..)" flow="SOURCE, l:38 -> c.method(..)" flow="'source', l:13 -> c.method(..)"
SINK(C.method(c, SOURCE, C)) #$ flow="SOURCE -> C.method(..)" flow="SOURCE, l:38 -> C.method(..)" flow="SOURCE, l:71 -> C.method(..)" flow="'source', l:13 -> C.method(..)"
SINK(func_obj(c, SOURCE, C)) #$ MISSING: flow="SOURCE -> func_obj(..)" flow="SOURCE, l:38 -> func_obj(..)" flow="SOURCE, l:71 -> func_obj(..)" flow="SOURCE, l:72 -> func_obj(..)" flow="'source', l:13 -> func_obj()"
# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method.
c_func_obj = C.classmethod.__func__
# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function.
SINK(c.classmethod(SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:38 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:71 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:72 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:73 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for Attribute()"
SINK(C.classmethod(SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:38 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:71 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:72 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:73 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for SOURCE, l:80 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for Attribute()"
SINK(c_func_obj(C, SOURCE))
SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)" flow="SOURCE, l:38 -> c.classmethod(..)" flow="SOURCE, l:71 -> c.classmethod(..)" flow="SOURCE, l:72 -> c.classmethod(..)" flow="SOURCE, l:73 -> c.classmethod(..)" flow="'source', l:13 -> c.classmethod(..)"
SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)" flow="SOURCE, l:38 -> C.classmethod(..)" flow="SOURCE, l:71 -> C.classmethod(..)" flow="SOURCE, l:72 -> C.classmethod(..)" flow="SOURCE, l:73 -> C.classmethod(..)" flow="SOURCE, l:80 -> C.classmethod(..)" flow="'source', l:13 -> C.classmethod(..)"
SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)" flow="SOURCE, l:38 -> c_func_obj(..)" flow="SOURCE, l:71 -> c_func_obj(..)" flow="SOURCE, l:72 -> c_func_obj(..)" flow="SOURCE, l:73 -> c_func_obj(..)" flow="SOURCE, l:80 -> c_func_obj(..)" flow="SOURCE, l:81 -> c_func_obj(..)" flow="'source', l:13 -> c_func_obj()"
# Generator functions
# A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterators iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.
@@ -156,4 +156,4 @@ customized = Customized()
SINK(Customized.a)
SINK_F(Customized.b)
SINK(customized.a)
SINK(customized.b) #$ flow="ControlFlowNode for SOURCE, l:152 -> ControlFlowNode for Attribute" flow="ControlFlowNode for Str, l:13 -> ControlFlowNode for Attribute"
SINK(customized.b) #$ flow="SOURCE, l:152 -> customized.b" flow="'source', l:13 -> customized.b"

View File

@@ -1 +0,0 @@
import experimental.dataflow.FlowTestUtil.MaximalFlowTest

View File

@@ -41,7 +41,7 @@ def SINK_F(x):
def test_tuple_with_local_flow():
x = (NONSOURCE, SOURCE)
y = x[1]
SINK(y) #$ flow="ControlFlowNode for SOURCE, l:42 -> ControlFlowNode for y" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for y"
SINK(y) #$ flow="SOURCE, l:42 -> y" flow="'source', l:20 -> y"
def test_tuple_negative():
@@ -53,45 +53,45 @@ def test_tuple_negative():
# 6.2.1. Identifiers (Names)
def test_names():
x = SOURCE
SINK(x) #$ flow="ControlFlowNode for SOURCE, l:55 -> ControlFlowNode for x" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for x"
SINK(x) #$ flow="SOURCE, l:55 -> x" flow="'source', l:20 -> x"
# 6.2.2. Literals
def test_string_literal():
x = "source"
SINK(x) #$ flow="ControlFlowNode for Str, l:61 -> ControlFlowNode for x"
SINK(x) #$ flow="'source', l:61 -> x"
def test_bytes_literal():
x = b"source"
SINK(x) #$ flow="ControlFlowNode for Str, l:66 -> ControlFlowNode for x"
SINK(x) #$ flow="b'source', l:66 -> x"
def test_integer_literal():
x = 42
SINK(x) #$ flow="ControlFlowNode for IntegerLiteral, l:71 -> ControlFlowNode for x"
SINK(x) #$ flow="42, l:71 -> x"
def test_floatnumber_literal():
x = 42.0
SINK(x) #$ flow="ControlFlowNode for FloatLiteral, l:76 -> ControlFlowNode for x"
SINK(x) #$ flow="42.0, l:76 -> x"
def test_imagnumber_literal():
x = 42j
SINK(x) #$ MISSING:flow="ControlFlowNode for FloatLiteral, l:81 -> ControlFlowNode for x"
SINK(x) #$ MISSING:flow="42j, l:81 -> x"
# 6.2.3. Parenthesized forms
def test_parenthesized_form():
x = (SOURCE)
SINK(x) #$ flow="ControlFlowNode for SOURCE, l:87 -> ControlFlowNode for x" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for x"
SINK(x) #$ flow="SOURCE, l:87 -> x" flow="'source', l:20 -> x"
# 6.2.5. List displays
def test_list_display():
x = [SOURCE]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:93 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:93 -> x[0]" flow="'source', l:20 -> x[0]"
def test_list_display_negative():
@@ -101,109 +101,109 @@ def test_list_display_negative():
def test_list_comprehension():
x = [SOURCE for y in [NONSOURCE]]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:103 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:103 -> x[0]" flow="'source', l:20 -> x[0]"
def test_list_comprehension_flow():
x = [y for y in [SOURCE]]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:108 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:108 -> x[0]" flow="'source', l:20 -> x[0]"
def test_list_comprehension_inflow():
l = [SOURCE]
x = [y for y in l]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:113 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:113 -> x[0]" flow="'source', l:20 -> x[0]"
def test_nested_list_display():
x = [*[SOURCE]]
SINK(x[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:119 -> ControlFlowNode for Subscript" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ MISSING:flow="SOURCE, l:119 -> x[0]" MISSING:flow="'source', l:20 -> x[0]"
# 6.2.6. Set displays
def test_set_display():
x = {SOURCE}
SINK(x.pop()) #$ flow="ControlFlowNode for SOURCE, l:125 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop()) #$ flow="SOURCE, l:125 -> x.pop()" flow="'source', l:20 -> x.pop()"
def test_set_comprehension():
x = {SOURCE for y in [NONSOURCE]}
SINK(x.pop()) #$ flow="ControlFlowNode for SOURCE, l:130 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop()) #$ flow="SOURCE, l:130 -> x.pop()" flow="'source', l:20 -> x.pop()"
def test_set_comprehension_flow():
x = {y for y in [SOURCE]}
SINK(x.pop()) #$ flow="ControlFlowNode for SOURCE, l:135 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop()) #$ flow="SOURCE, l:135 -> x.pop()" flow="'source', l:20 -> x.pop()"
def test_set_comprehension_inflow():
l = {SOURCE}
x = {y for y in l}
SINK(x.pop()) #$ flow="ControlFlowNode for SOURCE, l:140 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop()) #$ flow="SOURCE, l:140 -> x.pop()" flow="'source', l:20 -> x.pop()"
def test_nested_set_display():
x = {*{SOURCE}}
SINK(x.pop()) #$ MISSING:flow="ControlFlowNode for SOURCE, l:146 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop()) #$ MISSING:flow="SOURCE, l:146 -> x.pop()" MISSING:flow="'source', l:20 -> x.pop()"
# 6.2.7. Dictionary displays
def test_dict_display():
x = {"s": SOURCE}
SINK(x["s"]) #$ flow="ControlFlowNode for SOURCE, l:152 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x["s"]) #$ flow="SOURCE, l:152 -> x['s']" flow="'source', l:20 -> x['s']"
def test_dict_display_pop():
x = {"s": SOURCE}
SINK(x.pop("s")) #$ flow="ControlFlowNode for SOURCE, l:157 -> ControlFlowNode for Attribute()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop("s")) #$ flow="SOURCE, l:157 -> x.pop(..)" flow="'source', l:20 -> x.pop(..)"
def test_dict_comprehension():
x = {y: SOURCE for y in ["s"]}
SINK(x["s"]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:152 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:152 -> x['s']" MISING:flow="'source', l:20 -> x['s']"
def test_dict_comprehension_pop():
x = {y: SOURCE for y in ["s"]}
SINK(x.pop("s")) #$ MISSING:flow="ControlFlowNode for SOURCE, l:167 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:167 -> x.pop()" MISSING:flow="'source', l:20 -> x.pop()"
def test_nested_dict_display():
x = {**{"s": SOURCE}}
SINK(x["s"]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:172 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:172 -> x['s']" MISING:flow="'source', l:20 -> x['s']"
def test_nested_dict_display_pop():
x = {**{"s": SOURCE}}
SINK(x.pop("s")) #$ MISSING:flow="ControlFlowNode for SOURCE, l:177 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:177 -> x.pop()" MISSING:flow="'source', l:20 -> x.pop()"
# Nested comprehensions
def test_nested_comprehension():
x = [y for z in [[SOURCE]] for y in z]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:183 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:183 -> x[0]" flow="'source', l:20 -> x[0]"
def test_nested_comprehension_deep_with_local_flow():
x = [y for v in [[[[SOURCE]]]] for u in v for z in u for y in z]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:188 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:188 -> x[0]" flow="'source', l:20 -> x[0]"
def test_nested_comprehension_dict():
d = {"s": [SOURCE]}
x = [y for k, v in d.items() for y in v]
SINK(x[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:193 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ MISSING:flow="SOURCE, l:193 -> x[0]" MISING:flow="'source', l:20 -> x[0]"
def test_nested_comprehension_paren():
x = [y for y in (z for z in [SOURCE])]
SINK(x[0]) #$ flow="ControlFlowNode for SOURCE, l:199 -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(x[0]) #$ flow="SOURCE, l:199 -> x[0]" flow="'source', l:20 -> x[0]"
# 6.2.8. Generator expressions
def test_generator():
x = (SOURCE for y in [NONSOURCE])
SINK([*x][0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:205 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK([*x][0]) #$ MISSING:flow="SOURCE, l:205 -> List[0]" MISING:flow="'source', l:20 -> List[0]"
# 6.2.9. Yield expressions
@@ -213,7 +213,7 @@ def gen(x):
def test_yield():
g = gen(SOURCE)
SINK(next(g)) #$ MISSING:flow="ControlFlowNode for SOURCE, l:215 -> ControlFlowNode for next()" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for next()"
SINK(next(g)) #$ MISSING:flow="SOURCE, l:215 -> next()" MISING:flow="'source', l:20 -> next()"
def gen_from(x):
@@ -222,19 +222,19 @@ def gen_from(x):
def test_yield_from():
g = gen_from(SOURCE)
SINK(next(g)) #$ MISSING:flow="ControlFlowNode for SOURCE, l:224 -> ControlFlowNode for next()" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for next()"
SINK(next(g)) #$ MISSING:flow="SOURCE, l:224 -> next()" MISING:flow="'source', l:20 -> next()"
# a statement rather than an expression, but related to generators
def test_for():
for x in gen(SOURCE):
SINK(x) #$ MISSING:flow="ControlFlowNode for SOURCE, l:230 -> ControlFlowNode for x" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for x"
SINK(x) #$ MISSING:flow="SOURCE, l:230 -> x" MISING:flow="'source', l:20 -> x"
# 6.2.9.1. Generator-iterator methods
def test___next__():
g = gen(SOURCE)
SINK(g.__next__()) #$ MISSING:flow="ControlFlowNode for SOURCE, l:236 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(g.__next__()) #$ MISSING:flow="SOURCE, l:236 -> g.__next__()" MISSING:flow="'source', l:20 -> g.__next__()"
def gen2(x):
@@ -246,7 +246,7 @@ def gen2(x):
def test_send():
g = gen2(NONSOURCE)
n = next(g)
SINK(g.send(SOURCE)) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(g.send(SOURCE)) #$ MISSING:flow="SOURCE -> g.send()" MISSING:flow="'source', l:20 -> g.send()"
def gen_ex(x):
@@ -259,7 +259,7 @@ def gen_ex(x):
def test_throw():
g = gen_ex(SOURCE)
n = next(g)
SINK(g.throw(TypeError)) #$ MISSING:flow="ControlFlowNode for SOURCE, l:260 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(g.throw(TypeError)) #$ MISSING:flow="SOURCE, l:260 -> g.throw()" MISSING:flow="'source', l:20 -> g.throw()"
# no `test_close` as `close` involves no data flow
@@ -280,7 +280,7 @@ def runa(a):
async def atest___anext__():
g = agen(SOURCE)
SINK(await g.__anext__()) #$ MISSING:flow="ControlFlowNode for SOURCE, l:282 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(await g.__anext__()) #$ MISSING:flow="SOURCE, l:282 -> g.__anext__()" MISSING:flow="'source', l:20 -> g.__anext__()"
def test___anext__():
@@ -296,7 +296,7 @@ async def agen2(x):
async def atest_asend():
g = agen2(NONSOURCE)
n = await g.__anext__()
SINK(await g.asend(SOURCE)) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(await g.asend(SOURCE)) #$ MISSING:flow="SOURCE -> g.asend()" MISSING:flow="'source', l:20 -> g.asend()"
def test_asend():
@@ -313,7 +313,7 @@ async def agen_ex(x):
async def atest_athrow():
g = agen_ex(SOURCE)
n = await g.__anext__()
SINK(await g.athrow(TypeError)) #$ MISSING:flow="ControlFlowNode for SOURCE, l:314 -> ControlFlowNode for Attribute()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute()"
SINK(await g.athrow(TypeError)) #$ MISSING:flow="SOURCE, l:314 -> g.athrow()" MISSING:flow="'source', l:20 -> g.athrow()"
def test_athrow():
@@ -326,22 +326,22 @@ class C:
def test_attribute_reference():
SINK(C.a) #$ MISSING:flow="ControlFlowNode for SOURCE, l:325 -> ControlFlowNode for Attribute" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Attribute"
SINK(C.a) #$ MISSING:flow="SOURCE, l:325 -> C.a" MISSING:flow="'source', l:20 -> C.a"
# overriding __getattr__ should be tested by the class coverage tests
# 6.3.2. Subscriptions
def test_subscription_tuple():
SINK((SOURCE,)[0]) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK((SOURCE,)[0]) #$ flow="SOURCE -> Tuple[0]" flow="'source', l:20 -> Tuple[0]"
def test_subscription_list():
SINK([SOURCE][0]) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK([SOURCE][0]) #$ flow="SOURCE -> List[0]" flow="'source', l:20 -> List[0]"
def test_subscription_mapping():
SINK({"s": SOURCE}["s"]) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for Subscript" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK({"s": SOURCE}["s"]) #$ flow="SOURCE -> Dict['s']" flow="'source', l:20 -> Dict['s']"
# overriding __getitem__ should be tested by the class coverage tests
@@ -353,7 +353,7 @@ l = [SOURCE]
def test_slicing():
s = l[0:1:1]
SINK(s[0]) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for Subscript" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(s[0]) #$ MISSING:flow="SOURCE -> s[0]" MISSING:flow="'source', l:20 -> s[0]"
# The grammar seems to allow `l[0:1:1, 0:1]`, but the interpreter does not like it
@@ -364,7 +364,7 @@ def second(a, b):
def test_call_positional():
SINK(second(NONSOURCE, SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, SOURCE)) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def test_call_positional_negative():
@@ -372,15 +372,15 @@ def test_call_positional_negative():
def test_call_keyword():
SINK(second(NONSOURCE, b=SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def test_call_unpack_iterable():
SINK(second(NONSOURCE, *[SOURCE])) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, *[SOURCE])) #$ MISSING:flow="SOURCE -> second(..)" MISING:flow="'source', l:20 -> second(..)"
def test_call_unpack_mapping():
SINK(second(NONSOURCE, **{"b": SOURCE})) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, **{"b": SOURCE})) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def f_extra_pos(a, *b):
@@ -388,7 +388,7 @@ def f_extra_pos(a, *b):
def test_call_extra_pos():
SINK(f_extra_pos(NONSOURCE, SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_pos()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_pos()"
SINK(f_extra_pos(NONSOURCE, SOURCE)) #$ flow="SOURCE -> f_extra_pos(..)" flow="'source', l:20 -> f_extra_pos(..)"
def f_extra_keyword(a, **b):
@@ -396,7 +396,7 @@ def f_extra_keyword(a, **b):
def test_call_extra_keyword():
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_keyword()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_keyword()"
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> f_extra_keyword(..)" flow="'source', l:20 -> f_extra_keyword(..)"
# return the name of the first extra keyword argument
@@ -406,18 +406,18 @@ def f_extra_keyword_flow(**a):
# call the function with our source as the name of the keyword arguemnt
def test_call_extra_keyword_flow():
SINK(f_extra_keyword_flow(**{SOURCE: None})) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_keyword()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_keyword()"
SINK(f_extra_keyword_flow(**{SOURCE: None})) #$ MISSING:flow="SOURCE -> f_extra_keyword(..)" MISSING:flow="'source', l:20 -> f_extra_keyword(..)"
# 6.12. Assignment expressions
def test_assignment_expression():
x = NONSOURCE
SINK(x := SOURCE) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for x" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for x"
SINK(x := SOURCE) #$ MISSING:flow="SOURCE -> x" MISSING:flow="'source', l:20 -> x"
# 6.13. Conditional expressions
def test_conditional_true():
SINK(SOURCE if True else NONSOURCE) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for IfExp" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for IfExp"
SINK(SOURCE if True else NONSOURCE) #$ flow="SOURCE -> IfExp" flow="'source', l:20 -> IfExp"
def test_conditional_true_guards():
@@ -425,7 +425,7 @@ def test_conditional_true_guards():
def test_conditional_false():
SINK(NONSOURCE if False else SOURCE) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for IfExp" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for IfExp"
SINK(NONSOURCE if False else SOURCE) #$ flow="SOURCE -> IfExp" flow="'source', l:20 -> IfExp"
def test_conditional_false_guards():
@@ -435,13 +435,13 @@ def test_conditional_false_guards():
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_true():
x = NONSOURCE
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for IfExp" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for IfExp"
SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) #$ MISSING:flow="SOURCE -> IfExp" MISSING:flow="'source', l:20 -> IfExp"
# Condition is evaluated first, so x is SOURCE once chosen
def test_conditional_evaluation_false():
x = NONSOURCE
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for IfExp" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for IfExp"
SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) #$ MISSING:flow="SOURCE -> IfExp" MISSING:flow="'source', l:20 -> IfExp"
# 6.14. Lambdas
@@ -449,14 +449,14 @@ def test_lambda():
def f(x):
return x
SINK(f(SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f()"
SINK(f(SOURCE)) #$ flow="SOURCE -> f(..)" flow="'source', l:20 -> f(..)"
def test_lambda_positional():
def second(a, b):
return b
SINK(second(NONSOURCE, SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, SOURCE)) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def test_lambda_positional_negative():
@@ -470,50 +470,50 @@ def test_lambda_keyword():
def second(a, b):
return b
SINK(second(NONSOURCE, b=SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def test_lambda_unpack_iterable():
def second(a, b):
return b
SINK(second(NONSOURCE, *[SOURCE])) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()" # Flow missing
SINK(second(NONSOURCE, *[SOURCE])) #$ MISSING:flow="SOURCE -> second(..)" MISSING:flow="'source', l:20 -> second(..)" # Flow missing
def test_lambda_unpack_mapping():
def second(a, b):
return b
SINK(second(NONSOURCE, **{"b": SOURCE})) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for second()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for second()"
SINK(second(NONSOURCE, **{"b": SOURCE})) #$ flow="SOURCE -> second(..)" flow="'source', l:20 -> second(..)"
def test_lambda_extra_pos():
f_extra_pos = lambda a, *b: b[0]
SINK(f_extra_pos(NONSOURCE, SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_pos()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_pos()"
SINK(f_extra_pos(NONSOURCE, SOURCE)) #$ flow="SOURCE -> f_extra_pos(..)" flow="'source', l:20 -> f_extra_pos(..)"
def test_lambda_extra_keyword():
f_extra_keyword = lambda a, **b: b["b"]
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_keyword()" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_keyword()"
SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) #$ flow="SOURCE -> f_extra_keyword(..)" flow="'source', l:20 -> f_extra_keyword(..)"
# call the function with our source as the name of the keyword argument
def test_lambda_extra_keyword_flow():
# return the name of the first extra keyword argument
f_extra_keyword_flow = lambda **a: [*a][0]
SINK(f_extra_keyword_flow(**{SOURCE: None})) #$ MISSING:flow="ControlFlowNode for SOURCE -> ControlFlowNode for f_extra_keyword()" MISSING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for f_extra_keyword()"
SINK(f_extra_keyword_flow(**{SOURCE: None})) #$ MISSING:flow="SOURCE -> f_extra_keyword(..)" MISSING:flow="'source', l:20 -> f_extra_keyword(..)"
@expects(4)
def test_swap():
a = SOURCE
b = NONSOURCE
SINK(a) #$ flow="ControlFlowNode for SOURCE, l:509 -> ControlFlowNode for a" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for a"
SINK(a) #$ flow="SOURCE, l:509 -> a" flow="'source', l:20 -> a"
SINK_F(b)
a, b = b, a
SINK_F(a)
SINK(b) #$ flow="ControlFlowNode for SOURCE, l:509 -> ControlFlowNode for b" flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for b"
SINK(b) #$ flow="SOURCE, l:509 -> b" flow="'source', l:20 -> b"
def test_deep_callgraph():
@@ -538,7 +538,7 @@ def test_deep_callgraph():
return f5(arg)
x = f6(SOURCE)
SINK(x) #$ MISSING:flow="ControlFlowNode for SOURCE, l:540 -> ControlFlowNode for x" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for x"
SINK(x) #$ MISSING:flow="SOURCE, l:540 -> x" MISING:flow="'source', l:20 -> x"
@expects(2)
@@ -547,7 +547,7 @@ def test_dynamic_tuple_creation_1():
tup += (SOURCE,)
tup += (NONSOURCE,)
SINK(tup[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:547 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(tup[0]) #$ MISSING:flow="SOURCE, l:547 -> tup[0]" MISING:flow="'source', l:20 -> tup[0]"
SINK_F(tup[1])
@@ -557,7 +557,7 @@ def test_dynamic_tuple_creation_2():
tup += (SOURCE,)
tup += (NONSOURCE,)
SINK(tup[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:557 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(tup[0]) #$ MISSING:flow="SOURCE, l:557 -> tup[0]" MISING:flow="'source', l:20 -> tup[0]"
SINK_F(tup[1])
@@ -567,7 +567,7 @@ def test_dynamic_tuple_creation_3():
tup2 = (NONSOURCE,)
tup = tup1 + tup2
SINK(tup[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:566 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(tup[0]) #$ MISSING:flow="SOURCE, l:566 -> tup[0]" MISING:flow="'source', l:20 -> tup[0]"
SINK_F(tup[1])
@@ -578,5 +578,5 @@ def test_dynamic_tuple_creation_4():
for item in [SOURCE, NONSOURCE]:
tup += (item,)
SINK(tup[0]) #$ MISSING:flow="ControlFlowNode for SOURCE, l:578 -> ControlFlowNode for Subscript" MISING:flow="ControlFlowNode for Str, l:20 -> ControlFlowNode for Subscript"
SINK(tup[0]) #$ MISSING:flow="SOURCE, l:578 -> tup[0]" MISING:flow="'source', l:20 -> tup[0]"
SINK_F(tup[1])

View File

@@ -1,6 +1,7 @@
import python
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.DataFlow
import experimental.dataflow.TestUtil.PrintNode
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
@@ -18,31 +19,6 @@ class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
}
}
private string repr(Expr e) {
not e instanceof Num and
not e instanceof StrConst and
not e instanceof Subscript and
not e instanceof Call and
not e instanceof Attribute and
result = e.toString()
or
result = e.(Num).getN()
or
result =
e.(StrConst).getPrefix() + e.(StrConst).getText() +
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
or
result = repr(e.(Subscript).getObject()) + "[" + repr(e.(Subscript).getIndex()) + "]"
or
(
if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg())
then result = repr(e.(Call).getFunc()) + "(..)"
else result = repr(e.(Call).getFunc()) + "()"
)
or
result = repr(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
}
query predicate test_taint(string arg_location, string test_res, string scope_name, string repr) {
exists(Call call, Expr arg, boolean expected_taint, boolean has_taint |
// only consider files that are extracted as part of the test
@@ -69,6 +45,6 @@ query predicate test_taint(string arg_location, string test_res, string scope_na
arg_location = arg.getLocation().toString() and
test_res = test_res and
scope_name = call.getScope().getName() and
repr = repr(arg)
repr = prettyExp(arg)
)
}