Merge branch 'main' of github.com:github/codeql into python-support-pathlib

This commit is contained in:
Rasmus Lerchedahl Petersen
2021-04-22 15:04:21 +02:00
237 changed files with 5083 additions and 3039 deletions

View File

@@ -9,7 +9,11 @@ class ApiUseTest extends InlineExpectationsTest {
override string getARelevantTag() { result = "use" }
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
n = a.getAUse() and l = n.getLocation()
n = a.getAUse() and
l = n.getLocation() and
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
// from the inline tests.
not n instanceof DataFlow::ModuleVariableNode
}
override predicate hasActualResult(Location location, string element, string tag, string value) {

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -1,4 +1,4 @@
import experimental.dataflow.tainttracking.TestTaintLib
import experimental.meta.InlineTaintTest
import semmle.python.dataflow.new.BarrierGuards
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {

View File

@@ -1,27 +0,0 @@
| test_string_const_compare.py:16 | ok | test_eq | ts |
| test_string_const_compare.py:18 | ok | test_eq | ts |
| test_string_const_compare.py:20 | ok | test_eq | ts |
| test_string_const_compare.py:27 | ok | test_eq_unsafe | ts |
| test_string_const_compare.py:29 | ok | test_eq_unsafe | ts |
| test_string_const_compare.py:35 | fail | test_eq_with_or | ts |
| test_string_const_compare.py:37 | ok | test_eq_with_or | ts |
| test_string_const_compare.py:43 | ok | test_non_eq1 | ts |
| test_string_const_compare.py:45 | ok | test_non_eq1 | ts |
| test_string_const_compare.py:51 | ok | test_non_eq2 | ts |
| test_string_const_compare.py:53 | fail | test_non_eq2 | ts |
| test_string_const_compare.py:59 | ok | test_in_list | ts |
| test_string_const_compare.py:61 | ok | test_in_list | ts |
| test_string_const_compare.py:67 | ok | test_in_tuple | ts |
| test_string_const_compare.py:69 | ok | test_in_tuple | ts |
| test_string_const_compare.py:75 | ok | test_in_set | ts |
| test_string_const_compare.py:77 | ok | test_in_set | ts |
| test_string_const_compare.py:83 | ok | test_in_unsafe1 | ts |
| test_string_const_compare.py:85 | ok | test_in_unsafe1 | ts |
| test_string_const_compare.py:91 | ok | test_in_unsafe2 | ts |
| test_string_const_compare.py:93 | ok | test_in_unsafe2 | ts |
| test_string_const_compare.py:99 | ok | test_not_in1 | ts |
| test_string_const_compare.py:101 | ok | test_not_in1 | ts |
| test_string_const_compare.py:107 | ok | test_not_in2 | ts |
| test_string_const_compare.py:109 | fail | test_not_in2 | ts |
| test_string_const_compare.py:119 | fail | test_eq_thorugh_func | ts |
| test_string_const_compare.py:121 | ok | test_eq_thorugh_func | ts |

View File

@@ -15,32 +15,32 @@ def test_eq():
if ts == "safe":
ensure_not_tainted(ts)
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
# ts should still be tainted after exiting the if block
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_eq_unsafe(x="foo"):
"""This test-case might seem strange, but it was a FP in our old points-to based analysis."""
ts = TAINTED_STRING
if ts == ts:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
if ts == x:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_eq_with_or():
ts = TAINTED_STRING
if ts == "safe" or ts == "also_safe":
ensure_not_tainted(ts)
ensure_not_tainted(ts) # $ SPURIOUS: tainted
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_non_eq1():
ts = TAINTED_STRING
if ts != "safe":
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_not_tainted(ts)
@@ -48,9 +48,9 @@ def test_non_eq1():
def test_non_eq2():
ts = TAINTED_STRING
if not ts == "safe":
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_not_tainted(ts)
ensure_not_tainted(ts) # $ SPURIOUS: tainted
def test_in_list():
@@ -58,7 +58,7 @@ def test_in_list():
if ts in ["safe", "also_safe"]:
ensure_not_tainted(ts)
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_in_tuple():
@@ -66,7 +66,7 @@ def test_in_tuple():
if ts in ("safe", "also_safe"):
ensure_not_tainted(ts)
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_in_set():
@@ -74,29 +74,29 @@ def test_in_set():
if ts in {"safe", "also_safe"}:
ensure_not_tainted(ts)
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_in_unsafe1(xs):
ts = TAINTED_STRING
if ts in xs:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_in_unsafe2(x):
ts = TAINTED_STRING
if ts in ["safe", x]:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
def test_not_in1():
ts = TAINTED_STRING
if ts not in ["safe", "also_safe"]:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_not_tainted(ts)
@@ -104,9 +104,9 @@ def test_not_in1():
def test_not_in2():
ts = TAINTED_STRING
if not ts in ["safe", "also_safe"]:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
else:
ensure_not_tainted(ts)
ensure_not_tainted(ts) # $ SPURIOUS: tainted
def is_safe(x):
@@ -116,9 +116,9 @@ def is_safe(x):
def test_eq_thorugh_func():
ts = TAINTED_STRING
if is_safe(ts):
ensure_not_tainted(ts)
ensure_not_tainted(ts) # $ SPURIOUS: tainted
else:
ensure_tainted(ts)
ensure_tainted(ts) # $ tainted
# Make tests runable

View File

@@ -0,0 +1,20 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures
isSanitizer
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test.py:50:10:50:29 | ControlFlowNode for emulated_escaping() |
isSanitizerGuard
| TestTaintTrackingConfiguration | test.py:35:8:35:26 | ControlFlowNode for emulated_is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:29:8:29:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:44:8:44:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:52:12:52:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:72:8:72:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:80:12:80:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:104:8:104:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:127:12:127:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:132:16:132:25 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:137:20:137:29 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:30:8:30:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:40:8:40:25 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:55:8:55:21 | ControlFlowNode for is_safe() |

View File

@@ -1,4 +1,4 @@
import experimental.dataflow.tainttracking.TestTaintLib
import experimental.meta.InlineTaintTest
class IsSafeCheck extends DataFlow::BarrierGuard {
IsSafeCheck() {

View File

@@ -1,61 +0,0 @@
test_taint
| test.py:22 | ok | test_custom_sanitizer | s |
| test.py:36 | ok | test_custom_sanitizer_guard | s |
| test.py:38 | ok | test_custom_sanitizer_guard | s |
| test.py:40 | ok | test_custom_sanitizer_guard | s |
| test.py:51 | ok | test_escape | s2 |
| test_logical.py:30 | ok | test_basic | s |
| test_logical.py:32 | ok | test_basic | s |
| test_logical.py:35 | ok | test_basic | s |
| test_logical.py:37 | fail | test_basic | s |
| test_logical.py:45 | ok | test_or | s |
| test_logical.py:47 | ok | test_or | s |
| test_logical.py:51 | ok | test_or | s |
| test_logical.py:53 | ok | test_or | s |
| test_logical.py:57 | ok | test_or | s |
| test_logical.py:59 | ok | test_or | s |
| test_logical.py:67 | ok | test_and | s |
| test_logical.py:69 | ok | test_and | s |
| test_logical.py:73 | ok | test_and | s |
| test_logical.py:75 | ok | test_and | s |
| test_logical.py:79 | ok | test_and | s |
| test_logical.py:81 | fail | test_and | s |
| test_logical.py:89 | fail | test_tricky | s |
| test_logical.py:93 | fail | test_tricky | s_ |
| test_logical.py:100 | fail | test_nesting_not | s |
| test_logical.py:102 | ok | test_nesting_not | s |
| test_logical.py:105 | ok | test_nesting_not | s |
| test_logical.py:107 | fail | test_nesting_not | s |
| test_logical.py:116 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:118 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:121 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:123 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:126 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:128 | ok | test_nesting_not_with_and_true | s |
| test_logical.py:137 | fail | test_with_return | s |
| test_logical.py:146 | fail | test_with_exception | s |
| test_reference.py:31 | fail | test_basic | s2 |
| test_reference.py:31 | ok | test_basic | s |
| test_reference.py:33 | ok | test_basic | s |
| test_reference.py:33 | ok | test_basic | s2 |
| test_reference.py:41 | fail | test_identical_call | s.strip() |
| test_reference.py:43 | ok | test_identical_call | s.strip() |
| test_reference.py:56 | fail | test_class_attribute_access | c.foo |
| test_reference.py:58 | ok | test_class_attribute_access | c.foo |
isSanitizer
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
| TestTaintTrackingConfiguration | test.py:50:10:50:29 | ControlFlowNode for emulated_escaping() |
isSanitizerGuard
| TestTaintTrackingConfiguration | test.py:35:8:35:26 | ControlFlowNode for emulated_is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:29:8:29:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:44:8:44:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:50:12:50:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:66:8:66:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:72:12:72:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:92:8:92:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:115:12:115:21 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:120:16:120:25 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_logical.py:125:20:125:29 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:30:8:30:17 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:40:8:40:25 | ControlFlowNode for is_safe() |
| TestTaintTrackingConfiguration | test_reference.py:55:8:55:21 | ControlFlowNode for is_safe() |

View File

@@ -35,9 +35,9 @@ def test_custom_sanitizer_guard():
if emulated_is_safe(s):
ensure_not_tainted(s)
s = TAINTED_STRING
ensure_tainted(s)
ensure_tainted(s) # $ tainted
else:
ensure_tainted(s)
ensure_tainted(s) # $ tainted
def emulated_escaping(arg):

View File

@@ -29,12 +29,12 @@ def test_basic():
if is_safe(s):
ensure_not_tainted(s)
else:
ensure_tainted(s)
ensure_tainted(s) # $ tainted
if not is_safe(s):
ensure_tainted(s)
ensure_tainted(s) # $ tainted
else:
ensure_not_tainted(s)
ensure_not_tainted(s) # $ SPURIOUS: tainted
def test_or():
@@ -42,21 +42,27 @@ def test_or():
# x or y
if is_safe(s) or random_choice():
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
else:
ensure_tainted(s) # must be tainted
# must be tainted
ensure_tainted(s) # $ tainted
# not (x or y)
if not(is_safe(s) or random_choice()):
ensure_tainted(s) # must be tainted
# must be tainted
ensure_tainted(s) # $ tainted
else:
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
# not (x or y) == not x and not y [de Morgan's laws]
if not is_safe(s) and not random_choice():
ensure_tainted(s) # must be tainted
# must be tainted
ensure_tainted(s) # $ tainted
else:
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
def test_and():
@@ -64,21 +70,27 @@ def test_and():
# x and y
if is_safe(s) and random_choice():
ensure_not_tainted(s) # must not be tainted
# cannot be tainted
ensure_not_tainted(s)
else:
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
# not (x and y)
if not(is_safe(s) and random_choice()):
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
else:
# cannot be tainted
ensure_not_tainted(s)
# not (x and y) == not x or not y [de Morgan's laws]
if not is_safe(s) or not random_choice():
ensure_tainted(s) # might be tainted
# might be tainted
ensure_tainted(s) # $ tainted
else:
ensure_not_tainted(s)
# cannot be tainted
ensure_not_tainted(s) # $ SPURIOUS: tainted
def test_tricky():
@@ -86,25 +98,25 @@ def test_tricky():
x = is_safe(s)
if x:
ensure_not_tainted(s) # FP
ensure_not_tainted(s) # $ SPURIOUS: tainted
s_ = s
if is_safe(s):
ensure_not_tainted(s_) # FP
ensure_not_tainted(s_) # $ SPURIOUS: tainted
def test_nesting_not():
s = TAINTED_STRING
if not(not(is_safe(s))):
ensure_not_tainted(s)
ensure_not_tainted(s) # $ SPURIOUS: tainted
else:
ensure_tainted(s)
ensure_tainted(s) # $ tainted
if not(not(not(is_safe(s)))):
ensure_tainted(s)
ensure_tainted(s) # $ tainted
else:
ensure_not_tainted(s)
ensure_not_tainted(s) # $ SPURIOUS: tainted
# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in
@@ -113,17 +125,17 @@ def test_nesting_not_with_and_true():
s = TAINTED_STRING
if not(is_safe(s) and True):
ensure_tainted(s)
ensure_tainted(s) # $ tainted
else:
ensure_not_tainted(s)
if not(not(is_safe(s) and True)):
ensure_not_tainted(s)
else:
ensure_tainted(s)
ensure_tainted(s) # $ tainted
if not(not(not(is_safe(s) and True))):
ensure_tainted(s)
ensure_tainted(s) # $ tainted
else:
ensure_not_tainted(s)
@@ -134,7 +146,7 @@ def test_with_return():
if not is_safe(s):
return
ensure_not_tainted(s)
ensure_not_tainted(s) # $ SPURIOUS: tainted
def test_with_exception():
@@ -143,7 +155,7 @@ def test_with_exception():
if not is_safe(s):
raise Exception("unsafe")
ensure_not_tainted(s)
ensure_not_tainted(s) # $ SPURIOUS: tainted
# Make tests runable

View File

@@ -28,9 +28,9 @@ def test_basic():
s2 = s
if is_safe(s):
ensure_not_tainted(s, s2)
ensure_not_tainted(s, s2) # $ SPURIOUS: tainted
else:
ensure_tainted(s, s2)
ensure_tainted(s, s2) # $ tainted
def test_identical_call():
@@ -38,9 +38,9 @@ def test_identical_call():
s = TAINTED_STRING
if is_safe(s.strip()):
ensure_not_tainted(s.strip())
ensure_not_tainted(s.strip()) # $ SPURIOUS: tainted
else:
ensure_tainted(s.strip())
ensure_tainted(s.strip()) # $ tainted
class C(object):
@@ -53,9 +53,9 @@ def test_class_attribute_access():
c = C(s)
if is_safe(c.foo):
ensure_not_tainted(c.foo)
ensure_not_tainted(c.foo) # $ SPURIOUS: tainted
else:
ensure_tainted(c.foo)
ensure_tainted(c.foo) # $ tainted
# Make tests runable

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,34 +0,0 @@
| test_collections.py:16 | ok | test_access | tainted_list.copy() |
| test_collections.py:24 | ok | list_clear | tainted_list |
| test_collections.py:27 | fail | list_clear | tainted_list |
| test_pathlib.py:26 | ok | test_basic | tainted_path |
| test_pathlib.py:28 | ok | test_basic | tainted_pure_path |
| test_pathlib.py:29 | ok | test_basic | tainted_pure_posix_path |
| test_pathlib.py:30 | ok | test_basic | tainted_pure_windows_path |
| test_pathlib.py:32 | ok | test_basic | BinaryExpr |
| test_pathlib.py:33 | ok | test_basic | BinaryExpr |
| test_pathlib.py:35 | ok | test_basic | tainted_path.joinpath(..) |
| test_pathlib.py:36 | ok | test_basic | pathlib.Path(..).joinpath(..) |
| test_pathlib.py:37 | ok | test_basic | pathlib.Path(..).joinpath(..) |
| test_pathlib.py:39 | ok | test_basic | str(..) |
| test_pathlib.py:49 | ok | test_basic | tainted_posix_path |
| test_pathlib.py:55 | ok | test_basic | tainted_windows_path |
| test_string.py:17 | ok | str_methods | ts.casefold() |
| test_string.py:19 | ok | str_methods | ts.format_map(..) |
| test_string.py:20 | ok | str_methods | "{unsafe}".format_map(..) |
| test_string.py:31 | ok | binary_decode_encode | base64.a85encode(..) |
| test_string.py:32 | ok | binary_decode_encode | base64.a85decode(..) |
| test_string.py:35 | ok | binary_decode_encode | base64.b85encode(..) |
| test_string.py:36 | ok | binary_decode_encode | base64.b85decode(..) |
| test_string.py:39 | ok | binary_decode_encode | base64.encodebytes(..) |
| test_string.py:40 | ok | binary_decode_encode | base64.decodebytes(..) |
| test_string.py:48 | ok | f_strings | Fstring |
| test_unpacking.py:18 | ok | extended_unpacking | first |
| test_unpacking.py:18 | ok | extended_unpacking | last |
| test_unpacking.py:18 | ok | extended_unpacking | rest |
| test_unpacking.py:23 | ok | also_allowed | a |
| test_unpacking.py:31 | ok | also_allowed | b |
| test_unpacking.py:31 | ok | also_allowed | c |
| test_unpacking.py:39 | ok | nested | x |
| test_unpacking.py:39 | ok | nested | xs |
| test_unpacking.py:39 | ok | nested | ys |

View File

@@ -1 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -13,7 +13,7 @@ def test_access():
tainted_list = TAINTED_LIST
ensure_tainted(
tainted_list.copy(),
tainted_list.copy(), # $ tainted
)
@@ -21,10 +21,10 @@ def list_clear():
tainted_string = TAINTED_STRING
tainted_list = [tainted_string]
ensure_tainted(tainted_list)
ensure_tainted(tainted_list) # $ tainted
tainted_list.clear()
ensure_not_tainted(tainted_list)
ensure_not_tainted(tainted_list) # $ SPURIOUS: tainted
# Make tests runable

View File

@@ -23,20 +23,20 @@ def test_basic():
tainted_pure_windows_path = pathlib.PureWindowsPath(ts)
ensure_tainted(
tainted_path,
tainted_path, # $ tainted
tainted_pure_path,
tainted_pure_posix_path,
tainted_pure_windows_path,
tainted_pure_path, # $ tainted
tainted_pure_posix_path, # $ tainted
tainted_pure_windows_path, # $ tainted
pathlib.Path("foo") / ts,
ts / pathlib.Path("foo"),
pathlib.Path("foo") / ts, # $ tainted
ts / pathlib.Path("foo"), # $ tainted
tainted_path.joinpath("foo", "bar"),
pathlib.Path("foo").joinpath(tainted_path, "bar"),
pathlib.Path("foo").joinpath("bar", tainted_path),
tainted_path.joinpath("foo", "bar"), # $ tainted
pathlib.Path("foo").joinpath(tainted_path, "bar"), # $ tainted
pathlib.Path("foo").joinpath("bar", tainted_path), # $ tainted
str(tainted_path),
str(tainted_path), # $ tainted
# TODO: Tainted methods and attributes
# https://docs.python.org/3.8/library/pathlib.html#methods-and-properties
@@ -46,13 +46,13 @@ def test_basic():
tainted_posix_path = pathlib.PosixPath(ts)
ensure_tainted(
tainted_posix_path,
tainted_posix_path, # $ tainted
)
if os.name == "nt":
tainted_windows_path = pathlib.WindowsPath(ts)
ensure_tainted(
tainted_windows_path,
tainted_windows_path, # $ tainted
)
# Make tests runable

View File

@@ -14,10 +14,10 @@ def str_methods():
ts = TAINTED_STRING
tb = TAINTED_BYTES
ensure_tainted(
ts.casefold(),
ts.casefold(), # $ tainted
ts.format_map({}),
"{unsafe}".format_map({"unsafe": ts}),
ts.format_map({}), # $ tainted
"{unsafe}".format_map({"unsafe": ts}), # $ tainted
)
@@ -28,16 +28,16 @@ def binary_decode_encode():
ensure_tainted(
# New in Python 3.4
base64.a85encode(tb),
base64.a85decode(base64.a85encode(tb)),
base64.a85encode(tb), # $ tainted
base64.a85decode(base64.a85encode(tb)), # $ tainted
# New in Python 3.4
base64.b85encode(tb),
base64.b85decode(base64.b85encode(tb)),
base64.b85encode(tb), # $ tainted
base64.b85decode(base64.b85encode(tb)), # $ tainted
# New in Python 3.1
base64.encodebytes(tb),
base64.decodebytes(base64.encodebytes(tb)),
base64.encodebytes(tb), # $ tainted
base64.decodebytes(base64.encodebytes(tb)), # $ tainted
)
@@ -45,7 +45,7 @@ def f_strings():
print("\n# f_strings")
ts = TAINTED_STRING
ensure_tainted(f"foo {ts} bar")
ensure_tainted(f"foo {ts} bar") # $ tainted
# Make tests runable

View File

@@ -15,12 +15,12 @@ if TYPE_CHECKING:
def extended_unpacking():
first, *rest, last = TAINTED_LIST
ensure_tainted(first, rest, last)
ensure_tainted(first, rest, last) # $ tainted
def also_allowed():
*a, = TAINTED_LIST
ensure_tainted(a)
ensure_tainted(a) # $ tainted
# for b, *c in [(1, 2, 3), (4, 5, 6, 7)]:
# print(c)
@@ -28,7 +28,7 @@ def also_allowed():
# i=1; c=[5,6,7]
for b, *c in [TAINTED_LIST, TAINTED_LIST]:
ensure_tainted(b, c)
ensure_tainted(b, c) # $ tainted
def nested():
@@ -36,7 +36,7 @@ def nested():
ll = [l,l]
[[x, *xs], ys] = ll
ensure_tainted(x, xs, ys)
ensure_tainted(x, xs, ys) # $ tainted
# Make tests runable

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,176 +0,0 @@
| test_collections.py:23 | ok | test_construction | tainted_string |
| test_collections.py:24 | ok | test_construction | tainted_list |
| test_collections.py:25 | ok | test_construction | tainted_tuple |
| test_collections.py:26 | ok | test_construction | tainted_set |
| test_collections.py:27 | ok | test_construction | tainted_dict |
| test_collections.py:31 | ok | test_construction | list(..) |
| test_collections.py:32 | ok | test_construction | list(..) |
| test_collections.py:33 | ok | test_construction | list(..) |
| test_collections.py:34 | ok | test_construction | list(..) |
| test_collections.py:35 | ok | test_construction | list(..) |
| test_collections.py:37 | ok | test_construction | tuple(..) |
| test_collections.py:38 | ok | test_construction | set(..) |
| test_collections.py:39 | ok | test_construction | frozenset(..) |
| test_collections.py:47 | ok | test_access | tainted_list[0] |
| test_collections.py:48 | ok | test_access | tainted_list[x] |
| test_collections.py:49 | ok | test_access | tainted_list[Slice] |
| test_collections.py:51 | ok | test_access | sorted(..) |
| test_collections.py:52 | ok | test_access | reversed(..) |
| test_collections.py:53 | ok | test_access | iter(..) |
| test_collections.py:54 | ok | test_access | next(..) |
| test_collections.py:58 | ok | test_access | a |
| test_collections.py:58 | ok | test_access | b |
| test_collections.py:58 | ok | test_access | c |
| test_collections.py:61 | ok | test_access | h |
| test_collections.py:63 | ok | test_access | i |
| test_collections.py:70 | ok | test_dict_access | tainted_dict["name"] |
| test_collections.py:71 | ok | test_dict_access | tainted_dict.get(..) |
| test_collections.py:72 | ok | test_dict_access | tainted_dict[x] |
| test_collections.py:73 | ok | test_dict_access | tainted_dict.copy() |
| test_collections.py:77 | ok | test_dict_access | v |
| test_collections.py:79 | ok | test_dict_access | v |
| test_collections.py:87 | fail | test_named_tuple | point[0] |
| test_collections.py:88 | fail | test_named_tuple | point.x |
| test_collections.py:92 | ok | test_named_tuple | point[1] |
| test_collections.py:93 | ok | test_named_tuple | point.y |
| test_collections.py:97 | fail | test_named_tuple | a |
| test_collections.py:98 | ok | test_named_tuple | b |
| test_collections.py:106 | fail | test_defaultdict | tainted_default_dict["name"] |
| test_collections.py:107 | fail | test_defaultdict | tainted_default_dict.get(..) |
| test_collections.py:108 | fail | test_defaultdict | tainted_default_dict[x] |
| test_collections.py:109 | fail | test_defaultdict | tainted_default_dict.copy() |
| test_collections.py:112 | fail | test_defaultdict | v |
| test_collections.py:114 | fail | test_defaultdict | v |
| test_collections.py:121 | ok | test_copy_1 | copy(..) |
| test_collections.py:122 | ok | test_copy_1 | deepcopy(..) |
| test_collections.py:130 | ok | test_copy_2 | copy.copy(..) |
| test_collections.py:131 | ok | test_copy_2 | copy.deepcopy(..) |
| test_collections.py:139 | ok | list_index_assign | my_list |
| test_collections.py:142 | fail | list_index_assign | my_list |
| test_collections.py:149 | ok | list_index_aug_assign | my_list |
| test_collections.py:152 | fail | list_index_aug_assign | my_list |
| test_collections.py:159 | ok | list_append | my_list |
| test_collections.py:162 | ok | list_append | my_list |
| test_collections.py:169 | ok | list_extend | my_list |
| test_collections.py:172 | fail | list_extend | my_list |
| test_collections.py:179 | ok | dict_update_dict | my_dict |
| test_collections.py:182 | fail | dict_update_dict | my_dict |
| test_collections.py:189 | ok | dict_update_kv_list | my_dict |
| test_collections.py:192 | fail | dict_update_kv_list | my_dict |
| test_collections.py:198 | ok | dict_update_kv_arg | my_dict |
| test_collections.py:201 | fail | dict_update_kv_arg | my_dict |
| test_collections.py:208 | ok | dict_manual_update | my_dict |
| test_collections.py:212 | fail | dict_manual_update | my_dict |
| test_collections.py:220 | fail | dict_merge | merged |
| test_collections.py:227 | ok | set_add | my_set |
| test_collections.py:230 | ok | set_add | my_set |
| test_json.py:26 | ok | test | json.dumps(..) |
| test_json.py:27 | ok | test | json.loads(..) |
| test_json.py:34 | fail | test | tainted_filelike |
| test_json.py:35 | fail | test | json.load(..) |
| test_json.py:48 | ok | non_syntacical | dumps(..) |
| test_json.py:49 | ok | non_syntacical | dumps_alias(..) |
| test_json.py:50 | ok | non_syntacical | loads(..) |
| test_json.py:57 | fail | non_syntacical | tainted_filelike |
| test_json.py:58 | fail | non_syntacical | load(..) |
| test_string.py:25 | ok | str_operations | ts |
| test_string.py:26 | ok | str_operations | BinaryExpr |
| test_string.py:27 | ok | str_operations | BinaryExpr |
| test_string.py:28 | ok | str_operations | BinaryExpr |
| test_string.py:29 | ok | str_operations | ts[Slice] |
| test_string.py:30 | ok | str_operations | ts[Slice] |
| test_string.py:31 | ok | str_operations | ts[Slice] |
| test_string.py:32 | ok | str_operations | ts[0] |
| test_string.py:33 | ok | str_operations | str(..) |
| test_string.py:34 | ok | str_operations | bytes(..) |
| test_string.py:35 | ok | str_operations | unicode(..) |
| test_string.py:39 | ok | str_operations | aug_assignment |
| test_string.py:41 | ok | str_operations | aug_assignment |
| test_string.py:49 | ok | str_methods | ts.capitalize() |
| test_string.py:50 | ok | str_methods | ts.center(..) |
| test_string.py:51 | ok | str_methods | ts.expandtabs() |
| test_string.py:53 | ok | str_methods | ts.format() |
| test_string.py:54 | ok | str_methods | "{}".format(..) |
| test_string.py:55 | ok | str_methods | "{unsafe}".format(..) |
| test_string.py:57 | ok | str_methods | ts.join(..) |
| test_string.py:58 | ok | str_methods | "".join(..) |
| test_string.py:60 | ok | str_methods | ts.ljust(..) |
| test_string.py:61 | ok | str_methods | ts.lstrip() |
| test_string.py:62 | ok | str_methods | ts.lower() |
| test_string.py:64 | ok | str_methods | ts.replace(..) |
| test_string.py:65 | ok | str_methods | "safe".replace(..) |
| test_string.py:67 | ok | str_methods | ts.rjust(..) |
| test_string.py:68 | ok | str_methods | ts.rstrip() |
| test_string.py:69 | ok | str_methods | ts.strip() |
| test_string.py:70 | ok | str_methods | ts.swapcase() |
| test_string.py:71 | ok | str_methods | ts.title() |
| test_string.py:72 | ok | str_methods | ts.upper() |
| test_string.py:73 | ok | str_methods | ts.zfill(..) |
| test_string.py:75 | ok | str_methods | ts.encode(..) |
| test_string.py:76 | ok | str_methods | ts.encode(..).decode(..) |
| test_string.py:78 | ok | str_methods | tb.decode(..) |
| test_string.py:79 | ok | str_methods | tb.decode(..).encode(..) |
| test_string.py:82 | ok | str_methods | ts.partition(..) |
| test_string.py:83 | ok | str_methods | ts.rpartition(..) |
| test_string.py:84 | ok | str_methods | ts.rsplit(..) |
| test_string.py:85 | ok | str_methods | ts.split(..) |
| test_string.py:86 | ok | str_methods | ts.splitlines() |
| test_string.py:91 | ok | str_methods | "safe".replace(..) |
| test_string.py:93 | fail | str_methods | ts.join(..) |
| test_string.py:94 | fail | str_methods | ts.join(..) |
| test_string.py:104 | fail | non_syntactic | meth() |
| test_string.py:105 | fail | non_syntactic | _str(..) |
| test_string.py:114 | ok | percent_fmt | BinaryExpr |
| test_string.py:115 | ok | percent_fmt | BinaryExpr |
| test_string.py:116 | ok | percent_fmt | BinaryExpr |
| test_string.py:126 | ok | binary_decode_encode | base64.b64encode(..) |
| test_string.py:127 | ok | binary_decode_encode | base64.b64decode(..) |
| test_string.py:129 | ok | binary_decode_encode | base64.standard_b64encode(..) |
| test_string.py:130 | ok | binary_decode_encode | base64.standard_b64decode(..) |
| test_string.py:132 | ok | binary_decode_encode | base64.urlsafe_b64encode(..) |
| test_string.py:133 | ok | binary_decode_encode | base64.urlsafe_b64decode(..) |
| test_string.py:135 | ok | binary_decode_encode | base64.b32encode(..) |
| test_string.py:136 | ok | binary_decode_encode | base64.b32decode(..) |
| test_string.py:138 | ok | binary_decode_encode | base64.b16encode(..) |
| test_string.py:139 | ok | binary_decode_encode | base64.b16decode(..) |
| test_string.py:142 | ok | binary_decode_encode | base64.encodestring(..) |
| test_string.py:143 | ok | binary_decode_encode | base64.decodestring(..) |
| test_string.py:148 | fail | binary_decode_encode | quopri.encodestring(..) |
| test_string.py:149 | fail | binary_decode_encode | quopri.decodestring(..) |
| test_string.py:159 | ok | test_os_path_join | os.path.join(..) |
| test_string.py:160 | ok | test_os_path_join | os.path.join(..) |
| test_string.py:161 | ok | test_os_path_join | os.path.join(..) |
| test_string.py:162 | ok | test_os_path_join | ospath_alias.join(..) |
| test_unpacking.py:16 | ok | unpacking | a |
| test_unpacking.py:16 | ok | unpacking | b |
| test_unpacking.py:16 | ok | unpacking | c |
| test_unpacking.py:22 | ok | unpacking_to_list | a |
| test_unpacking.py:22 | ok | unpacking_to_list | b |
| test_unpacking.py:22 | ok | unpacking_to_list | c |
| test_unpacking.py:31 | ok | nested | a1 |
| test_unpacking.py:31 | ok | nested | a2 |
| test_unpacking.py:31 | ok | nested | a3 |
| test_unpacking.py:31 | ok | nested | b |
| test_unpacking.py:31 | ok | nested | c |
| test_unpacking.py:35 | ok | nested | a1 |
| test_unpacking.py:35 | ok | nested | a2 |
| test_unpacking.py:35 | ok | nested | a3 |
| test_unpacking.py:35 | ok | nested | b |
| test_unpacking.py:35 | ok | nested | c |
| test_unpacking.py:39 | ok | nested | a1 |
| test_unpacking.py:39 | ok | nested | a2 |
| test_unpacking.py:39 | ok | nested | a3 |
| test_unpacking.py:39 | ok | nested | b |
| test_unpacking.py:39 | ok | nested | c |
| test_unpacking.py:46 | ok | unpack_from_set | a |
| test_unpacking.py:46 | ok | unpack_from_set | b |
| test_unpacking.py:46 | ok | unpack_from_set | c |
| test_unpacking.py:55 | ok | contrived_1 | a |
| test_unpacking.py:55 | ok | contrived_1 | b |
| test_unpacking.py:55 | ok | contrived_1 | c |
| test_unpacking.py:56 | fail | contrived_1 | d |
| test_unpacking.py:56 | fail | contrived_1 | e |
| test_unpacking.py:56 | fail | contrived_1 | f |
| test_unpacking.py:65 | ok | contrived_2 | a |
| test_unpacking.py:65 | ok | contrived_2 | b |
| test_unpacking.py:65 | ok | contrived_2 | c |

View File

@@ -1 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -20,23 +20,23 @@ def test_construction():
tainted_dict = {'key': tainted_string}
ensure_tainted(
tainted_string,
tainted_list,
tainted_tuple,
tainted_set,
tainted_dict,
tainted_string, # $ tainted
tainted_list, # $ tainted
tainted_tuple, # $ tainted
tainted_set, # $ tainted
tainted_dict, # $ tainted
)
ensure_tainted(
list(tainted_list),
list(tainted_tuple),
list(tainted_set),
list(tainted_dict.values()),
list(tainted_dict.items()),
list(tainted_list), # $ tainted
list(tainted_tuple), # $ tainted
list(tainted_set), # $ tainted
list(tainted_dict.values()), # $ tainted
list(tainted_dict.items()), # $ tainted
tuple(tainted_list),
set(tainted_list),
frozenset(tainted_list),
tuple(tainted_list), # $ tainted
set(tainted_list), # $ tainted
frozenset(tainted_list), # $ tainted
)
@@ -44,39 +44,39 @@ def test_access(x, y, z):
tainted_list = TAINTED_LIST
ensure_tainted(
tainted_list[0],
tainted_list[x],
tainted_list[y:z],
tainted_list[0], # $ tainted
tainted_list[x], # $ tainted
tainted_list[y:z], # $ tainted
sorted(tainted_list),
reversed(tainted_list),
iter(tainted_list),
next(iter(tainted_list)),
sorted(tainted_list), # $ tainted
reversed(tainted_list), # $ tainted
iter(tainted_list), # $ tainted
next(iter(tainted_list)), # $ tainted
)
a, b, c = tainted_list[0:3]
ensure_tainted(a, b, c)
ensure_tainted(a, b, c) # $ tainted
for h in tainted_list:
ensure_tainted(h)
ensure_tainted(h) # $ tainted
for i in reversed(tainted_list):
ensure_tainted(i)
ensure_tainted(i) # $ tainted
def test_dict_access(x):
tainted_dict = TAINTED_DICT
ensure_tainted(
tainted_dict["name"],
tainted_dict.get("name"),
tainted_dict[x],
tainted_dict.copy(),
tainted_dict["name"], # $ tainted
tainted_dict.get("name"), # $ tainted
tainted_dict[x], # $ tainted
tainted_dict.copy(), # $ tainted
)
for v in tainted_dict.values():
ensure_tainted(v)
ensure_tainted(v) # $ tainted
for k, v in tainted_dict.items():
ensure_tainted(v)
ensure_tainted(v) # $ tainted
def test_named_tuple(): # TODO: namedtuple currently not handled
@@ -84,8 +84,8 @@ def test_named_tuple(): # TODO: namedtuple currently not handled
point = Point(TAINTED_STRING, 'safe')
ensure_tainted(
point[0],
point.x,
point[0], # $ MISSING: tainted
point.x, # $ MISSING: tainted
)
ensure_not_tainted(
@@ -94,7 +94,7 @@ def test_named_tuple(): # TODO: namedtuple currently not handled
)
a, b = point
ensure_tainted(a)
ensure_tainted(a) # $ MISSING: tainted
ensure_not_tainted(b)
@@ -103,23 +103,23 @@ def test_defaultdict(key, x): # TODO: defaultdict currently not handled
tainted_default_dict[key] += TAINTED_STRING
ensure_tainted(
tainted_default_dict["name"],
tainted_default_dict.get("name"),
tainted_default_dict[x],
tainted_default_dict.copy(),
tainted_default_dict["name"], # $ MISSING: tainted
tainted_default_dict.get("name"), # $ MISSING: tainted
tainted_default_dict[x], # $ MISSING: tainted
tainted_default_dict.copy(), # $ MISSING: tainted
)
for v in tainted_default_dict.values():
ensure_tainted(v)
ensure_tainted(v) # $ MISSING: tainted
for k, v in tainted_default_dict.items():
ensure_tainted(v)
ensure_tainted(v) # $ MISSING: tainted
def test_copy_1():
from copy import copy, deepcopy
ensure_tainted(
copy(TAINTED_LIST),
deepcopy(TAINTED_LIST),
copy(TAINTED_LIST), # $ tainted
deepcopy(TAINTED_LIST), # $ tainted
)
@@ -127,8 +127,8 @@ def test_copy_2():
import copy
ensure_tainted(
copy.copy(TAINTED_LIST),
copy.deepcopy(TAINTED_LIST),
copy.copy(TAINTED_LIST), # $ tainted
copy.deepcopy(TAINTED_LIST), # $ tainted
)
@@ -139,7 +139,7 @@ def list_index_assign():
ensure_not_tainted(my_list)
my_list[0] = tainted_string
ensure_tainted(my_list)
ensure_tainted(my_list) # $ MISSING: tainted
def list_index_aug_assign():
@@ -149,7 +149,7 @@ def list_index_aug_assign():
ensure_not_tainted(my_list)
my_list[0] += tainted_string
ensure_tainted(my_list)
ensure_tainted(my_list) # $ MISSING: tainted
def list_append():
@@ -159,7 +159,7 @@ def list_append():
ensure_not_tainted(my_list)
my_list.append(tainted_string)
ensure_tainted(my_list)
ensure_tainted(my_list) # $ tainted
def list_extend():
@@ -169,7 +169,7 @@ def list_extend():
ensure_not_tainted(my_list)
my_list.extend(tainted_list)
ensure_tainted(my_list)
ensure_tainted(my_list) # $ MISSING: tainted
def dict_update_dict():
@@ -179,7 +179,7 @@ def dict_update_dict():
ensure_not_tainted(my_dict)
my_dict.update(tainted_dict)
ensure_tainted(my_dict)
ensure_tainted(my_dict) # $ MISSING: tainted
def dict_update_kv_list():
@@ -189,7 +189,7 @@ def dict_update_kv_list():
ensure_not_tainted(my_dict)
my_dict.update(tainted_kv_list)
ensure_tainted(my_dict)
ensure_tainted(my_dict) # $ MISSING: tainted
def dict_update_kv_arg():
@@ -198,7 +198,7 @@ def dict_update_kv_arg():
ensure_not_tainted(my_dict)
my_dict.update(key2=TAINTED_STRING)
ensure_tainted(my_dict)
ensure_tainted(my_dict) # $ MISSING: tainted
def dict_manual_update():
@@ -209,7 +209,7 @@ def dict_manual_update():
for k in tainted_dict:
my_dict[k] = tainted_dict[k]
ensure_tainted(my_dict)
ensure_tainted(my_dict) # $ MISSING: tainted
def dict_merge():
@@ -217,7 +217,7 @@ def dict_merge():
tainted_dict = {"key2": TAINTED_STRING}
merged = {**my_dict, **tainted_dict}
ensure_tainted(merged)
ensure_tainted(merged) # $ MISSING: tainted
def set_add():
@@ -227,7 +227,7 @@ def set_add():
ensure_not_tainted(my_set)
my_set.add(tainted_string)
ensure_tainted(my_set)
ensure_tainted(my_set) # $ tainted
# Make tests runable

View File

@@ -23,16 +23,16 @@ def test():
import json
ensure_tainted(
json.dumps(ts),
json.loads(json.dumps(ts)),
json.dumps(ts), # $ tainted
json.loads(json.dumps(ts)), # $ tainted
)
# For Python2, need to convert to unicode for StringIO to work
tainted_filelike = StringIO(unicode(json.dumps(ts)))
ensure_tainted(
tainted_filelike,
json.load(tainted_filelike),
tainted_filelike, # $ MISSING: tainted
json.load(tainted_filelike), # $ MISSING: tainted
)
def non_syntacical():
@@ -45,17 +45,17 @@ def non_syntacical():
dumps_alias = dumps
ensure_tainted(
dumps(ts),
dumps_alias(ts),
loads(dumps(ts)),
dumps(ts), # $ tainted
dumps_alias(ts), # $ tainted
loads(dumps(ts)), # $ tainted
)
# For Python2, need to convert to unicode for StringIO to work
tainted_filelike = StringIO(unicode(dumps(ts)))
ensure_tainted(
tainted_filelike,
load(tainted_filelike),
tainted_filelike, # $ MISSING: tainted
load(tainted_filelike), # $ MISSING: tainted
)
# Make tests runable

View File

@@ -22,23 +22,23 @@ def str_operations():
tb = TAINTED_BYTES
ensure_tainted(
ts,
ts + "foo",
"foo" + ts,
ts * 5,
ts[0 : len(ts)],
ts[:],
ts[0:1000],
ts[0],
str(ts),
bytes(tb),
unicode(ts),
ts, # $ tainted
ts + "foo", # $ tainted
"foo" + ts, # $ tainted
ts * 5, # $ tainted
ts[0 : len(ts)], # $ tainted
ts[:], # $ tainted
ts[0:1000], # $ tainted
ts[0], # $ tainted
str(ts), # $ tainted
bytes(tb), # $ tainted
unicode(ts), # $ tainted
)
aug_assignment = "safe"
ensure_not_tainted(aug_assignment)
aug_assignment += TAINTED_STRING
ensure_tainted(aug_assignment)
ensure_tainted(aug_assignment) # $ tainted
def str_methods():
@@ -46,52 +46,54 @@ def str_methods():
ts = TAINTED_STRING
tb = TAINTED_BYTES
ensure_tainted(
ts.capitalize(),
ts.center(100),
ts.expandtabs(),
ts.capitalize(), # $ tainted
ts.center(100), # $ tainted
ts.expandtabs(), # $ tainted
ts.format(),
"{}".format(ts),
"{unsafe}".format(unsafe=ts),
ts.format(), # $ tainted
"{}".format(ts), # $ tainted
"{unsafe}".format(unsafe=ts), # $ tainted
ts.join(["", ""]),
"".join([ts]),
ts.join(["", ""]), # $ tainted
"".join([ts]), # $ tainted
ts.ljust(100),
ts.lstrip(),
ts.lower(),
ts.ljust(100), # $ tainted
ts.lstrip(), # $ tainted
ts.lower(), # $ tainted
ts.replace("old", "new"),
"safe".replace("safe", ts),
ts.replace("old", "new"), # $ tainted
"safe".replace("safe", ts), # $ tainted
ts.rjust(100),
ts.rstrip(),
ts.strip(),
ts.swapcase(),
ts.title(),
ts.upper(),
ts.zfill(100),
ts.rjust(100), # $ tainted
ts.rstrip(), # $ tainted
ts.strip(), # $ tainted
ts.swapcase(), # $ tainted
ts.title(), # $ tainted
ts.upper(), # $ tainted
ts.zfill(100), # $ tainted
ts.encode("utf-8"),
ts.encode("utf-8").decode("utf-8"),
ts.encode("utf-8"), # $ tainted
ts.encode("utf-8").decode("utf-8"), # $ tainted
tb.decode("utf-8"),
tb.decode("utf-8").encode("utf-8"),
tb.decode("utf-8"), # $ tainted
tb.decode("utf-8").encode("utf-8"), # $ tainted
# string methods that return a list
ts.partition("_"),
ts.rpartition("_"),
ts.rsplit("_"),
ts.split("_"),
ts.splitlines(),
ts.partition("_"), # $ tainted
ts.rpartition("_"), # $ tainted
ts.rsplit("_"), # $ tainted
ts.split("_"), # $ tainted
ts.splitlines(), # $ tainted
)
ensure_not_tainted(
# Intuitively I think this should be safe, but better discuss it
"safe".replace(ts, "also-safe"),
ts.join([]), # FP due to separator not being used with zero/one elements
ts.join(["safe"]), # FP due to separator not being used with zero/one elements
# FPs due to separator (`ts`) not ending up in result, when the list only has
# zero/one elements
ts.join([]), # $ SPURIOUS: tainted
ts.join(["safe"]), # $ SPURIOUS: tainted
)
@@ -101,8 +103,8 @@ def non_syntactic():
meth = ts.upper
_str = str
ensure_tainted(
meth(),
_str(ts),
meth(), # $ MISSING: tainted
_str(ts), # $ MISSING: tainted
)
@@ -111,9 +113,9 @@ def percent_fmt():
ts = TAINTED_STRING
tainted_fmt = ts + " %s %s"
ensure_tainted(
tainted_fmt % (1, 2),
"%s foo bar" % ts,
"%s %s %s" % (1, 2, ts),
tainted_fmt % (1, 2), # $ tainted
"%s foo bar" % ts, # $ tainted
"%s %s %s" % (1, 2, ts), # $ tainted
)
@@ -123,30 +125,30 @@ def binary_decode_encode():
import base64
ensure_tainted(
base64.b64encode(tb),
base64.b64decode(base64.b64encode(tb)),
base64.b64encode(tb), # $ tainted
base64.b64decode(base64.b64encode(tb)), # $ tainted
base64.standard_b64encode(tb),
base64.standard_b64decode(base64.standard_b64encode(tb)),
base64.standard_b64encode(tb), # $ tainted
base64.standard_b64decode(base64.standard_b64encode(tb)), # $ tainted
base64.urlsafe_b64encode(tb),
base64.urlsafe_b64decode(base64.urlsafe_b64encode(tb)),
base64.urlsafe_b64encode(tb), # $ tainted
base64.urlsafe_b64decode(base64.urlsafe_b64encode(tb)), # $ tainted
base64.b32encode(tb),
base64.b32decode(base64.b32encode(tb)),
base64.b32encode(tb), # $ tainted
base64.b32decode(base64.b32encode(tb)), # $ tainted
base64.b16encode(tb),
base64.b16decode(base64.b16encode(tb)),
base64.b16encode(tb), # $ tainted
base64.b16decode(base64.b16encode(tb)), # $ tainted
# deprecated since Python 3.1, but still works
base64.encodestring(tb),
base64.decodestring(base64.encodestring(tb)),
base64.encodestring(tb), # $ tainted
base64.decodestring(base64.encodestring(tb)), # $ tainted
)
import quopri
ensure_tainted(
quopri.encodestring(tb),
quopri.decodestring(quopri.encodestring(tb)),
quopri.encodestring(tb), # $ MISSING: tainted
quopri.decodestring(quopri.encodestring(tb)), # $ MISSING: tainted
)
@@ -156,10 +158,10 @@ def test_os_path_join():
print("\n# test_os_path_join")
ts = TAINTED_STRING
ensure_tainted(
os.path.join(ts, "foo", "bar"),
os.path.join(ts),
os.path.join("foo", "bar", ts),
ospath_alias.join("foo", "bar", ts),
os.path.join(ts, "foo", "bar"), # $ tainted
os.path.join(ts), # $ tainted
os.path.join("foo", "bar", ts), # $ tainted
ospath_alias.join("foo", "bar", ts), # $ tainted
)

View File

@@ -13,13 +13,13 @@ if TYPE_CHECKING:
def unpacking():
l = TAINTED_LIST[0:3]
a, b, c = l
ensure_tainted(a, b, c)
ensure_tainted(a, b, c) # $ tainted
def unpacking_to_list():
l = TAINTED_LIST[0:3]
[a, b, c] = l
ensure_tainted(a, b, c)
ensure_tainted(a, b, c) # $ tainted
def nested():
@@ -28,22 +28,22 @@ def nested():
# list
[[a1, a2, a3], b, c] = ll
ensure_tainted(a1, a2, a3, b, c)
ensure_tainted(a1, a2, a3, b, c) # $ tainted
# tuple
((a1, a2, a3), b, c) = ll
ensure_tainted(a1, a2, a3, b, c)
ensure_tainted(a1, a2, a3, b, c) # $ tainted
# mixed
[(a1, a2, a3), b, c] = ll
ensure_tainted(a1, a2, a3, b, c)
ensure_tainted(a1, a2, a3, b, c) # $ tainted
def unpack_from_set():
# no guarantee on ordering ... don't know why you would ever do this
a, b, c = {"foo", "bar", TAINTED_STRING}
# either all should be tainted, or none of them
ensure_tainted(a, b, c)
ensure_tainted(a, b, c) # $ tainted
def contrived_1():
@@ -52,8 +52,8 @@ def contrived_1():
no_taint_list = [1,2,3]
(a, b, c), (d, e, f) = tainted_list, no_taint_list
ensure_tainted(a, b, c)
ensure_not_tainted(d, e, f) # FP: we mark `d`, `e` and `f` as tainted.
ensure_tainted(a, b, c) # $ tainted
ensure_not_tainted(d, e, f) # $ SPURIOUS: tainted
def contrived_2():
@@ -62,7 +62,7 @@ def contrived_2():
# Old taint tracking was only able to handle taint nested 2 levels in sequences,
# so would not mark a, b, c as tainted
[[[ (a, b, c) ]]] = [[[ TAINTED_LIST[0:3] ]]]
ensure_tainted(a, b, c)
ensure_tainted(a, b, c) # $ tainted
# Make tests runable

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -25,14 +25,14 @@ initially_tainted = NOT_TAINTED
ensure_not_tainted(initially_tainted)
def use_of_initially_tainted():
ensure_not_tainted(initially_tainted) # FP
ensure_not_tainted(initially_tainted) # $ SPURIOUS: tainted
# A very similar case to the above, but here we _do_ want taint flow, because the initially tainted
# value is actually used before it gets reassigned to an untainted value.
# value is actually used before it gets reassigned to an untainted value.
def use_of_initially_tainted2():
ensure_tainted(initially_tainted2)
ensure_tainted(initially_tainted2) # $ tainted
initially_tainted2 = TAINTED_STRING
use_of_initially_tainted2()
@@ -47,7 +47,7 @@ def write_tainted():
g = TAINTED_STRING
def sink_global():
ensure_tainted(g)
ensure_tainted(g) # $ tainted
write_global()
write_tainted()

View File

@@ -1,6 +0,0 @@
| test.py:12 | ok | test | tainted_later |
| test.py:25 | ok | test | initially_tainted |
| test.py:28 | fail | use_of_initially_tainted | initially_tainted |
| test.py:35 | ok | use_of_initially_tainted2 | initially_tainted2 |
| test.py:40 | ok | test | initially_tainted2 |
| test.py:50 | ok | sink_global | g |

View File

@@ -1 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -1,6 +1,7 @@
module_tracker
| import_as_attr.py:1:6:1:11 | ControlFlowNode for ImportExpr |
module_attr_tracker
| import_as_attr.py:0:0:0:0 | ModuleVariableNode for Global Variable attr_ref in Module import_as_attr |
| import_as_attr.py:1:20:1:35 | ControlFlowNode for ImportMember |
| import_as_attr.py:1:28:1:35 | GSSA Variable attr_ref |
| import_as_attr.py:3:1:3:1 | GSSA Variable x |

View File

@@ -0,0 +1,94 @@
/**
* Defines a InlineExpectationsTest for checking whether any arguments in
* `ensure_tainted` and `ensure_not_tainted` calls are tainted.
*
* Also defines query predicates to ensure that:
* - if any arguments to `ensure_not_tainted` are tainted, their annotation is marked with `SPURIOUS`.
* - if any arguments to `ensure_tainted` are not tainted, their annotation is marked with `MISSING`.
*
* The functionality of this module is tested in `ql/test/experimental/meta/inline-taint-test-demo`.
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
import TestUtilities.InlineExpectationsTest
import experimental.dataflow.TestUtil.PrintNode
DataFlow::Node shouldBeTainted() {
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() = "ensure_tainted" and
result in [call.getArg(_), call.getArgByName(_)]
)
}
DataFlow::Node shouldNotBeTainted() {
exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(NameNode).getId() = "ensure_not_tainted" and
result in [call.getArg(_), call.getArgByName(_)]
)
}
class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asCfgNode().(NameNode).getId() in [
"TAINTED_STRING", "TAINTED_BYTES", "TAINTED_LIST", "TAINTED_DICT"
]
or
source instanceof RemoteFlowSource
}
override predicate isSink(DataFlow::Node sink) {
sink = shouldBeTainted()
or
sink = shouldNotBeTainted()
}
}
class InlineTaintTest extends InlineExpectationsTest {
InlineTaintTest() { this = "InlineTaintTest" }
override string getARelevantTag() { result = "tainted" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(DataFlow::Node sink |
any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
location = sink.getLocation() and
element = prettyExp(sink.asExpr()) and
value = "" and
tag = "tainted"
)
}
}
query predicate argumentToEnsureNotTaintedNotMarkedAsSpurious(
Location location, string error, string element
) {
error = "ERROR, you should add `SPURIOUS:` to this annotation" and
location = shouldNotBeTainted().getLocation() and
any(InlineTaintTest test).hasActualResult(location, element, "tainted", _) and
exists(GoodExpectation good, ActualResult actualResult |
good.matchesActualResult(actualResult) and
actualResult.getLocation() = location and
actualResult.toString() = element
)
}
query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing(
Location location, string error, string element
) {
error = "ERROR, you should add `# $ MISSING: tainted` annotation" and
exists(DataFlow::Node sink |
sink = shouldBeTainted() and
element = prettyExp(sink.asExpr()) and
not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
location = sink.getLocation() and
not exists(FalseNegativeExpectation missingResult |
missingResult.getLocation().getStartLine() = location.getStartLine()
)
)
}

View File

@@ -0,0 +1,7 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
| taint_test.py:48:9:48:29 | taint_test.py:48 | ERROR, you should add `SPURIOUS:` to this annotation | should_not_be_tainted |
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
| taint_test.py:32:9:32:25 | taint_test.py:32 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted |
| taint_test.py:37:24:37:40 | taint_test.py:37 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted |
failures
| taint_test.py:41:20:41:21 | ts | Fixed missing result:tainted= |

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -0,0 +1,49 @@
def expected_usage():
ts = TAINTED_STRING
# simulating handling something we _want_ to treat at tainted, but we currently treat as untainted
should_be_tainted = "pretend this is unsafe"
ensure_tainted(
ts, # $ tainted
should_be_tainted, # $ MISSING: tainted
)
# having one annotation for multiple arguments is OK, as long as all arguments
# fulfil the same annotation
ensure_tainted(ts, ts) # $ tainted
# simulating handling something we _want_ to treat at untainted, but we currently treat as tainted
should_not_be_tainted = "pretend this is now safe" + ts
ensure_not_tainted(
should_not_be_tainted, # $ SPURIOUS: tainted
"FOO"
)
def bad_usage():
ts = TAINTED_STRING
# simulating handling something we _want_ to treat at tainted, but we currently treat as untainted
should_be_tainted = "pretend this is unsafe"
# This element _should_ have a `$ MISSING: tainted` annotation, which will be alerted in the .expected output
ensure_tainted(
should_be_tainted,
)
# using one annotation for multiple arguments i not OK when it's mixed whether our
# taint-tracking works as expected
ensure_tainted(ts, should_be_tainted) # $ tainted
# if you try to get around it by adding BOTH annotations, that results in a problem
# from the default set of inline-test-expectation rules
ensure_tainted(ts, should_be_tainted) # $ tainted MISSING: tainted
# simulating handling something we _want_ to treat at untainted, but we currently treat as tainted
should_not_be_tainted = "pretend this is now safe" + ts
# This annotation _should_ have used `SPURIOUS`, which will be alerted on in the .expected output
ensure_not_tainted(
should_not_be_tainted # $ tainted
)

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,100 +0,0 @@
| response_test.py:61 | ok | get_redirect_url | foo |
| taint_forms.py:6 | ok | to_python | value |
| taint_forms.py:9 | ok | validate | value |
| taint_forms.py:12 | ok | run_validators | value |
| taint_forms.py:15 | ok | clean | value |
| taint_forms.py:33 | ok | clean | cleaned_data |
| taint_forms.py:34 | ok | clean | cleaned_data["key"] |
| taint_forms.py:35 | ok | clean | cleaned_data.get(..) |
| taint_forms.py:39 | ok | clean | self.cleaned_data |
| taint_forms.py:40 | ok | clean | self.cleaned_data["key"] |
| taint_forms.py:41 | ok | clean | self.cleaned_data.get(..) |
| taint_forms.py:46 | ok | clean_foo | self.cleaned_data |
| taint_test.py:8 | ok | test_taint | bar |
| taint_test.py:8 | ok | test_taint | foo |
| taint_test.py:9 | ok | test_taint | baz |
| taint_test.py:15 | ok | test_taint | request |
| taint_test.py:17 | ok | test_taint | request.body |
| taint_test.py:18 | ok | test_taint | request.path |
| taint_test.py:19 | ok | test_taint | request.path_info |
| taint_test.py:23 | ok | test_taint | request.method |
| taint_test.py:25 | ok | test_taint | request.encoding |
| taint_test.py:26 | ok | test_taint | request.content_type |
| taint_test.py:29 | ok | test_taint | request.content_params |
| taint_test.py:30 | ok | test_taint | request.content_params["key"] |
| taint_test.py:31 | ok | test_taint | request.content_params.get(..) |
| taint_test.py:35 | ok | test_taint | request.GET |
| taint_test.py:36 | ok | test_taint | request.GET["key"] |
| taint_test.py:37 | ok | test_taint | request.GET.get(..) |
| taint_test.py:38 | fail | test_taint | request.GET.getlist(..) |
| taint_test.py:39 | fail | test_taint | request.GET.getlist(..)[0] |
| taint_test.py:40 | ok | test_taint | request.GET.pop(..) |
| taint_test.py:41 | ok | test_taint | request.GET.pop(..)[0] |
| taint_test.py:42 | ok | test_taint | request.GET.popitem()[0] |
| taint_test.py:43 | ok | test_taint | request.GET.popitem()[1] |
| taint_test.py:44 | ok | test_taint | request.GET.popitem()[1][0] |
| taint_test.py:45 | fail | test_taint | request.GET.dict() |
| taint_test.py:46 | fail | test_taint | request.GET.dict()["key"] |
| taint_test.py:47 | fail | test_taint | request.GET.urlencode() |
| taint_test.py:50 | ok | test_taint | request.POST |
| taint_test.py:53 | ok | test_taint | request.COOKIES |
| taint_test.py:54 | ok | test_taint | request.COOKIES["key"] |
| taint_test.py:55 | ok | test_taint | request.COOKIES.get(..) |
| taint_test.py:58 | ok | test_taint | request.FILES |
| taint_test.py:59 | ok | test_taint | request.FILES["key"] |
| taint_test.py:60 | fail | test_taint | request.FILES["key"].content_type |
| taint_test.py:61 | fail | test_taint | request.FILES["key"].content_type_extra |
| taint_test.py:62 | fail | test_taint | request.FILES["key"].content_type_extra["key"] |
| taint_test.py:63 | fail | test_taint | request.FILES["key"].charset |
| taint_test.py:64 | fail | test_taint | request.FILES["key"].name |
| taint_test.py:65 | fail | test_taint | request.FILES["key"].file |
| taint_test.py:66 | fail | test_taint | request.FILES["key"].file.read() |
| taint_test.py:68 | ok | test_taint | request.FILES.get(..) |
| taint_test.py:69 | fail | test_taint | request.FILES.get(..).name |
| taint_test.py:70 | fail | test_taint | request.FILES.getlist(..) |
| taint_test.py:71 | fail | test_taint | request.FILES.getlist(..)[0] |
| taint_test.py:72 | fail | test_taint | request.FILES.getlist(..)[0].name |
| taint_test.py:73 | fail | test_taint | request.FILES.dict() |
| taint_test.py:74 | fail | test_taint | request.FILES.dict()["key"] |
| taint_test.py:75 | fail | test_taint | request.FILES.dict()["key"].name |
| taint_test.py:78 | ok | test_taint | request.META |
| taint_test.py:79 | ok | test_taint | request.META["HTTP_USER_AGENT"] |
| taint_test.py:80 | ok | test_taint | request.META.get(..) |
| taint_test.py:83 | ok | test_taint | request.headers |
| taint_test.py:84 | ok | test_taint | request.headers["user-agent"] |
| taint_test.py:85 | ok | test_taint | request.headers["USER_AGENT"] |
| taint_test.py:88 | ok | test_taint | request.resolver_match |
| taint_test.py:89 | fail | test_taint | request.resolver_match.args |
| taint_test.py:90 | fail | test_taint | request.resolver_match.args[0] |
| taint_test.py:91 | fail | test_taint | request.resolver_match.kwargs |
| taint_test.py:92 | fail | test_taint | request.resolver_match.kwargs["key"] |
| taint_test.py:94 | fail | test_taint | request.get_full_path() |
| taint_test.py:95 | fail | test_taint | request.get_full_path_info() |
| taint_test.py:99 | fail | test_taint | request.read() |
| taint_test.py:100 | fail | test_taint | request.readline() |
| taint_test.py:101 | fail | test_taint | request.readlines() |
| taint_test.py:102 | fail | test_taint | request.readlines()[0] |
| taint_test.py:103 | fail | test_taint | ListComp |
| taint_test.py:109 | ok | test_taint | args |
| taint_test.py:110 | ok | test_taint | args[0] |
| taint_test.py:111 | ok | test_taint | kwargs |
| taint_test.py:112 | ok | test_taint | kwargs["key"] |
| taint_test.py:116 | ok | test_taint | request.current_app |
| taint_test.py:121 | ok | test_taint | request.get_host() |
| taint_test.py:122 | ok | test_taint | request.get_port() |
| taint_test.py:129 | fail | test_taint | request.build_absolute_uri() |
| taint_test.py:130 | fail | test_taint | request.build_absolute_uri(..) |
| taint_test.py:131 | fail | test_taint | request.build_absolute_uri(..) |
| taint_test.py:134 | ok | test_taint | request.build_absolute_uri(..) |
| taint_test.py:135 | ok | test_taint | request.build_absolute_uri(..) |
| taint_test.py:143 | ok | test_taint | request.get_signed_cookie(..) |
| taint_test.py:144 | ok | test_taint | request.get_signed_cookie(..) |
| taint_test.py:145 | ok | test_taint | request.get_signed_cookie(..) |
| taint_test.py:149 | fail | test_taint | request.get_signed_cookie(..) |
| taint_test.py:150 | fail | test_taint | request.get_signed_cookie(..) |
| taint_test.py:157 | ok | some_method | self.request |
| taint_test.py:158 | ok | some_method | self.request.GET["key"] |
| taint_test.py:160 | ok | some_method | self.args |
| taint_test.py:161 | ok | some_method | self.args[0] |
| taint_test.py:163 | ok | some_method | self.kwargs |
| taint_test.py:164 | ok | some_method | self.kwargs["key"] |

View File

@@ -1,6 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib
import semmle.python.dataflow.new.RemoteFlowSources
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
}

View File

@@ -58,7 +58,7 @@ def redirect_shortcut(request):
class CustomRedirectView(RedirectView):
def get_redirect_url(self, foo): # $ requestHandler routedParameter=foo
ensure_tainted(foo)
ensure_tainted(foo) # $ tainted
next = "https://example.com/{}".format(foo)
return next # $ HttpResponse HttpRedirectResponse redirectLocation=next

View File

@@ -3,16 +3,16 @@ import django.forms
class MyField(django.forms.Field):
def to_python(self, value):
ensure_tainted(value)
ensure_tainted(value) # $ tainted
def validate(self, value):
ensure_tainted(value)
ensure_tainted(value) # $ tainted
def run_validators(self, value):
ensure_tainted(value)
ensure_tainted(value) # $ tainted
def clean(self, value):
ensure_tainted(value)
ensure_tainted(value) # $ tainted
# # Base definition of `clean` looks like the following, so there is actually
# # _data flow_ from the methods, but we will ignore for simplicity.
@@ -30,17 +30,17 @@ class MyForm(django.forms.Form):
cleaned_data = super().clean()
ensure_tainted(
cleaned_data,
cleaned_data["key"],
cleaned_data.get("key"),
cleaned_data, # $ tainted
cleaned_data["key"], # $ tainted
cleaned_data.get("key"), # $ tainted
)
ensure_tainted(
self.cleaned_data,
self.cleaned_data["key"],
self.cleaned_data.get("key"),
self.cleaned_data, # $ tainted
self.cleaned_data["key"], # $ tainted
self.cleaned_data.get("key"), # $ tainted
)
def clean_foo(self):
# This method is supposed to clean the `foo` field in context of this form.
ensure_tainted(self.cleaned_data)
ensure_tainted(self.cleaned_data) # $ tainted

View File

@@ -5,111 +5,114 @@ from django.views import View
def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler routedParameter=foo routedParameter=bar
ensure_tainted(foo, bar)
ensure_tainted(foo, bar) # $ tainted
ensure_not_tainted(baz)
# Manually inspected all fields of the HttpRequest object
# https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects
ensure_tainted(
request,
request, # $ tainted
request.body,
request.path,
request.path_info,
request.body, # $ tainted
request.path, # $ tainted
request.path_info, # $ tainted
# With CSRF middleware disabled, it's possible to use custom methods,
# for example by `curl -X FOO <url>`
request.method,
request.method, # $ tainted
request.encoding,
request.content_type,
request.encoding, # $ tainted
request.content_type, # $ tainted
# Dict[str, str]
request.content_params,
request.content_params["key"],
request.content_params.get("key"),
request.content_params, # $ tainted
request.content_params["key"], # $ tainted
request.content_params.get("key"), # $ tainted
# django.http.QueryDict
# see https://docs.djangoproject.com/en/3.0/ref/request-response/#querydict-objects
request.GET,
request.GET["key"],
request.GET.get("key"),
request.GET.getlist("key"),
request.GET.getlist("key")[0],
request.GET.pop("key"),
request.GET.pop("key")[0],
request.GET.popitem()[0], # key
request.GET.popitem()[1], # values
request.GET.popitem()[1][0], # values[0]
request.GET.dict(),
request.GET.dict()["key"],
request.GET.urlencode(),
request.GET, # $ tainted
request.GET["key"], # $ tainted
request.GET.get("key"), # $ tainted
request.GET.getlist("key"), # $ MISSING: tainted
request.GET.getlist("key")[0], # $ MISSING: tainted
request.GET.pop("key"), # $ tainted
request.GET.pop("key")[0], # $ tainted
# key
request.GET.popitem()[0], # $ tainted
# values
request.GET.popitem()[1], # $ tainted
# values[0]
request.GET.popitem()[1][0], # $ tainted
request.GET.dict(), # $ MISSING: tainted
request.GET.dict()["key"], # $ MISSING: tainted
request.GET.urlencode(), # $ MISSING: tainted
# django.http.QueryDict (same as above, did not duplicate tests)
request.POST,
request.POST, # $ tainted
# Dict[str, str]
request.COOKIES,
request.COOKIES["key"],
request.COOKIES.get("key"),
request.COOKIES, # $ tainted
request.COOKIES["key"], # $ tainted
request.COOKIES.get("key"), # $ tainted
# MultiValueDict[str, UploadedFile]
request.FILES,
request.FILES["key"],
request.FILES["key"].content_type,
request.FILES["key"].content_type_extra,
request.FILES["key"].content_type_extra["key"],
request.FILES["key"].charset,
request.FILES["key"].name,
request.FILES["key"].file,
request.FILES["key"].file.read(),
request.FILES, # $ tainted
request.FILES["key"], # $ tainted
request.FILES["key"].content_type, # $ MISSING: tainted
request.FILES["key"].content_type_extra, # $ MISSING: tainted
request.FILES["key"].content_type_extra["key"], # $ MISSING: tainted
request.FILES["key"].charset, # $ MISSING: tainted
request.FILES["key"].name, # $ MISSING: tainted
request.FILES["key"].file, # $ MISSING: tainted
request.FILES["key"].file.read(), # $ MISSING: tainted
request.FILES.get("key"),
request.FILES.get("key").name,
request.FILES.getlist("key"),
request.FILES.getlist("key")[0],
request.FILES.getlist("key")[0].name,
request.FILES.dict(),
request.FILES.dict()["key"],
request.FILES.dict()["key"].name,
request.FILES.get("key"), # $ tainted
request.FILES.get("key").name, # $ MISSING: tainted
request.FILES.getlist("key"), # $ MISSING: tainted
request.FILES.getlist("key")[0], # $ MISSING: tainted
request.FILES.getlist("key")[0].name, # $ MISSING: tainted
request.FILES.dict(), # $ MISSING: tainted
request.FILES.dict()["key"], # $ MISSING: tainted
request.FILES.dict()["key"].name, # $ MISSING: tainted
# Dict[str, Any]
request.META,
request.META["HTTP_USER_AGENT"],
request.META.get("HTTP_USER_AGENT"),
request.META, # $ tainted
request.META["HTTP_USER_AGENT"], # $ tainted
request.META.get("HTTP_USER_AGENT"), # $ tainted
# HttpHeaders (case insensitive dict-like)
request.headers,
request.headers["user-agent"],
request.headers["USER_AGENT"],
request.headers, # $ tainted
request.headers["user-agent"], # $ tainted
request.headers["USER_AGENT"], # $ tainted
# django.urls.ResolverMatch
request.resolver_match,
request.resolver_match.args,
request.resolver_match.args[0],
request.resolver_match.kwargs,
request.resolver_match.kwargs["key"],
request.resolver_match, # $ tainted
request.resolver_match.args, # $ MISSING: tainted
request.resolver_match.args[0], # $ MISSING: tainted
request.resolver_match.kwargs, # $ MISSING: tainted
request.resolver_match.kwargs["key"], # $ MISSING: tainted
request.get_full_path(),
request.get_full_path_info(),
request.get_full_path(), # $ MISSING: tainted
request.get_full_path_info(), # $ MISSING: tainted
# build_absolute_uri handled below
# get_signed_cookie handled below
request.read(),
request.readline(),
request.readlines(),
request.readlines()[0],
[line for line in request],
request.read(), # $ MISSING: tainted
request.readline(), # $ MISSING: tainted
request.readlines(), # $ MISSING: tainted
request.readlines()[0], # $ MISSING: tainted
[line for line in request], # $ MISSING: tainted
)
# django.urls.ResolverMatch also supports iterable unpacking
_view, args, kwargs = request.resolver_match
ensure_tainted(
args,
args[0],
kwargs,
kwargs["key"],
args, # $ tainted
args[0], # $ tainted
kwargs, # $ tainted
kwargs["key"], # $ tainted
)
ensure_not_tainted(
@@ -126,9 +129,9 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
# build_absolute_uri
####################################
ensure_tainted(
request.build_absolute_uri(),
request.build_absolute_uri(request.GET["key"]),
request.build_absolute_uri(location=request.GET["key"]),
request.build_absolute_uri(), # $ MISSING: tainted
request.build_absolute_uri(request.GET["key"]), # $ MISSING: tainted
request.build_absolute_uri(location=request.GET["key"]), # $ MISSING: tainted
)
ensure_not_tainted(
request.build_absolute_uri("/hardcoded/"),
@@ -146,22 +149,22 @@ def test_taint(request: HttpRequest, foo, bar, baz=None): # $requestHandler rou
)
# However, providing tainted default value might result in taint
ensure_tainted(
request.get_signed_cookie("key", request.COOKIES["key"]),
request.get_signed_cookie("key", default=request.COOKIES["key"]),
request.get_signed_cookie("key", request.COOKIES["key"]), # $ MISSING: tainted
request.get_signed_cookie("key", default=request.COOKIES["key"]), # $ MISSING: tainted
)
class ClassView(View):
def some_method(self):
ensure_tainted(
self.request,
self.request.GET["key"],
self.request, # $ tainted
self.request.GET["key"], # $ tainted
self.args,
self.args[0],
self.args, # $ tainted
self.args[0], # $ tainted
self.kwargs,
self.kwargs["key"],
self.kwargs, # $ tainted
self.kwargs["key"], # $ tainted
)

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,10 +0,0 @@
| fabric_v1_execute.py:7 | fail | unsafe | cmd |
| fabric_v1_execute.py:7 | fail | unsafe | cmd2 |
| fabric_v1_execute.py:8 | ok | unsafe | safe_arg |
| fabric_v1_execute.py:8 | ok | unsafe | safe_optional |
| fabric_v1_execute.py:14 | fail | unsafe | cmd |
| fabric_v1_execute.py:14 | fail | unsafe | cmd2 |
| fabric_v1_execute.py:15 | ok | unsafe | safe_arg |
| fabric_v1_execute.py:15 | ok | unsafe | safe_optional |
| fabric_v1_execute.py:21 | ok | some_http_handler | cmd |
| fabric_v1_execute.py:21 | ok | some_http_handler | cmd2 |

View File

@@ -1 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib

View File

@@ -4,21 +4,21 @@ from fabric.api import run, execute
def unsafe(cmd, safe_arg, cmd2=None, safe_optional=5):
ensure_tainted(cmd, cmd2)
ensure_tainted(cmd, cmd2) # $ MISSING: tainted
ensure_not_tainted(safe_arg, safe_optional)
class Foo(object):
def unsafe(self, cmd, safe_arg, cmd2=None, safe_optional=5):
ensure_tainted(cmd, cmd2)
ensure_tainted(cmd, cmd2) # $ MISSING: tainted
ensure_not_tainted(safe_arg, safe_optional)
def some_http_handler():
cmd = TAINTED_STRING
cmd2 = TAINTED_STRING
ensure_tainted(cmd, cmd2)
ensure_tainted(cmd, cmd2) # $ tainted
execute(unsafe, cmd=cmd, safe_arg='safe_arg', cmd2=cmd2)

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,98 +0,0 @@
| taint_test.py:6 | ok | test_taint | name |
| taint_test.py:6 | ok | test_taint | number |
| taint_test.py:7 | ok | test_taint | foo |
| taint_test.py:14 | ok | test_taint | request.environ |
| taint_test.py:15 | ok | test_taint | request.environ.get(..) |
| taint_test.py:17 | ok | test_taint | request.path |
| taint_test.py:18 | ok | test_taint | request.full_path |
| taint_test.py:19 | ok | test_taint | request.base_url |
| taint_test.py:20 | ok | test_taint | request.url |
| taint_test.py:23 | fail | test_taint | request.accept_charsets.best |
| taint_test.py:24 | fail | test_taint | request.accept_charsets.best_match(..) |
| taint_test.py:25 | ok | test_taint | request.accept_charsets[0] |
| taint_test.py:26 | ok | test_taint | request.accept_encodings |
| taint_test.py:27 | ok | test_taint | request.accept_languages |
| taint_test.py:28 | ok | test_taint | request.accept_mimetypes |
| taint_test.py:31 | ok | test_taint | request.access_control_request_headers |
| taint_test.py:33 | ok | test_taint | request.access_control_request_method |
| taint_test.py:35 | ok | test_taint | request.access_route |
| taint_test.py:36 | ok | test_taint | request.access_route[0] |
| taint_test.py:39 | ok | test_taint | request.args |
| taint_test.py:40 | ok | test_taint | request.args['key'] |
| taint_test.py:41 | ok | test_taint | request.args.getlist(..) |
| taint_test.py:44 | ok | test_taint | request.authorization |
| taint_test.py:45 | ok | test_taint | request.authorization['username'] |
| taint_test.py:46 | fail | test_taint | request.authorization.username |
| taint_test.py:49 | ok | test_taint | request.cache_control |
| taint_test.py:51 | fail | test_taint | request.cache_control.max_age |
| taint_test.py:52 | fail | test_taint | request.cache_control.max_stale |
| taint_test.py:53 | fail | test_taint | request.cache_control.min_fresh |
| taint_test.py:55 | ok | test_taint | request.content_encoding |
| taint_test.py:57 | ok | test_taint | request.content_md5 |
| taint_test.py:59 | ok | test_taint | request.content_type |
| taint_test.py:62 | ok | test_taint | request.cookies |
| taint_test.py:63 | ok | test_taint | request.cookies['key'] |
| taint_test.py:65 | ok | test_taint | request.data |
| taint_test.py:68 | ok | test_taint | request.files |
| taint_test.py:69 | ok | test_taint | request.files['key'] |
| taint_test.py:70 | fail | test_taint | request.files['key'].filename |
| taint_test.py:71 | fail | test_taint | request.files['key'].stream |
| taint_test.py:72 | ok | test_taint | request.files.getlist(..) |
| taint_test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename |
| taint_test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream |
| taint_test.py:77 | ok | test_taint | request.form |
| taint_test.py:78 | ok | test_taint | request.form['key'] |
| taint_test.py:79 | ok | test_taint | request.form.getlist(..) |
| taint_test.py:81 | ok | test_taint | request.get_data() |
| taint_test.py:83 | ok | test_taint | request.get_json() |
| taint_test.py:84 | ok | test_taint | request.get_json()['foo'] |
| taint_test.py:85 | ok | test_taint | request.get_json()['foo']['bar'] |
| taint_test.py:89 | ok | test_taint | request.headers |
| taint_test.py:90 | ok | test_taint | request.headers['key'] |
| taint_test.py:91 | fail | test_taint | request.headers.get_all(..) |
| taint_test.py:92 | fail | test_taint | request.headers.getlist(..) |
| taint_test.py:93 | ok | test_taint | list(..) |
| taint_test.py:94 | fail | test_taint | request.headers.to_wsgi_list() |
| taint_test.py:96 | ok | test_taint | request.json |
| taint_test.py:97 | ok | test_taint | request.json['foo'] |
| taint_test.py:98 | ok | test_taint | request.json['foo']['bar'] |
| taint_test.py:100 | ok | test_taint | request.method |
| taint_test.py:102 | ok | test_taint | request.mimetype |
| taint_test.py:104 | ok | test_taint | request.mimetype_params |
| taint_test.py:106 | ok | test_taint | request.origin |
| taint_test.py:109 | ok | test_taint | request.pragma |
| taint_test.py:111 | ok | test_taint | request.query_string |
| taint_test.py:113 | ok | test_taint | request.referrer |
| taint_test.py:115 | ok | test_taint | request.remote_addr |
| taint_test.py:117 | ok | test_taint | request.remote_user |
| taint_test.py:120 | ok | test_taint | request.stream |
| taint_test.py:121 | ok | test_taint | request.input_stream |
| taint_test.py:123 | ok | test_taint | request.url |
| taint_test.py:125 | ok | test_taint | request.user_agent |
| taint_test.py:128 | ok | test_taint | request.values |
| taint_test.py:129 | ok | test_taint | request.values['key'] |
| taint_test.py:130 | ok | test_taint | request.values.getlist(..) |
| taint_test.py:133 | ok | test_taint | request.view_args |
| taint_test.py:134 | ok | test_taint | request.view_args['key'] |
| taint_test.py:138 | ok | test_taint | request.script_root |
| taint_test.py:139 | ok | test_taint | request.url_root |
| taint_test.py:143 | ok | test_taint | request.charset |
| taint_test.py:144 | ok | test_taint | request.url_charset |
| taint_test.py:148 | ok | test_taint | request.date |
| taint_test.py:151 | ok | test_taint | request.endpoint |
| taint_test.py:156 | ok | test_taint | request.host |
| taint_test.py:157 | ok | test_taint | request.host_url |
| taint_test.py:159 | ok | test_taint | request.scheme |
| taint_test.py:161 | ok | test_taint | request.script_root |
| taint_test.py:169 | ok | test_taint | request.args |
| taint_test.py:170 | ok | test_taint | a |
| taint_test.py:171 | ok | test_taint | b |
| taint_test.py:173 | ok | test_taint | request.args['key'] |
| taint_test.py:174 | ok | test_taint | a['key'] |
| taint_test.py:175 | ok | test_taint | b['key'] |
| taint_test.py:177 | ok | test_taint | request.args.getlist(..) |
| taint_test.py:178 | ok | test_taint | a.getlist(..) |
| taint_test.py:179 | ok | test_taint | b.getlist(..) |
| taint_test.py:180 | ok | test_taint | gl(..) |
| taint_test.py:187 | ok | test_taint | req.path |
| taint_test.py:188 | ok | test_taint | gd() |

View File

@@ -1,6 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib
import semmle.python.dataflow.new.RemoteFlowSources
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
}

View File

@@ -3,7 +3,7 @@ app = Flask(__name__)
@app.route("/test_taint/<name>/<int:number>") # $routeSetup="/test_taint/<name>/<int:number>"
def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler routedParameter=name routedParameter=number
ensure_tainted(name, number)
ensure_tainted(name, number) # $ tainted
ensure_not_tainted(foo)
# Manually inspected all fields of the Request object
@@ -11,127 +11,136 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
ensure_tainted(
request.environ,
request.environ.get('HTTP_AUTHORIZATION'),
request.environ, # $ tainted
request.environ.get('HTTP_AUTHORIZATION'), # $ tainted
request.path,
request.full_path,
request.base_url,
request.url,
request.path, # $ tainted
request.full_path, # $ tainted
request.base_url, # $ tainted
request.url, # $ tainted
# These request.accept_* properties are instances of subclasses of werkzeug.datastructures.Accept
request.accept_charsets.best,
request.accept_charsets.best_match(["utf-8", "utf-16"]),
request.accept_charsets[0],
request.accept_encodings,
request.accept_languages,
request.accept_mimetypes,
request.accept_charsets.best, # $ MISSING: tainted
request.accept_charsets.best_match(["utf-8", "utf-16"]), # $ MISSING: tainted
request.accept_charsets[0], # $ tainted
request.accept_encodings, # $ tainted
request.accept_languages, # $ tainted
request.accept_mimetypes, # $ tainted
# werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet)
request.access_control_request_headers,
request.access_control_request_headers, # $ tainted
request.access_control_request_method,
request.access_control_request_method, # $ tainted
request.access_route,
request.access_route[0],
request.access_route, # $ tainted
request.access_route[0], # $ tainted
# By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\
request.args,
request.args['key'],
request.args.getlist('key'),
request.args, # $ tainted
request.args['key'], # $ tainted
request.args.get('key'), # $ tainted
request.args.getlist('key'), # $ tainted
# werkzeug.datastructures.Authorization (a dict, with some properties)
request.authorization,
request.authorization['username'],
request.authorization.username,
request.authorization, # $ tainted
request.authorization['username'], # $ tainted
request.authorization.username, # $ MISSING: tainted
# werkzeug.datastructures.RequestCacheControl
request.cache_control,
request.cache_control, # $ tainted
# These should be `int`s, but can be strings... see debug method below
request.cache_control.max_age,
request.cache_control.max_stale,
request.cache_control.min_fresh,
request.cache_control.max_age, # $ MISSING: tainted
request.cache_control.max_stale, # $ MISSING: tainted
request.cache_control.min_fresh, # $ MISSING: tainted
request.content_encoding,
request.content_encoding, # $ tainted
request.content_md5,
request.content_md5, # $ tainted
request.content_type,
request.content_type, # $ tainted
# werkzeug.datastructures.ImmutableTypeConversionDict (which is basically just a dict)
request.cookies,
request.cookies['key'],
request.cookies, # $ tainted
request.cookies['key'], # $ tainted
request.data,
request.data, # $ tainted
# a werkzeug.datastructures.MultiDict, mapping [str, werkzeug.datastructures.FileStorage]
request.files,
request.files['key'],
request.files['key'].filename,
request.files['key'].stream,
request.files.getlist('key'),
request.files.getlist('key')[0].filename,
request.files.getlist('key')[0].stream,
request.files, # $ tainted
request.files['key'], # $ tainted
request.files['key'].filename, # $ MISSING: tainted
request.files['key'].stream, # $ MISSING: tainted
request.files.get('key'), # $ tainted
request.files.get('key').filename, # $ MISSING: tainted
request.files.get('key').stream, # $ MISSING: tainted
request.files.getlist('key'), # $ tainted
request.files.getlist('key')[0].filename, # $ MISSING: tainted
request.files.getlist('key')[0].stream, # $ MISSING: tainted
# By default werkzeug.datastructures.ImmutableMultiDict -- although can be changed :\
request.form,
request.form['key'],
request.form.getlist('key'),
request.form, # $ tainted
request.form['key'], # $ tainted
request.form.get('key'), # $ tainted
request.form.getlist('key'), # $ tainted
request.get_data(),
request.get_data(), # $ tainted
request.get_json(),
request.get_json()['foo'],
request.get_json()['foo']['bar'],
request.get_json(), # $ tainted
request.get_json()['foo'], # $ tainted
request.get_json()['foo']['bar'], # $ tainted
# werkzeug.datastructures.EnvironHeaders,
# which has same interface as werkzeug.datastructures.Headers
request.headers,
request.headers['key'],
request.headers.get_all('key'),
request.headers.getlist('key'),
list(request.headers), # (k, v) list
request.headers.to_wsgi_list(), # (k, v) list
request.headers, # $ tainted
request.headers['key'], # $ tainted
request.headers.get('key'), # $ tainted
request.headers.get_all('key'), # $ MISSING: tainted
request.headers.getlist('key'), # $ MISSING: tainted
# two ways to get (k, v) lists
list(request.headers), # $ tainted
request.headers.to_wsgi_list(), # $ MISSING: tainted
request.json,
request.json['foo'],
request.json['foo']['bar'],
request.json, # $ tainted
request.json['foo'], # $ tainted
request.json['foo']['bar'], # $ tainted
request.method,
request.method, # $ tainted
request.mimetype,
request.mimetype, # $ tainted
request.mimetype_params,
request.mimetype_params, # $ tainted
request.origin,
request.origin, # $ tainted
# werkzeug.datastructures.HeaderSet (subclass of collections_abc.MutableSet)
request.pragma,
request.pragma, # $ tainted
request.query_string,
request.query_string, # $ tainted
request.referrer,
request.referrer, # $ tainted
request.remote_addr,
request.remote_addr, # $ tainted
request.remote_user,
request.remote_user, # $ tainted
# file-like object
request.stream,
request.input_stream,
request.stream, # $ tainted
request.input_stream, # $ tainted
request.url,
request.url, # $ tainted
request.user_agent,
request.user_agent, # $ tainted
# werkzeug.datastructures.CombinedMultiDict, which is basically just a werkzeug.datastructures.MultiDict
request.values,
request.values['key'],
request.values.getlist('key'),
request.values, # $ tainted
request.values['key'], # $ tainted
request.values.get('key'), # $ tainted
request.values.getlist('key'), # $ tainted
# dict
request.view_args,
request.view_args['key'],
request.view_args, # $ tainted
request.view_args['key'], # $ tainted
request.view_args.get('key'), # $ tainted
)
ensure_not_tainted(
@@ -166,26 +175,26 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
b = a
gl = b.getlist
ensure_tainted(
request.args,
a,
b,
request.args, # $ tainted
a, # $ tainted
b, # $ tainted
request.args['key'],
a['key'],
b['key'],
request.args['key'], # $ tainted
a['key'], # $ tainted
b['key'], # $ tainted
request.args.getlist('key'),
a.getlist('key'),
b.getlist('key'),
gl('key'),
request.args.getlist('key'), # $ tainted
a.getlist('key'), # $ tainted
b.getlist('key'), # $ tainted
gl('key'), # $ tainted
)
# aliasing tests
req = request
gd = request.get_data
ensure_tainted(
req.path,
gd(),
req.path, # $ tainted
gd(), # $ tainted
)

View File

@@ -6,7 +6,7 @@ private import semmle.python.dataflow.new.TaintTracking
/** A data-flow Node representing an instance of MyClass. */
abstract class MyClass extends DataFlow::Node { }
private DataFlow::Node myClassGetValue(MyClass qualifier, DataFlow::TypeTracker t) {
private DataFlow::LocalSourceNode myClassGetValue(MyClass qualifier, DataFlow::TypeTracker t) {
t.startInAttr("get_value") and
result = qualifier
or
@@ -14,7 +14,7 @@ private DataFlow::Node myClassGetValue(MyClass qualifier, DataFlow::TypeTracker
}
DataFlow::Node myClassGetValue(MyClass qualifier) {
result = myClassGetValue(qualifier, DataFlow::TypeTracker::end())
myClassGetValue(qualifier, DataFlow::TypeTracker::end()).flowsTo(result)
}
// Config

View File

@@ -32,8 +32,8 @@ def test_additional_taint():
cmd3 = builtins.compile(src, "<filename>", "exec")
ensure_tainted(
src,
cmd1,
cmd2,
cmd3,
src, # $ tainted
cmd1, # $ tainted
cmd2, # $ tainted
cmd3, # $ tainted
)

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,37 +0,0 @@
| CodeExecution.py:35 | ok | test_additional_taint | src |
| CodeExecution.py:36 | ok | test_additional_taint | cmd1 |
| CodeExecution.py:37 | ok | test_additional_taint | cmd2 |
| CodeExecution.py:38 | ok | test_additional_taint | cmd3 |
| http_server.py:22 | ok | test_cgi_FieldStorage_taint | form |
| http_server.py:24 | ok | test_cgi_FieldStorage_taint | form['key'] |
| http_server.py:25 | ok | test_cgi_FieldStorage_taint | form['key'].value |
| http_server.py:26 | ok | test_cgi_FieldStorage_taint | form['key'].file |
| http_server.py:27 | ok | test_cgi_FieldStorage_taint | form['key'].filename |
| http_server.py:28 | ok | test_cgi_FieldStorage_taint | form['key'][0] |
| http_server.py:29 | ok | test_cgi_FieldStorage_taint | form['key'][0].value |
| http_server.py:30 | ok | test_cgi_FieldStorage_taint | form['key'][0].file |
| http_server.py:31 | ok | test_cgi_FieldStorage_taint | form['key'][0].filename |
| http_server.py:32 | fail | test_cgi_FieldStorage_taint | ListComp |
| http_server.py:34 | ok | test_cgi_FieldStorage_taint | form.getvalue(..) |
| http_server.py:35 | ok | test_cgi_FieldStorage_taint | form.getvalue(..)[0] |
| http_server.py:37 | ok | test_cgi_FieldStorage_taint | form.getfirst(..) |
| http_server.py:39 | ok | test_cgi_FieldStorage_taint | form.getlist(..) |
| http_server.py:40 | ok | test_cgi_FieldStorage_taint | form.getlist(..)[0] |
| http_server.py:41 | fail | test_cgi_FieldStorage_taint | ListComp |
| http_server.py:50 | ok | taint_sources | self |
| http_server.py:52 | ok | taint_sources | self.requestline |
| http_server.py:54 | ok | taint_sources | self.path |
| http_server.py:56 | ok | taint_sources | self.headers |
| http_server.py:57 | ok | taint_sources | self.headers['Foo'] |
| http_server.py:58 | ok | taint_sources | self.headers.get(..) |
| http_server.py:59 | fail | taint_sources | self.headers.get_all(..) |
| http_server.py:60 | fail | taint_sources | self.headers.keys() |
| http_server.py:61 | ok | taint_sources | self.headers.values() |
| http_server.py:62 | ok | taint_sources | self.headers.items() |
| http_server.py:63 | fail | taint_sources | self.headers.as_bytes() |
| http_server.py:64 | fail | taint_sources | self.headers.as_string() |
| http_server.py:65 | ok | taint_sources | str(..) |
| http_server.py:66 | ok | taint_sources | bytes(..) |
| http_server.py:68 | ok | taint_sources | self.rfile |
| http_server.py:69 | fail | taint_sources | self.rfile.read() |
| http_server.py:78 | ok | taint_sources | form |

View File

@@ -1,9 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib
import semmle.python.dataflow.new.RemoteFlowSources
class WithRemoteFlowSources extends TestTaintTrackingConfiguration {
override predicate isSource(DataFlow::Node source) {
super.isSource(source) or
source instanceof RemoteFlowSource
}
}

View File

@@ -19,26 +19,28 @@ def test_cgi_FieldStorage_taint():
form = cgi.FieldStorage()
ensure_tainted(
form,
form, # $ tainted
form['key'], # will be a list, if multiple fields named "key" are provided
form['key'].value,
form['key'].file,
form['key'].filename,
form['key'][0],
form['key'][0].value,
form['key'][0].file,
form['key'][0].filename,
[field.value for field in form['key']],
# `form['key']` will be a list, if multiple fields named "key" are provided
form['key'], # $ tainted
form['key'].value, # $ tainted
form['key'].file, # $ tainted
form['key'].filename, # $ tainted
form['key'][0], # $ tainted
form['key'][0].value, # $ tainted
form['key'][0].file, # $ tainted
form['key'][0].filename, # $ tainted
[field.value for field in form['key']], # $ MISSING: tainted
form.getvalue('key'), # will be a list, if multiple fields named "key" are provided
form.getvalue('key')[0],
# `form.getvalue('key')` will be a list, if multiple fields named "key" are provided
form.getvalue('key'), # $ tainted
form.getvalue('key')[0], # $ tainted
form.getfirst('key'),
form.getfirst('key'), # $ tainted
form.getlist('key'),
form.getlist('key')[0],
[field.value for field in form.getlist('key')],
form.getlist('key'), # $ tainted
form.getlist('key')[0], # $ tainted
[field.value for field in form.getlist('key')], # $ MISSING: tainted
)
@@ -47,26 +49,26 @@ class MyHandler(BaseHTTPRequestHandler):
def taint_sources(self):
ensure_tainted(
self,
self, # $ tainted
self.requestline,
self.requestline, # $ tainted
self.path,
self.path, # $ tainted
self.headers,
self.headers['Foo'],
self.headers.get('Foo'),
self.headers.get_all('Foo'),
self.headers.keys(),
self.headers.values(),
self.headers.items(),
self.headers.as_bytes(),
self.headers.as_string(),
str(self.headers),
bytes(self.headers),
self.headers, # $ tainted
self.headers['Foo'], # $ tainted
self.headers.get('Foo'), # $ tainted
self.headers.get_all('Foo'), # $ MISSING: tainted
self.headers.keys(), # $ MISSING: tainted
self.headers.values(), # $ tainted
self.headers.items(), # $ tainted
self.headers.as_bytes(), # $ MISSING: tainted
self.headers.as_string(), # $ MISSING: tainted
str(self.headers), # $ tainted
bytes(self.headers), # $ tainted
self.rfile,
self.rfile.read(),
self.rfile, # $ tainted
self.rfile.read(), # $ MISSING: tainted
)
form = cgi.FieldStorage(
@@ -75,7 +77,7 @@ class MyHandler(BaseHTTPRequestHandler):
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers.get('content-type')},
)
ensure_tainted(form)
ensure_tainted(form) # $ tainted
def do_GET(self): # $ requestHandler

View File

@@ -0,0 +1,3 @@
argumentToEnsureNotTaintedNotMarkedAsSpurious
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
failures

View File

@@ -0,0 +1 @@
import experimental.meta.InlineTaintTest

View File

@@ -1,41 +0,0 @@
| taint_test.py:6 | ok | get | name |
| taint_test.py:6 | ok | get | number |
| taint_test.py:7 | ok | get | foo |
| taint_test.py:11 | ok | get | self.get_argument(..) |
| taint_test.py:12 | ok | get | self.get_arguments(..) |
| taint_test.py:13 | ok | get | self.get_arguments(..)[0] |
| taint_test.py:15 | ok | get | self.get_body_argument(..) |
| taint_test.py:16 | ok | get | self.get_body_arguments(..) |
| taint_test.py:17 | ok | get | self.get_body_arguments(..)[0] |
| taint_test.py:19 | ok | get | self.get_query_argument(..) |
| taint_test.py:20 | ok | get | self.get_query_arguments(..) |
| taint_test.py:21 | ok | get | self.get_query_arguments(..)[0] |
| taint_test.py:23 | ok | get | self.path_args |
| taint_test.py:24 | ok | get | self.path_args[0] |
| taint_test.py:26 | ok | get | self.path_kwargs |
| taint_test.py:27 | ok | get | self.path_kwargs["name"] |
| taint_test.py:34 | ok | get | request |
| taint_test.py:40 | ok | get | request.uri |
| taint_test.py:41 | ok | get | request.path |
| taint_test.py:42 | ok | get | request.query |
| taint_test.py:43 | ok | get | request.full_url() |
| taint_test.py:45 | ok | get | request.remote_ip |
| taint_test.py:47 | ok | get | request.body |
| taint_test.py:49 | ok | get | request.arguments |
| taint_test.py:50 | ok | get | request.arguments["name"] |
| taint_test.py:51 | ok | get | request.arguments["name"][0] |
| taint_test.py:53 | ok | get | request.query_arguments |
| taint_test.py:54 | ok | get | request.query_arguments["name"] |
| taint_test.py:55 | ok | get | request.query_arguments["name"][0] |
| taint_test.py:57 | ok | get | request.body_arguments |
| taint_test.py:58 | ok | get | request.body_arguments["name"] |
| taint_test.py:59 | ok | get | request.body_arguments["name"][0] |
| taint_test.py:62 | ok | get | request.headers |
| taint_test.py:63 | ok | get | request.headers["header-name"] |
| taint_test.py:64 | fail | get | request.headers.get_list(..) |
| taint_test.py:65 | fail | get | request.headers.get_all() |
| taint_test.py:66 | fail | get | ListComp |
| taint_test.py:69 | ok | get | request.cookies |
| taint_test.py:70 | ok | get | request.cookies["cookie-name"] |
| taint_test.py:71 | fail | get | request.cookies["cookie-name"].key |
| taint_test.py:72 | fail | get | request.cookies["cookie-name"].value |

View File

@@ -1,6 +0,0 @@
import experimental.dataflow.tainttracking.TestTaintLib
import semmle.python.dataflow.new.RemoteFlowSources
class RemoteFlowTestTaintConfiguration extends TestTaintTrackingConfiguration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
}

View File

@@ -3,73 +3,73 @@ import tornado.web
class TaintTest(tornado.web.RequestHandler):
def get(self, name = "World!", number="0", foo="foo"): # $ requestHandler routedParameter=name routedParameter=number
ensure_tainted(name, number)
ensure_tainted(name, number) # $ tainted
ensure_not_tainted(foo)
ensure_tainted(
# see https://www.tornadoweb.org/en/stable/web.html#input
self.get_argument("name"),
self.get_arguments("name"),
self.get_arguments("name")[0],
self.get_argument("name"), # $ tainted
self.get_arguments("name"), # $ tainted
self.get_arguments("name")[0], # $ tainted
self.get_body_argument("name"),
self.get_body_arguments("name"),
self.get_body_arguments("name")[0],
self.get_body_argument("name"), # $ tainted
self.get_body_arguments("name"), # $ tainted
self.get_body_arguments("name")[0], # $ tainted
self.get_query_argument("name"),
self.get_query_arguments("name"),
self.get_query_arguments("name")[0],
self.get_query_argument("name"), # $ tainted
self.get_query_arguments("name"), # $ tainted
self.get_query_arguments("name")[0], # $ tainted
self.path_args,
self.path_args[0],
self.path_args, # $ tainted
self.path_args[0], # $ tainted
self.path_kwargs,
self.path_kwargs["name"],
self.path_kwargs, # $ tainted
self.path_kwargs["name"], # $ tainted
)
request = self.request
ensure_tainted(
# see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPServerRequest
request,
request, # $ tainted
# For the URL https:://example.com/foo/bar?baz=42
# request.uri="/foo/bar?baz=42"
# request.path="/foo/bar"
# request.query="baz=42"
request.uri,
request.path,
request.query,
request.full_url(),
request.uri, # $ tainted
request.path, # $ tainted
request.query, # $ tainted
request.full_url(), # $ tainted
request.remote_ip,
request.remote_ip, # $ tainted
request.body,
request.body, # $ tainted
request.arguments,
request.arguments["name"],
request.arguments["name"][0],
request.arguments, # $ tainted
request.arguments["name"], # $ tainted
request.arguments["name"][0], # $ tainted
request.query_arguments,
request.query_arguments["name"],
request.query_arguments["name"][0],
request.query_arguments, # $ tainted
request.query_arguments["name"], # $ tainted
request.query_arguments["name"][0], # $ tainted
request.body_arguments,
request.body_arguments["name"],
request.body_arguments["name"][0],
request.body_arguments, # $ tainted
request.body_arguments["name"], # $ tainted
request.body_arguments["name"][0], # $ tainted
# dict-like, see https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders
request.headers,
request.headers["header-name"],
request.headers.get_list("header-name"),
request.headers.get_all(),
[(k, v) for (k, v) in request.headers.get_all()],
request.headers, # $ tainted
request.headers["header-name"], # $ tainted
request.headers.get_list("header-name"), # $ MISSING: tainted
request.headers.get_all(), # $ MISSING: tainted
[(k, v) for (k, v) in request.headers.get_all()], # $ MISSING: tainted
# Dict[str, http.cookies.Morsel]
request.cookies,
request.cookies["cookie-name"],
request.cookies["cookie-name"].key,
request.cookies["cookie-name"].value,
request.cookies, # $ tainted
request.cookies["cookie-name"], # $ tainted
request.cookies["cookie-name"].key, # $ MISSING: tainted
request.cookies["cookie-name"].value, # $ MISSING: tainted
)