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:
Rasmus Wriedt Larsen
2020-08-25 14:16:18 +02:00
parent 01a61469d3
commit a1ada62596
23 changed files with 500 additions and 108 deletions

View File

@@ -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) {

View File

@@ -0,0 +1 @@
| test.py:16 | fail | test_access | tainted_list.copy() |

View File

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

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

@@ -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()

View File

@@ -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 |

View File

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

View File

@@ -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")

View File

@@ -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(..) |

View File

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

View File

@@ -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()

View File

@@ -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 |

View File

@@ -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

View File

@@ -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(..) |

View File

@@ -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")

View File

@@ -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))

View File

@@ -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 |

View File

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

View File

@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=1 --lang=3

View File

@@ -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()

View File

@@ -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 |

View File

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

View File

@@ -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()