Python: start modernizing routing tests

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-11-12 22:43:34 +01:00
parent 823ed447df
commit 4fe2576b9a
3 changed files with 132 additions and 17 deletions

View File

@@ -72,8 +72,8 @@ def argument_passing(
f,
**g,
):
SINK1(a)
SINK2(b)
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)
SINK4(d)
SINK5(e)
@@ -95,8 +95,8 @@ def test_argument_passing2():
def with_pos_only(a, /, b):
SINK1(a)
SINK2(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"
@expects(6)
@@ -107,8 +107,8 @@ def test_pos_only():
def with_multiple_kw_args(a, b, c):
SINK1(a)
SINK2(b)
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)
@@ -121,8 +121,8 @@ def test_multiple_kw_args():
def with_default_arguments(a=arg1, b=arg2, c=arg3):
SINK1(a)
SINK2(b)
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)
@@ -136,14 +136,14 @@ def test_default_arguments():
# Nested constructor pattern
def grab_foo_bar_baz(foo, **kwargs):
SINK1(foo)
SINK1(foo) #$ arg1="ControlFlowNode for arg1, l:160 -> ControlFlowNode for 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)
SINK2(bar) #$ arg2="ControlFlowNode for arg2, l:160 -> ControlFlowNode for bar"
try:
SINK2_F(kwargs["bar"])
except:
@@ -163,14 +163,14 @@ def test_grab():
# All combinations
def test_pos_pos():
def with_pos(a):
SINK1(a)
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:168 -> ControlFlowNode for a"
with_pos(arg1)
def test_pos_pos_only():
def with_pos_only(a, /):
SINK1(a)
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:175 -> ControlFlowNode for 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])
SINK1(a[0]) #$ arg1="ControlFlowNode for arg1, l:183 -> ControlFlowNode for Subscript"
with_star(arg1)
def test_pos_kw():
def with_kw(a=""):
SINK1(a)
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:190 -> ControlFlowNode for a"
with_kw(arg1)
def test_kw_pos():
def with_pos(a):
SINK1(a)
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:197 -> ControlFlowNode for a"
with_pos(a=arg1)
def test_kw_kw():
def with_kw(a=""):
SINK1(a)
SINK1(a) #$ arg1="ControlFlowNode for arg1, l:204 -> ControlFlowNode for a"
with_kw(a=arg1)
def test_kw_doublestar():
def with_doublestar(**a):
SINK1(a["a"])
SINK1(a["a"]) #$ arg1="ControlFlowNode for arg1, l:211 -> ControlFlowNode for Subscript"
with_doublestar(a=arg1)

View File

@@ -0,0 +1,32 @@
| classes.py:556:15:556:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:565 -> ControlFlowNode for key" |
| classes.py:557:15:557:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_getitem, l:563 -> ControlFlowNode for self" |
| classes.py:572:15:572:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:581 -> ControlFlowNode for key" |
| classes.py:573:15:573:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_setitem, l:578 -> ControlFlowNode for self" |
| classes.py:587:15:587:17 | ControlFlowNode for key | Unexpected result: arg2="ControlFlowNode for arg2, l:595 -> ControlFlowNode for key" |
| classes.py:588:15:588:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_delitem, l:593 -> ControlFlowNode for self" |
| classes.py:658:15:658:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:667 -> ControlFlowNode for other" |
| classes.py:659:15:659:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_add, l:665 -> ControlFlowNode for self" |
| classes.py:673:15:673:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:682 -> ControlFlowNode for other" |
| classes.py:674:15:674:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_sub, l:680 -> ControlFlowNode for self" |
| classes.py:688:15:688:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:697 -> ControlFlowNode for other" |
| classes.py:689:15:689:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_mul, l:695 -> ControlFlowNode for self" |
| classes.py:703:15:703:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:712 -> ControlFlowNode for other" |
| classes.py:704:15:704:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_matmul, l:710 -> ControlFlowNode for self" |
| classes.py:718:15:718:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:727 -> ControlFlowNode for other" |
| classes.py:719:15:719:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_truediv, l:725 -> ControlFlowNode for self" |
| classes.py:733:15:733:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:742 -> ControlFlowNode for other" |
| classes.py:734:15:734:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_floordiv, l:740 -> ControlFlowNode for self" |
| classes.py:748:15:748:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:757 -> ControlFlowNode for other" |
| classes.py:749:15:749:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_mod, l:755 -> ControlFlowNode for self" |
| classes.py:778:15:778:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:793 -> ControlFlowNode for other" |
| classes.py:779:15:779:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_pow, l:791 -> ControlFlowNode for self" |
| classes.py:799:15:799:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:808 -> ControlFlowNode for other" |
| classes.py:800:15:800:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_lshift, l:806 -> ControlFlowNode for self" |
| classes.py:814:15:814:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:823 -> ControlFlowNode for other" |
| classes.py:815:15:815:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_rshift, l:821 -> ControlFlowNode for self" |
| classes.py:829:15:829:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:838 -> ControlFlowNode for other" |
| classes.py:830:15:830:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_and, l:836 -> ControlFlowNode for self" |
| classes.py:844:15:844:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:853 -> ControlFlowNode for other" |
| classes.py:845:15:845:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_xor, l:851 -> ControlFlowNode for self" |
| classes.py:859:15:859:19 | ControlFlowNode for other | Unexpected result: arg2="ControlFlowNode for arg2, l:868 -> ControlFlowNode for other" |
| classes.py:860:15:860:18 | ControlFlowNode for self | Unexpected result: arg1="SSA variable with_or, l:866 -> ControlFlowNode for self" |

View File

@@ -0,0 +1,83 @@
import python
import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
import experimental.dataflow.FlowTestUtil.FlowTest
class Argument1RoutingTest extends FlowTest {
Argument1RoutingTest() { this = "Argument1RoutingTest" }
override string flowTag() { result = "arg1" }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
exists(Argument1RoutingConfig cfg | cfg.hasFlow(source, sink))
}
}
/**
* A configuration to check routing of arguments through magic methods.
*/
class Argument1RoutingConfig extends DataFlow::Configuration {
Argument1RoutingConfig() { this = "Argument1RoutingConfig" }
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg1"
or
exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call |
def.getVariable() = node.(DataFlow::EssaNode).getVar() and
def.getValue() = call.getNode() and
call.getNode().(CallNode).getFunction().(NameNode).getId().matches("With\\_%")
) and
node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%")
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK1" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
/**
* We want to be able to use `arg` in a sequence of calls such as `func(kw=arg); ... ; func(arg)`.
* Use-use flow lets the argument to the first call reach the sink inside the second call,
* making it seem like we handle all cases even if we only handle the last one.
* We make the test honest by preventing flow into source nodes.
*/
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument2RoutingTest extends FlowTest {
Argument2RoutingTest() { this = "Argument2RoutingTest" }
override string flowTag() { result = "arg2" }
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
exists(Argument2RoutingConfig cfg | cfg.hasFlow(source, sink))
}
}
/**
* A configuration to check routing of arguments through magic methods.
*/
class Argument2RoutingConfig extends DataFlow::Configuration {
Argument2RoutingConfig() { this = "Argument2RoutingConfig" }
override predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg2"
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "SINK2" and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
/**
* We want to be able to use `arg` in a sequence of calls such as `func(kw=arg); ... ; func(arg)`.
* Use-use flow lets the argument to the first call reach the sink inside the second call,
* making it seem like we handle all cases even if we only handle the last one.
* We make the test honest by preventing flow into source nodes.
*/
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}