mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Python: Remodel taint tests for shared lib
I took the bits from ql/test/library-tests/taint/ that seemed easy to port. I left out namedtuple for now, but it is part of internal tracking ticket, so won't be forgotten.
This commit is contained in:
@@ -6,7 +6,8 @@ class TestTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in ["TAINTED_STRING", "TAINTED_BYTES"]
|
||||
source.(DataFlow::CfgNode).getNode().(NameNode).getId() in ["TAINTED_STRING", "TAINTED_BYTES",
|
||||
"TAINTED_LIST", "TAINTED_DICT"]
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test.py:16 | fail | test_access | tainted_list.copy() |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=1 --lang=3
|
||||
@@ -0,0 +1,22 @@
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
# Actual tests
|
||||
|
||||
def test_access():
|
||||
tainted_list = TAINTED_LIST
|
||||
|
||||
ensure_tainted(
|
||||
tainted_list.copy(),
|
||||
)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test_access()
|
||||
@@ -0,0 +1,37 @@
|
||||
| test.py:24 | ok | test_construction | tainted_string |
|
||||
| test.py:25 | fail | test_construction | tainted_list |
|
||||
| test.py:26 | fail | test_construction | tainted_tuple |
|
||||
| test.py:27 | fail | test_construction | tainted_set |
|
||||
| test.py:28 | fail | test_construction | tainted_dict |
|
||||
| test.py:32 | fail | test_construction | list(..) |
|
||||
| test.py:33 | fail | test_construction | list(..) |
|
||||
| test.py:34 | fail | test_construction | list(..) |
|
||||
| test.py:35 | fail | test_construction | list(..) |
|
||||
| test.py:36 | fail | test_construction | list(..) |
|
||||
| test.py:38 | fail | test_construction | tuple(..) |
|
||||
| test.py:39 | fail | test_construction | set(..) |
|
||||
| test.py:40 | fail | test_construction | frozenset(..) |
|
||||
| test.py:48 | ok | test_access | tainted_list[0] |
|
||||
| test.py:49 | ok | test_access | tainted_list[x] |
|
||||
| test.py:50 | ok | test_access | tainted_list[Slice] |
|
||||
| test.py:54 | fail | test_access | a |
|
||||
| test.py:54 | fail | test_access | b |
|
||||
| test.py:54 | fail | test_access | c |
|
||||
| test.py:57 | fail | test_access | h |
|
||||
| test.py:59 | fail | test_access | i |
|
||||
| test.py:66 | ok | test_dict_access | tainted_dict["name"] |
|
||||
| test.py:67 | ok | test_dict_access | tainted_dict[x] |
|
||||
| test.py:68 | fail | test_dict_access | tainted_dict.copy() |
|
||||
| test.py:72 | fail | test_dict_access | v |
|
||||
| test.py:74 | fail | test_dict_access | v |
|
||||
| test.py:82 | fail | test_named_tuple | point[0] |
|
||||
| test.py:83 | fail | test_named_tuple | point.x |
|
||||
| test.py:87 | ok | test_named_tuple | point[1] |
|
||||
| test.py:88 | ok | test_named_tuple | point.y |
|
||||
| test.py:92 | fail | test_named_tuple | a |
|
||||
| test.py:93 | ok | test_named_tuple | b |
|
||||
| test.py:101 | fail | test_defaultdict | tainted_default_dict["name"] |
|
||||
| test.py:102 | fail | test_defaultdict | tainted_default_dict[x] |
|
||||
| test.py:103 | fail | test_defaultdict | tainted_default_dict.copy() |
|
||||
| test.py:106 | fail | test_defaultdict | v |
|
||||
| test.py:108 | fail | test_defaultdict | v |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -0,0 +1,117 @@
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
|
||||
# Actual tests
|
||||
|
||||
from collections import defaultdict, namedtuple
|
||||
|
||||
|
||||
def test_construction():
|
||||
tainted_string = TAINTED_STRING
|
||||
tainted_list = [tainted_string]
|
||||
tainted_tuple = (tainted_string,)
|
||||
tainted_set = {tainted_string} # TODO: set currently not handled
|
||||
tainted_dict = {'key': tainted_string}
|
||||
|
||||
ensure_tainted(
|
||||
tainted_string,
|
||||
tainted_list,
|
||||
tainted_tuple,
|
||||
tainted_set,
|
||||
tainted_dict,
|
||||
)
|
||||
|
||||
ensure_tainted(
|
||||
list(tainted_list),
|
||||
list(tainted_tuple),
|
||||
list(tainted_set), # TODO: set currently not handled
|
||||
list(tainted_dict.values()),
|
||||
list(tainted_dict.items()), # TODO: dict.items() currently not handled
|
||||
|
||||
tuple(tainted_list),
|
||||
set(tainted_list),
|
||||
frozenset(tainted_list), # TODO: frozenset constructor currently not handled
|
||||
)
|
||||
|
||||
|
||||
def test_access(x, y, z):
|
||||
tainted_list = TAINTED_LIST
|
||||
|
||||
ensure_tainted(
|
||||
tainted_list[0],
|
||||
tainted_list[x],
|
||||
tainted_list[y:z],
|
||||
)
|
||||
|
||||
a, b, c = tainted_list[0:3]
|
||||
ensure_tainted(a, b, c)
|
||||
|
||||
for h in tainted_list:
|
||||
ensure_tainted(h)
|
||||
for i in reversed(tainted_list):
|
||||
ensure_tainted(i)
|
||||
|
||||
|
||||
def test_dict_access(x):
|
||||
tainted_dict = TAINTED_DICT
|
||||
|
||||
ensure_tainted(
|
||||
tainted_dict["name"],
|
||||
tainted_dict[x],
|
||||
tainted_dict.copy(),
|
||||
)
|
||||
|
||||
for v in tainted_dict.values():
|
||||
ensure_tainted(v)
|
||||
for k, v in tainted_dict.items(): # TODO: dict.items() currently not handled
|
||||
ensure_tainted(v)
|
||||
|
||||
|
||||
def test_named_tuple(): # TODO: namedtuple currently not handled
|
||||
Point = namedtuple('Point', ['x', 'y'])
|
||||
point = Point(TAINTED_STRING, 'safe')
|
||||
|
||||
ensure_tainted(
|
||||
point[0],
|
||||
point.x,
|
||||
)
|
||||
|
||||
ensure_not_tainted(
|
||||
point[1],
|
||||
point.y,
|
||||
)
|
||||
|
||||
a, b = point
|
||||
ensure_tainted(a)
|
||||
ensure_not_tainted(b)
|
||||
|
||||
|
||||
def test_defaultdict(key, x): # TODO: defaultdict currently not handled
|
||||
tainted_default_dict = defaultdict(str)
|
||||
tainted_default_dict[key] += TAINTED_STRING
|
||||
|
||||
ensure_tainted(
|
||||
tainted_default_dict["name"],
|
||||
tainted_default_dict[x],
|
||||
tainted_default_dict.copy(),
|
||||
)
|
||||
for v in tainted_default_dict.values():
|
||||
ensure_tainted(v)
|
||||
for k, v in tainted_default_dict.items():
|
||||
ensure_tainted(v)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test_construction()
|
||||
test_access(0, 0, 2)
|
||||
test_dict_access("name")
|
||||
test_named_tuple()
|
||||
test_defaultdict("key", "key")
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.py:26 | fail | test | json.dumps(..) |
|
||||
| test.py:27 | fail | test | json.loads(..) |
|
||||
| test.py:34 | fail | test | tainted_filelike |
|
||||
| test.py:35 | fail | test | json.load(..) |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -0,0 +1,41 @@
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
|
||||
# Actual tests
|
||||
|
||||
import json
|
||||
from io import StringIO
|
||||
|
||||
# Workaround for Python3 not having unicode
|
||||
import sys
|
||||
if sys.version_info[0] == 3:
|
||||
unicode = str
|
||||
|
||||
def test():
|
||||
print("\n# test")
|
||||
ts = TAINTED_STRING
|
||||
|
||||
ensure_tainted(
|
||||
json.dumps(ts),
|
||||
json.loads(json.dumps(ts)),
|
||||
)
|
||||
|
||||
# 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),
|
||||
)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test()
|
||||
@@ -1,10 +1,10 @@
|
||||
| test.py:26 | ok | str_methods | ts.casefold() |
|
||||
| test.py:28 | ok | str_methods | ts.format_map(..) |
|
||||
| test.py:29 | fail | str_methods | "{unsafe}".format_map(..) |
|
||||
| test.py:40 | fail | binary_decode_encode | base64.a85encode(..) |
|
||||
| test.py:41 | fail | binary_decode_encode | base64.a85decode(..) |
|
||||
| test.py:44 | fail | binary_decode_encode | base64.b85encode(..) |
|
||||
| test.py:45 | fail | binary_decode_encode | base64.b85decode(..) |
|
||||
| test.py:48 | fail | binary_decode_encode | base64.encodebytes(..) |
|
||||
| test.py:49 | fail | binary_decode_encode | base64.decodebytes(..) |
|
||||
| test.py:57 | ok | f_strings | Fstring |
|
||||
| test.py:17 | ok | str_methods | ts.casefold() |
|
||||
| test.py:19 | ok | str_methods | ts.format_map(..) |
|
||||
| test.py:20 | fail | str_methods | "{unsafe}".format_map(..) |
|
||||
| test.py:31 | fail | binary_decode_encode | base64.a85encode(..) |
|
||||
| test.py:32 | fail | binary_decode_encode | base64.a85decode(..) |
|
||||
| test.py:35 | fail | binary_decode_encode | base64.b85encode(..) |
|
||||
| test.py:36 | fail | binary_decode_encode | base64.b85decode(..) |
|
||||
| test.py:39 | fail | binary_decode_encode | base64.encodebytes(..) |
|
||||
| test.py:40 | fail | binary_decode_encode | base64.decodebytes(..) |
|
||||
| test.py:48 | ok | f_strings | Fstring |
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
# Python 3 specific taint tracking for string
|
||||
|
||||
TAINTED_STRING = "TAINTED_STRING"
|
||||
TAINTED_BYTES = b"TAINTED_BYTES"
|
||||
|
||||
|
||||
def ensure_tainted(*args):
|
||||
print("- ensure_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
|
||||
|
||||
def ensure_not_tainted(*args):
|
||||
print("- ensure_not_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
# Actual tests
|
||||
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
| test.py:32 | ok | str_operations | ts |
|
||||
| test.py:33 | ok | str_operations | BinaryExpr |
|
||||
| test.py:34 | ok | str_operations | BinaryExpr |
|
||||
| test.py:35 | ok | str_operations | BinaryExpr |
|
||||
| test.py:36 | ok | str_operations | ts[Slice] |
|
||||
| test.py:37 | ok | str_operations | ts[Slice] |
|
||||
| test.py:38 | ok | str_operations | ts[Slice] |
|
||||
| test.py:39 | ok | str_operations | ts[0] |
|
||||
| test.py:40 | ok | str_operations | str(..) |
|
||||
| test.py:41 | ok | str_operations | bytes(..) |
|
||||
| test.py:42 | ok | str_operations | unicode(..) |
|
||||
| test.py:51 | ok | str_methods | ts.capitalize() |
|
||||
| test.py:52 | ok | str_methods | ts.center(..) |
|
||||
| test.py:53 | ok | str_methods | ts.expandtabs() |
|
||||
| test.py:55 | ok | str_methods | ts.format() |
|
||||
| test.py:56 | ok | str_methods | "{}".format(..) |
|
||||
| test.py:57 | ok | str_methods | "{unsafe}".format(..) |
|
||||
| test.py:59 | ok | str_methods | ts.join(..) |
|
||||
| test.py:60 | fail | str_methods | "".join(..) |
|
||||
| test.py:62 | ok | str_methods | ts.ljust(..) |
|
||||
| test.py:63 | ok | str_methods | ts.lstrip() |
|
||||
| test.py:64 | ok | str_methods | ts.lower() |
|
||||
| test.py:66 | ok | str_methods | ts.replace(..) |
|
||||
| test.py:67 | ok | str_methods | "safe".replace(..) |
|
||||
| test.py:69 | ok | str_methods | ts.rjust(..) |
|
||||
| test.py:70 | ok | str_methods | ts.rstrip() |
|
||||
| test.py:71 | ok | str_methods | ts.strip() |
|
||||
| test.py:72 | ok | str_methods | ts.swapcase() |
|
||||
| test.py:73 | ok | str_methods | ts.title() |
|
||||
| test.py:74 | ok | str_methods | ts.upper() |
|
||||
| test.py:75 | ok | str_methods | ts.zfill(..) |
|
||||
| test.py:77 | ok | str_methods | ts.encode(..) |
|
||||
| test.py:78 | ok | str_methods | ts.encode(..).decode(..) |
|
||||
| test.py:80 | ok | str_methods | tb.decode(..) |
|
||||
| test.py:81 | ok | str_methods | tb.decode(..).encode(..) |
|
||||
| test.py:84 | ok | str_methods | ts.partition(..) |
|
||||
| test.py:85 | ok | str_methods | ts.rpartition(..) |
|
||||
| test.py:86 | ok | str_methods | ts.rsplit(..) |
|
||||
| test.py:87 | ok | str_methods | ts.split(..) |
|
||||
| test.py:88 | ok | str_methods | ts.splitlines() |
|
||||
| test.py:93 | ok | str_methods | "safe".replace(..) |
|
||||
| test.py:95 | fail | str_methods | ts.join(..) |
|
||||
| test.py:96 | fail | str_methods | ts.join(..) |
|
||||
| test.py:106 | fail | non_syntactic | meth() |
|
||||
| test.py:107 | fail | non_syntactic | _str(..) |
|
||||
| test.py:116 | ok | percent_fmt | BinaryExpr |
|
||||
| test.py:117 | ok | percent_fmt | BinaryExpr |
|
||||
| test.py:118 | fail | percent_fmt | BinaryExpr |
|
||||
| test.py:128 | fail | binary_decode_encode | base64.b64encode(..) |
|
||||
| test.py:129 | fail | binary_decode_encode | base64.b64decode(..) |
|
||||
| test.py:131 | fail | binary_decode_encode | base64.standard_b64encode(..) |
|
||||
| test.py:132 | fail | binary_decode_encode | base64.standard_b64decode(..) |
|
||||
| test.py:134 | fail | binary_decode_encode | base64.urlsafe_b64encode(..) |
|
||||
| test.py:135 | fail | binary_decode_encode | base64.urlsafe_b64decode(..) |
|
||||
| test.py:137 | fail | binary_decode_encode | base64.b32encode(..) |
|
||||
| test.py:138 | fail | binary_decode_encode | base64.b32decode(..) |
|
||||
| test.py:140 | fail | binary_decode_encode | base64.b16encode(..) |
|
||||
| test.py:141 | fail | binary_decode_encode | base64.b16decode(..) |
|
||||
| test.py:156 | fail | binary_decode_encode | base64.encodestring(..) |
|
||||
| test.py:157 | fail | binary_decode_encode | base64.decodestring(..) |
|
||||
| test.py:162 | fail | binary_decode_encode | quopri.encodestring(..) |
|
||||
| test.py:163 | fail | binary_decode_encode | quopri.decodestring(..) |
|
||||
| test.py:25 | ok | str_operations | ts |
|
||||
| test.py:26 | ok | str_operations | BinaryExpr |
|
||||
| test.py:27 | ok | str_operations | BinaryExpr |
|
||||
| test.py:28 | ok | str_operations | BinaryExpr |
|
||||
| test.py:29 | ok | str_operations | ts[Slice] |
|
||||
| test.py:30 | ok | str_operations | ts[Slice] |
|
||||
| test.py:31 | ok | str_operations | ts[Slice] |
|
||||
| test.py:32 | ok | str_operations | ts[0] |
|
||||
| test.py:33 | ok | str_operations | str(..) |
|
||||
| test.py:34 | ok | str_operations | bytes(..) |
|
||||
| test.py:35 | ok | str_operations | unicode(..) |
|
||||
| test.py:44 | ok | str_methods | ts.capitalize() |
|
||||
| test.py:45 | ok | str_methods | ts.center(..) |
|
||||
| test.py:46 | ok | str_methods | ts.expandtabs() |
|
||||
| test.py:48 | ok | str_methods | ts.format() |
|
||||
| test.py:49 | ok | str_methods | "{}".format(..) |
|
||||
| test.py:50 | ok | str_methods | "{unsafe}".format(..) |
|
||||
| test.py:52 | ok | str_methods | ts.join(..) |
|
||||
| test.py:53 | fail | str_methods | "".join(..) |
|
||||
| test.py:55 | ok | str_methods | ts.ljust(..) |
|
||||
| test.py:56 | ok | str_methods | ts.lstrip() |
|
||||
| test.py:57 | ok | str_methods | ts.lower() |
|
||||
| test.py:59 | ok | str_methods | ts.replace(..) |
|
||||
| test.py:60 | ok | str_methods | "safe".replace(..) |
|
||||
| test.py:62 | ok | str_methods | ts.rjust(..) |
|
||||
| test.py:63 | ok | str_methods | ts.rstrip() |
|
||||
| test.py:64 | ok | str_methods | ts.strip() |
|
||||
| test.py:65 | ok | str_methods | ts.swapcase() |
|
||||
| test.py:66 | ok | str_methods | ts.title() |
|
||||
| test.py:67 | ok | str_methods | ts.upper() |
|
||||
| test.py:68 | ok | str_methods | ts.zfill(..) |
|
||||
| test.py:70 | ok | str_methods | ts.encode(..) |
|
||||
| test.py:71 | ok | str_methods | ts.encode(..).decode(..) |
|
||||
| test.py:73 | ok | str_methods | tb.decode(..) |
|
||||
| test.py:74 | ok | str_methods | tb.decode(..).encode(..) |
|
||||
| test.py:77 | ok | str_methods | ts.partition(..) |
|
||||
| test.py:78 | ok | str_methods | ts.rpartition(..) |
|
||||
| test.py:79 | ok | str_methods | ts.rsplit(..) |
|
||||
| test.py:80 | ok | str_methods | ts.split(..) |
|
||||
| test.py:81 | ok | str_methods | ts.splitlines() |
|
||||
| test.py:86 | ok | str_methods | "safe".replace(..) |
|
||||
| test.py:88 | fail | str_methods | ts.join(..) |
|
||||
| test.py:89 | fail | str_methods | ts.join(..) |
|
||||
| test.py:99 | fail | non_syntactic | meth() |
|
||||
| test.py:100 | fail | non_syntactic | _str(..) |
|
||||
| test.py:109 | ok | percent_fmt | BinaryExpr |
|
||||
| test.py:110 | ok | percent_fmt | BinaryExpr |
|
||||
| test.py:111 | fail | percent_fmt | BinaryExpr |
|
||||
| test.py:121 | fail | binary_decode_encode | base64.b64encode(..) |
|
||||
| test.py:122 | fail | binary_decode_encode | base64.b64decode(..) |
|
||||
| test.py:124 | fail | binary_decode_encode | base64.standard_b64encode(..) |
|
||||
| test.py:125 | fail | binary_decode_encode | base64.standard_b64decode(..) |
|
||||
| test.py:127 | fail | binary_decode_encode | base64.urlsafe_b64encode(..) |
|
||||
| test.py:128 | fail | binary_decode_encode | base64.urlsafe_b64decode(..) |
|
||||
| test.py:130 | fail | binary_decode_encode | base64.b32encode(..) |
|
||||
| test.py:131 | fail | binary_decode_encode | base64.b32decode(..) |
|
||||
| test.py:133 | fail | binary_decode_encode | base64.b16encode(..) |
|
||||
| test.py:134 | fail | binary_decode_encode | base64.b16decode(..) |
|
||||
| test.py:149 | fail | binary_decode_encode | base64.encodestring(..) |
|
||||
| test.py:150 | fail | binary_decode_encode | base64.decodestring(..) |
|
||||
| test.py:155 | fail | binary_decode_encode | quopri.encodestring(..) |
|
||||
| test.py:156 | fail | binary_decode_encode | quopri.decodestring(..) |
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import sys
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
unicode = str
|
||||
|
||||
|
||||
TAINTED_STRING = "TAINTED_STRING"
|
||||
TAINTED_BYTES = b"TAINTED_BYTES"
|
||||
|
||||
|
||||
def ensure_tainted(*args):
|
||||
print("- ensure_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
|
||||
|
||||
def ensure_not_tainted(*args):
|
||||
print("- ensure_not_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
|
||||
# Actual tests
|
||||
|
||||
# Workaround for Python3 not having unicode
|
||||
import sys
|
||||
if sys.version_info[0] == 3:
|
||||
unicode = str
|
||||
|
||||
|
||||
def str_operations():
|
||||
print("\n# str_operations")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
TAINTED_STRING = "TAINTED_STRING"
|
||||
TAINTED_BYTES = b"TAINTED_BYTES"
|
||||
TAINTED_LIST = ["tainted-{}".format(i) for i in range(5)]
|
||||
TAINTED_DICT = {"name": TAINTED_STRING, "some key": "foo"}
|
||||
|
||||
def ensure_tainted(*args):
|
||||
print("- ensure_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
|
||||
|
||||
def ensure_not_tainted(*args):
|
||||
print("- ensure_not_tainted")
|
||||
for i, arg in enumerate(args):
|
||||
print("arg {}: {!r}".format(i, arg))
|
||||
@@ -0,0 +1,9 @@
|
||||
| test.py:18 | fail | extended_unpacking | first |
|
||||
| test.py:18 | fail | extended_unpacking | last |
|
||||
| test.py:18 | fail | extended_unpacking | rest |
|
||||
| test.py:23 | fail | also_allowed | a |
|
||||
| test.py:31 | fail | also_allowed | b |
|
||||
| test.py:31 | fail | also_allowed | c |
|
||||
| test.py:39 | fail | nested | x |
|
||||
| test.py:39 | fail | nested | xs |
|
||||
| test.py:39 | fail | nested | ys |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=1 --lang=3
|
||||
@@ -0,0 +1,46 @@
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
# Actual tests
|
||||
|
||||
# Extended Iterable Unpacking -- PEP 3132
|
||||
# https://www.python.org/dev/peps/pep-3132/
|
||||
|
||||
|
||||
def extended_unpacking():
|
||||
first, *rest, last = TAINTED_LIST
|
||||
ensure_tainted(first, rest, last)
|
||||
|
||||
|
||||
def also_allowed():
|
||||
*a, = TAINTED_LIST
|
||||
ensure_tainted(a)
|
||||
|
||||
# for b, *c in [(1, 2, 3), (4, 5, 6, 7)]:
|
||||
# print(c)
|
||||
# i=0; c=[2,3]
|
||||
# i=1; c=[5,6,7]
|
||||
|
||||
for b, *c in [TAINTED_LIST, TAINTED_LIST]:
|
||||
ensure_tainted(b, c)
|
||||
|
||||
|
||||
def nested():
|
||||
l = TAINTED_LIST
|
||||
ll = [l,l]
|
||||
|
||||
[[x, *xs], ys] = ll
|
||||
ensure_tainted(x, xs, ys)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
extended_unpacking()
|
||||
also_allowed()
|
||||
nested()
|
||||
@@ -0,0 +1,33 @@
|
||||
| test.py:16 | fail | unpacking | a |
|
||||
| test.py:16 | fail | unpacking | b |
|
||||
| test.py:16 | fail | unpacking | c |
|
||||
| test.py:22 | fail | unpacking_to_list | a |
|
||||
| test.py:22 | fail | unpacking_to_list | b |
|
||||
| test.py:22 | fail | unpacking_to_list | c |
|
||||
| test.py:31 | fail | nested | a1 |
|
||||
| test.py:31 | fail | nested | a2 |
|
||||
| test.py:31 | fail | nested | a3 |
|
||||
| test.py:31 | fail | nested | b |
|
||||
| test.py:31 | fail | nested | c |
|
||||
| test.py:35 | fail | nested | a1 |
|
||||
| test.py:35 | fail | nested | a2 |
|
||||
| test.py:35 | fail | nested | a3 |
|
||||
| test.py:35 | fail | nested | b |
|
||||
| test.py:35 | fail | nested | c |
|
||||
| test.py:39 | fail | nested | a1 |
|
||||
| test.py:39 | fail | nested | a2 |
|
||||
| test.py:39 | fail | nested | a3 |
|
||||
| test.py:39 | fail | nested | b |
|
||||
| test.py:39 | fail | nested | c |
|
||||
| test.py:46 | fail | unpack_from_set | a |
|
||||
| test.py:46 | fail | unpack_from_set | b |
|
||||
| test.py:46 | fail | unpack_from_set | c |
|
||||
| test.py:56 | fail | contrived_1 | a |
|
||||
| test.py:56 | fail | contrived_1 | b |
|
||||
| test.py:56 | fail | contrived_1 | c |
|
||||
| test.py:57 | ok | contrived_1 | d |
|
||||
| test.py:57 | ok | contrived_1 | e |
|
||||
| test.py:57 | ok | contrived_1 | f |
|
||||
| test.py:65 | fail | contrived_2 | a |
|
||||
| test.py:65 | fail | contrived_2 | b |
|
||||
| test.py:65 | fail | contrived_2 | c |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -0,0 +1,75 @@
|
||||
# Add taintlib to PATH so it can be imported during runtime without any hassle
|
||||
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from taintlib import *
|
||||
|
||||
# This has no runtime impact, but allows autocomplete to work
|
||||
from typing import TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from ..taintlib import *
|
||||
|
||||
|
||||
# Actual tests
|
||||
|
||||
def unpacking():
|
||||
l = TAINTED_LIST[0:3]
|
||||
a, b, c = l
|
||||
ensure_tainted(a, b, c)
|
||||
|
||||
|
||||
def unpacking_to_list():
|
||||
l = TAINTED_LIST[0:3]
|
||||
[a, b, c] = l
|
||||
ensure_tainted(a, b, c)
|
||||
|
||||
|
||||
def nested():
|
||||
l = TAINTED_LIST[0:3]
|
||||
ll = [l, l, l]
|
||||
|
||||
# list
|
||||
[[a1, a2, a3], b, c] = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
|
||||
# tuple
|
||||
((a1, a2, a3), b, c) = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
|
||||
# mixed
|
||||
[(a1, a2, a3), b, c] = ll
|
||||
ensure_tainted(a1, a2, a3, b, c)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def contrived_1():
|
||||
# A contrived example. Don't know why anyone would ever actually do this.
|
||||
tainted_list = TAINTED_LIST[0:3]
|
||||
no_taint_list = [1,2,3]
|
||||
|
||||
# We don't handle this case currently, since we mark `d`, `e` and `f` as tainted.
|
||||
(a, b, c), (d, e, f) = tainted_list, no_taint_list
|
||||
ensure_tainted(a, b, c)
|
||||
ensure_not_tainted(d, e, f)
|
||||
|
||||
|
||||
def contrived_2():
|
||||
# A contrived example. Don't know why anyone would ever actually do this.
|
||||
|
||||
# We currently only handle taint nested 2 levels.
|
||||
[[[ (a,b,c) ]]] = [[[ TAINTED_LIST[0:3] ]]]
|
||||
ensure_tainted(a, b, c)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
unpacking()
|
||||
unpacking_to_list()
|
||||
nested()
|
||||
unpack_from_set()
|
||||
contrived_1()
|
||||
contrived_2()
|
||||
Reference in New Issue
Block a user