From 003fece49049db5d63af5a75b46627443dac4aa2 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 26 Apr 2023 14:52:40 +0200 Subject: [PATCH] python: add test for capturing via `global` --- .../test/experimental/dataflow/validTest.py | 1 + .../dataflow/variable-capture/global.py | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 python/ql/test/experimental/dataflow/variable-capture/global.py diff --git a/python/ql/test/experimental/dataflow/validTest.py b/python/ql/test/experimental/dataflow/validTest.py index 02acc17249d..21e5e013cb4 100644 --- a/python/ql/test/experimental/dataflow/validTest.py +++ b/python/ql/test/experimental/dataflow/validTest.py @@ -68,6 +68,7 @@ if __name__ == "__main__": check_tests_valid("coverage-py3.classes") check_tests_valid("variable-capture.in") check_tests_valid("variable-capture.nonlocal") + check_tests_valid("variable-capture.global") check_tests_valid("variable-capture.dict") check_tests_valid("variable-capture.test_collections") check_tests_valid("module-initialization.multiphase") diff --git a/python/ql/test/experimental/dataflow/variable-capture/global.py b/python/ql/test/experimental/dataflow/variable-capture/global.py new file mode 100644 index 00000000000..b7096f53410 --- /dev/null +++ b/python/ql/test/experimental/dataflow/variable-capture/global.py @@ -0,0 +1,102 @@ +# Here we test writing to a captured variable via the `nonlocal` keyword (see `out`). +# We also test reading one captured variable and writing the value to another (see `through`). + +# 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") + + +sinkO1 = "" +sinkO2 = "" +nonSink0 = "" + +def out(): + def captureOut1(): + global sinkO1 + sinkO1 = SOURCE + captureOut1() + SINK(sinkO1) #$ captured + + def captureOut2(): + def m(): + global sinkO2 + sinkO2 = SOURCE + m() + captureOut2() + SINK(sinkO2) #$ captured + + def captureOut1NotCalled(): + global nonSink0 + nonSink0 = SOURCE + SINK_F(nonSink0) #$ SPURIOUS: captured + + def captureOut2NotCalled(): + def m(): + global nonSink0 + nonSink0 = SOURCE + captureOut2NotCalled() + SINK_F(nonSink0) #$ SPURIOUS: captured + +@expects(4) +def test_out(): + out() + +sinkT1 = "" +sinkT2 = "" +nonSinkT0 = "" +def through(tainted): + def captureOut1(): + global sinkT1 + sinkT1 = tainted + captureOut1() + SINK(sinkT1) #$ MISSING:captured + + def captureOut2(): + def m(): + global sinkT2 + sinkT2 = tainted + m() + captureOut2() + SINK(sinkT2) #$ MISSING:captured + + def captureOut1NotCalled(): + global nonSinkT0 + nonSinkT0 = tainted + SINK_F(nonSinkT0) + + def captureOut2NotCalled(): + def m(): + global nonSinkT0 + nonSinkT0 = tainted + captureOut2NotCalled() + SINK_F(nonSinkT0) + +@expects(4) +def test_through(): + through(SOURCE)