Python: Fix SINK/SINK_F usage for crosstalk tests

As discussed in PR review
https://github.com/github/codeql/pull/11208#discussion_r1022473421
This commit is contained in:
Rasmus Wriedt Larsen
2022-11-22 11:01:26 +01:00
parent e886b53a94
commit d876acde4c
3 changed files with 25 additions and 10 deletions

View File

@@ -16,8 +16,9 @@ class DataFlowTest extends FlowTest {
query predicate missingAnnotationOnSink(Location location, string error, string element) {
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
exists(DataFlow::Node sink |
any(TestConfiguration config).isSink(sink) and
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
exists(DataFlow::CallCfgNode call |
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
(sink = call.getArg(_) or sink = call.getArgByName(_))
) and

View File

@@ -13,7 +13,17 @@ def is_source(x):
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
def SINK(x):
def SINK(x, *, not_present_at_runtime=False):
# not_present_at_runtime supports use-cases where we want flow from data-flow layer
# (so we want to use SINK), but we end up in a siaution where it's not possible to
# actually get flow from a source at runtime. The only use-case is for the
# cross-talk tests, where our ability to use if-then-else is limited because doing
# so would make cfg-splitting kick in, and that would solve the problem trivially
# (by the splitting).
if not_present_at_runtime:
print("OK")
return
if is_source(x):
print("OK")
else:
@@ -186,6 +196,9 @@ def test_nested_obj_method():
# ------------------------------------------------------------------------------
# Crosstalk test -- using different function based on conditional
# ------------------------------------------------------------------------------
# NOTE: These tests use `SINK(objy.y, not_present_at_runtime=True)` since it's not
# possible to use if-then-else statements, since that would make cfg-splitting kick in,
# and that would solve the problem trivially (by the splitting).
class CrosstalkTestX:
def __init__(self):
@@ -229,7 +242,7 @@ def test_no_crosstalk_reference(cond=True):
SINK(objx.x) # $ flow="SOURCE, l:-4 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK_F(objy.y) # $ flow="SOURCE, l:-5 -> objy.y"
SINK(objy.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-5 -> objy.y"
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
@@ -252,7 +265,7 @@ def test_potential_crosstalk_different_name(cond=True):
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
@@ -275,7 +288,7 @@ def test_potential_crosstalk_same_name(cond=True):
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
@expects(10) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..)
@@ -298,10 +311,10 @@ def test_potential_crosstalk_same_name_object_reference(cond=True):
SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x"
SINK_F(objx.y)
SINK_F(objy.x)
SINK_F(objy.y) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y"
SINK(obj.x) # $ flow="SOURCE, l:-7 -> obj.x"
SINK_F(obj.y) # $ flow="SOURCE, l:-8 -> obj.y"
SINK(obj.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-8 -> obj.y"
# ------------------------------------------------------------------------------

View File

@@ -38,9 +38,10 @@ class TestConfiguration extends DataFlow::Configuration {
}
override predicate isSink(DataFlow::Node node) {
exists(CallNode call |
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and
(node = call.getArg(_) or node = call.getArgByName(_)) and
not node = call.getArgByName("not_present_at_runtime")
)
}