Python: Remove absolute line numbers

- Use relative line numbers in flow test
- Elide line numbers in routing test (new concept)
This commit is contained in:
Rasmus Lerchedahl Petersen
2021-01-19 17:05:42 +01:00
parent 42fa3bdb81
commit 77da4b0106
8 changed files with 482 additions and 440 deletions

View File

@@ -26,8 +26,15 @@ abstract class FlowTest extends InlineExpectationsTest {
pragma[inline]
private string lineStr(DataFlow::Node fromNode, DataFlow::Node toNode) {
if fromNode.getLocation().getStartLine() = toNode.getLocation().getStartLine()
then result = ""
else result = ", l:" + fromNode.getLocation().getStartLine()
exists(int delta |
delta = fromNode.getLocation().getStartLine() - toNode.getLocation().getStartLine()
|
if delta = 0
then result = ""
else
if delta > 0
then result = ", l:+" + delta.toString()
else result = ", l:" + delta.toString()
)
}
}

View File

@@ -0,0 +1,35 @@
import python
import semmle.python.dataflow.new.DataFlow
import TestUtilities.InlineExpectationsTest
import experimental.dataflow.TestUtil.PrintNode
/**
* A routing test is designed to test that vlues are routed to the
* correct arguments of the correct functions. It is assumed that
* the functions tested sink their arguments sequentially, that is
* `SINK1(arg1)`, etc.
*/
abstract class RoutingTest extends InlineExpectationsTest {
bindingset[this]
RoutingTest() { any() }
abstract string flowTag();
abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode);
override string getARelevantTag() { result in ["func", this.flowTag()] }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(DataFlow::Node fromNode, DataFlow::Node toNode | this.relevantFlow(fromNode, toNode) |
location = fromNode.getLocation() and
element = fromNode.toString() and
(
tag = this.flowTag() and
value = "\"" + prettyNode(fromNode).replaceAll("\"", "'") + "\""
or
tag = "func" and
value = toNode.getEnclosingCallable().getCallableValue().getScope().getQualifiedName() // TODO: More robust pretty printing?
)
)
}
}

View File

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

View File

@@ -72,78 +72,78 @@ def argument_passing(
f,
**g,
):
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"
SINK1(a)
SINK2(b)
SINK3(c)
SINK4(d)
SINK5(e)
SINK6(f)
try:
SINK7(g["g"]) #$ arg7="arg7, l:89 -> g['g']"
SINK7(g["g"])
except:
print("OK")
@expects(7)
def test_argument_passing1():
argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7})
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"
@expects(7)
def test_argument_passing2():
argument_passing(arg1, arg2, arg3, f=arg6)
argument_passing(arg1, arg2, arg3, f=arg6) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=argument_passing
def with_pos_only(a, /, 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"
SINK1(a)
SINK2(b)
@expects(6)
def test_pos_only():
with_pos_only(arg1, arg2)
with_pos_only(arg1, b=arg2)
with_pos_only(arg1, *(arg2,))
with_pos_only(arg1, arg2) #$ arg1="arg1" arg2="arg2" func=with_pos_only
with_pos_only(arg1, b=arg2) #$ arg1="arg1" arg2="arg2" func=with_pos_only
with_pos_only(arg1, *(arg2,)) #$ arg1="arg1" func=with_pos_only MISSING: arg2="arg2"
def with_multiple_kw_args(a, b, 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"
SINK1(a)
SINK2(b)
SINK3(c)
@expects(9)
def test_multiple_kw_args():
with_multiple_kw_args(b=arg2, c=arg3, a=arg1)
with_multiple_kw_args(arg1, *(arg2,), arg3)
with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2)
with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1})
with_multiple_kw_args(b=arg2, c=arg3, a=arg1) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=with_multiple_kw_args
with_multiple_kw_args(arg1, *(arg2,), arg3) #$ arg1="arg1" func=with_multiple_kw_args MISSING: arg2="arg2" arg3="arg3"
with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2) #$ arg1="arg1" arg3="arg3" func=with_multiple_kw_args MISSING: arg2="arg2"
with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1}) #$ arg1="arg1" arg2="arg2" arg3="arg3" func=with_multiple_kw_args
def with_default_arguments(a=arg1, b=arg2, c=arg3):
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"
def with_default_arguments(a=arg1, b=arg2, c=arg3): # Need a mechanism to test default arguments
SINK1(a)
SINK2(b)
SINK3(c)
@expects(12)
def test_default_arguments():
with_default_arguments()
with_default_arguments(arg1)
with_default_arguments(b=arg2)
with_default_arguments(**{"c": arg3})
with_default_arguments(arg1) #$ arg1="arg1" func=with_default_arguments
with_default_arguments(b=arg2) #$ arg2="arg2" func=with_default_arguments
with_default_arguments(**{"c": arg3}) #$ arg3="arg3" func=with_default_arguments
# Nested constructor pattern
def grab_foo_bar_baz(foo, **kwargs):
SINK1(foo) #$ arg1="arg1, l:160 -> foo"
SINK1(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="arg2, l:160 -> bar"
SINK2(bar)
try:
SINK2_F(kwargs["bar"])
except:
@@ -152,60 +152,60 @@ def grab_bar_baz(bar, **kwargs):
def grab_baz(baz):
SINK3(baz) #$ arg3="arg3, l:160 -> baz"
SINK3(baz)
@expects(4)
def test_grab():
grab_foo_bar_baz(baz=arg3, bar=arg2, foo=arg1)
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
# All combinations
def test_pos_pos():
def with_pos(a):
SINK1(a) #$ arg1="arg1, l:168 -> a"
SINK1(a)
with_pos(arg1)
with_pos(arg1) #$ arg1="arg1" func=test_pos_pos.with_pos
def test_pos_pos_only():
def with_pos_only(a, /):
SINK1(a) #$ arg1="arg1, l:175 -> a"
SINK1(a)
with_pos_only(arg1)
with_pos_only(arg1) #$ arg1="arg1" func=test_pos_pos_only.with_pos_only
def test_pos_star():
def with_star(*a):
if len(a) > 0:
SINK1(a[0]) #$ arg1="arg1, l:183 -> a[0]"
SINK1(a[0])
with_star(arg1)
with_star(arg1) #$ arg1="arg1" func=test_pos_star.with_star
def test_pos_kw():
def with_kw(a=""):
SINK1(a) #$ arg1="arg1, l:190 -> a"
SINK1(a)
with_kw(arg1)
with_kw(arg1) #$ arg1="arg1" func=test_pos_kw.with_kw
def test_kw_pos():
def with_pos(a):
SINK1(a) #$ arg1="arg1, l:197 -> a"
SINK1(a)
with_pos(a=arg1)
with_pos(a=arg1) #$ arg1="arg1" func=test_kw_pos.with_pos
def test_kw_kw():
def with_kw(a=""):
SINK1(a) #$ arg1="arg1, l:204 -> a"
SINK1(a)
with_kw(a=arg1)
with_kw(a=arg1) #$ arg1="arg1" func=test_kw_kw.with_kw
def test_kw_doublestar():
def with_doublestar(**a):
SINK1(a["a"]) #$ arg1="arg1, l:211 -> a['a']"
SINK1(a["a"])
with_doublestar(a=arg1)
with_doublestar(a=arg1) #$ arg1="arg1" func=test_kw_doublestar.with_doublestar

View File

@@ -1,9 +1,9 @@
import python
import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
import experimental.dataflow.TestUtil.FlowTest
import experimental.dataflow.TestUtil.RoutingTest
class Argument1RoutingTest extends FlowTest {
class Argument1RoutingTest extends RoutingTest {
Argument1RoutingTest() { this = "Argument1RoutingTest" }
override string flowTag() { result = "arg1" }
@@ -46,7 +46,7 @@ class Argument1RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument2RoutingTest extends FlowTest {
class Argument2RoutingTest extends RoutingTest {
Argument2RoutingTest() { this = "Argument2RoutingTest" }
override string flowTag() { result = "arg2" }
@@ -82,7 +82,7 @@ class Argument2RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument3RoutingTest extends FlowTest {
class Argument3RoutingTest extends RoutingTest {
Argument3RoutingTest() { this = "Argument3RoutingTest" }
override string flowTag() { result = "arg3" }
@@ -118,7 +118,7 @@ class Argument3RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument4RoutingTest extends FlowTest {
class Argument4RoutingTest extends RoutingTest {
Argument4RoutingTest() { this = "Argument4RoutingTest" }
override string flowTag() { result = "arg4" }
@@ -154,7 +154,7 @@ class Argument4RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument5RoutingTest extends FlowTest {
class Argument5RoutingTest extends RoutingTest {
Argument5RoutingTest() { this = "Argument5RoutingTest" }
override string flowTag() { result = "arg5" }
@@ -190,7 +190,7 @@ class Argument5RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument6RoutingTest extends FlowTest {
class Argument6RoutingTest extends RoutingTest {
Argument6RoutingTest() { this = "Argument6RoutingTest" }
override string flowTag() { result = "arg6" }
@@ -226,7 +226,7 @@ class Argument6RoutingConfig extends DataFlow::Configuration {
override predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
}
class Argument7RoutingTest extends FlowTest {
class Argument7RoutingTest extends RoutingTest {
Argument7RoutingTest() { this = "Argument7RoutingTest" }
override string flowTag() { result = "arg7" }

File diff suppressed because it is too large Load Diff

View File

@@ -153,7 +153,7 @@ class Customized:
# testing __new__ and __init__
customized = Customized()
SINK(Customized.a)
SINK(Customized.a) #$ MISSING:flow="SOURCE, l:-8 -> customized.a"
SINK_F(Customized.b)
SINK(customized.a)
SINK(customized.b) #$ flow="SOURCE, l:152 -> customized.b"
SINK(customized.a) #$ MISSING:flow="SOURCE, l:-10 -> customized.a"
SINK(customized.b) #$ flow="SOURCE, l:-7 -> customized.b"

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="SOURCE, l:42 -> y"
SINK(y) #$ flow="SOURCE, l:-2 -> 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="SOURCE, l:55 -> x"
SINK(x) #$ flow="SOURCE, l:-1 -> x"
# 6.2.2. Literals
def test_string_literal():
x = "source"
SINK(x) #$ flow="'source', l:61 -> x"
SINK(x) #$ flow="'source', l:-1 -> x"
def test_bytes_literal():
x = b"source"
SINK(x) #$ flow="b'source', l:66 -> x"
SINK(x) #$ flow="b'source', l:-1 -> x"
def test_integer_literal():
x = 42
SINK(x) #$ flow="42, l:71 -> x"
SINK(x) #$ flow="42, l:-1 -> x"
def test_floatnumber_literal():
x = 42.0
SINK(x) #$ flow="42.0, l:76 -> x"
SINK(x) #$ flow="42.0, l:-1 -> x"
def test_imagnumber_literal():
x = 42j
SINK(x) #$ MISSING:flow="42j, l:81 -> x"
SINK(x) #$ MISSING:flow="42j, l:-1 -> x"
# 6.2.3. Parenthesized forms
def test_parenthesized_form():
x = (SOURCE)
SINK(x) #$ flow="SOURCE, l:87 -> x"
SINK(x) #$ flow="SOURCE, l:-1 -> x"
# 6.2.5. List displays
def test_list_display():
x = [SOURCE]
SINK(x[0]) #$ flow="SOURCE, l:93 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> x[0]"
def test_list_display_negative():
@@ -101,103 +101,103 @@ def test_list_display_negative():
def test_list_comprehension():
x = [SOURCE for y in [NONSOURCE]]
SINK(x[0]) #$ flow="SOURCE, l:103 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> x[0]"
def test_list_comprehension_flow():
x = [y for y in [SOURCE]]
SINK(x[0]) #$ flow="SOURCE, l:108 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> x[0]"
def test_list_comprehension_inflow():
l = [SOURCE]
x = [y for y in l]
SINK(x[0]) #$ flow="SOURCE, l:113 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-2 -> x[0]"
def test_nested_list_display():
x = [*[SOURCE]]
SINK(x[0]) #$ MISSING:flow="SOURCE, l:119 -> x[0]"
SINK(x[0]) #$ MISSING:flow="SOURCE, l:-1 -> x[0]"
# 6.2.6. Set displays
def test_set_display():
x = {SOURCE}
SINK(x.pop()) #$ flow="SOURCE, l:125 -> x.pop()"
SINK(x.pop()) #$ flow="SOURCE, l:-1 -> x.pop()"
def test_set_comprehension():
x = {SOURCE for y in [NONSOURCE]}
SINK(x.pop()) #$ flow="SOURCE, l:130 -> x.pop()"
SINK(x.pop()) #$ flow="SOURCE, l:-1 -> x.pop()"
def test_set_comprehension_flow():
x = {y for y in [SOURCE]}
SINK(x.pop()) #$ flow="SOURCE, l:135 -> x.pop()"
SINK(x.pop()) #$ flow="SOURCE, l:-1 -> x.pop()"
def test_set_comprehension_inflow():
l = {SOURCE}
x = {y for y in l}
SINK(x.pop()) #$ flow="SOURCE, l:140 -> x.pop()"
SINK(x.pop()) #$ flow="SOURCE, l:-2 -> x.pop()"
def test_nested_set_display():
x = {*{SOURCE}}
SINK(x.pop()) #$ MISSING:flow="SOURCE, l:146 -> x.pop()"
SINK(x.pop()) #$ MISSING:flow="SOURCE, l:-1 -> x.pop()"
# 6.2.7. Dictionary displays
def test_dict_display():
x = {"s": SOURCE}
SINK(x["s"]) #$ flow="SOURCE, l:152 -> x['s']"
SINK(x["s"]) #$ flow="SOURCE, l:-1 -> x['s']"
def test_dict_display_pop():
x = {"s": SOURCE}
SINK(x.pop("s")) #$ flow="SOURCE, l:157 -> x.pop(..)"
SINK(x.pop("s")) #$ flow="SOURCE, l:-1 -> x.pop(..)"
def test_dict_comprehension():
x = {y: SOURCE for y in ["s"]}
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:152 -> x['s']"
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:-1 -> x['s']"
def test_dict_comprehension_pop():
x = {y: SOURCE for y in ["s"]}
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:167 -> x.pop()"
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:-1 -> x.pop()"
def test_nested_dict_display():
x = {**{"s": SOURCE}}
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:172 -> x['s']"
SINK(x["s"]) #$ MISSING:flow="SOURCE, l:-1 -> x['s']"
def test_nested_dict_display_pop():
x = {**{"s": SOURCE}}
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:177 -> x.pop()"
SINK(x.pop("s")) #$ MISSING:flow="SOURCE, l:-1 -> x.pop()"
# Nested comprehensions
def test_nested_comprehension():
x = [y for z in [[SOURCE]] for y in z]
SINK(x[0]) #$ flow="SOURCE, l:183 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> 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="SOURCE, l:188 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> 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="SOURCE, l:193 -> x[0]"
SINK(x[0]) #$ MISSING:flow="SOURCE, l:-1 -> x[0]"
def test_nested_comprehension_paren():
x = [y for y in (z for z in [SOURCE])]
SINK(x[0]) #$ flow="SOURCE, l:199 -> x[0]"
SINK(x[0]) #$ flow="SOURCE, l:-1 -> x[0]"
# 6.2.8. Generator expressions
@@ -228,7 +228,7 @@ def test_yield_from():
# a statement rather than an expression, but related to generators
def test_for():
for x in gen(SOURCE):
SINK(x) #$ MISSING:flow="SOURCE, l:230 -> x"
SINK(x) #$ MISSING:flow="SOURCE, l:-1 -> x"
# 6.2.9.1. Generator-iterator methods
@@ -508,12 +508,12 @@ def test_lambda_extra_keyword_flow():
def test_swap():
a = SOURCE
b = NONSOURCE
SINK(a) #$ flow="SOURCE, l:509 -> a"
SINK(a) #$ flow="SOURCE, l:-2 -> a"
SINK_F(b)
a, b = b, a
SINK_F(a)
SINK(b) #$ flow="SOURCE, l:509 -> b"
SINK(b) #$ flow="SOURCE, l:-7 -> b"
def test_deep_callgraph():
@@ -538,7 +538,7 @@ def test_deep_callgraph():
return f5(arg)
x = f6(SOURCE)
SINK(x) #$ MISSING:flow="SOURCE, l:540 -> x"
SINK(x) #$ MISSING:flow="SOURCE, l:-1 -> x"
@expects(2)