mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Python: Expand on taint sanitizer tests
Most interesting to look at the custom sanitizers. Once we have use-use flow, we should handle this case: ``` s = TAINTED_STRING emulated_authentication_check(s) ensure_not_tainted(s) ```
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
test_taint
|
||||
| test.py:22 | fail | test_custom_sanitizer | s |
|
||||
| test.py:36 | fail | test_custom_sanitizer_guard | s |
|
||||
| test.py:38 | ok | test_custom_sanitizer_guard | s |
|
||||
| test.py:49 | ok | test_escape | s2 |
|
||||
isSanitizer
|
||||
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test.py:48:10:48:29 | ControlFlowNode for emulated_escaping() |
|
||||
isSanitizerGuard
|
||||
@@ -0,0 +1,29 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
|
||||
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(Call call |
|
||||
call.getFunc().(Name).getId() = "emulated_authentication_check" and
|
||||
call.getArg(0) = node.asExpr()
|
||||
)
|
||||
or
|
||||
node.asExpr().(Call).getFunc().(Name).getId() = "emulated_escaping"
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
// exists(Call call |
|
||||
// call.getFunc().(Name).getId() = "emulated_is_safe" and
|
||||
// )
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
query predicate isSanitizer(TestTaintTrackingConfiguration conf, DataFlow::Node node) {
|
||||
exists(node.getLocation().getFile().getRelativePath()) and
|
||||
conf.isSanitizer(node)
|
||||
}
|
||||
|
||||
query predicate isSanitizerGuard(TestTaintTrackingConfiguration conf, DataFlow::BarrierGuard guard) {
|
||||
exists(guard.getLocation().getFile().getRelativePath()) and
|
||||
conf.isSanitizerGuard(guard)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
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 emulated_authentication_check(arg):
|
||||
if not arg == "safe":
|
||||
raise Exception("user unauthenticated")
|
||||
|
||||
|
||||
def test_custom_sanitizer():
|
||||
s = TAINTED_STRING
|
||||
|
||||
try:
|
||||
emulated_authentication_check(s)
|
||||
ensure_not_tainted(s)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def emulated_is_safe(arg):
|
||||
# emulating something we won't be able to look at source code for
|
||||
return eval("False")
|
||||
|
||||
|
||||
def test_custom_sanitizer_guard():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if emulated_is_safe(s):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
|
||||
|
||||
def emulated_escaping(arg):
|
||||
return arg.replace("<", "?").replace(">", "?").replace("'", "?").replace("\"", "?")
|
||||
|
||||
|
||||
def test_escape():
|
||||
s = TAINTED_STRING
|
||||
|
||||
s2 = emulated_escaping(s)
|
||||
ensure_not_tainted(s2)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test_custom_sanitizer()
|
||||
test_custom_sanitizer_guard()
|
||||
test_escape()
|
||||
@@ -1,5 +1,37 @@
|
||||
| test.py:16 | fail | const_eq_clears_taint | ts |
|
||||
| test.py:18 | ok | const_eq_clears_taint | ts |
|
||||
| test.py:24 | fail | const_eq_clears_taint2 | ts |
|
||||
| test.py:29 | ok | non_const_eq_preserves_taint | ts |
|
||||
| test.py:31 | ok | non_const_eq_preserves_taint | ts |
|
||||
| test_logical.py:30 | fail | 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 | fail | test_and | s |
|
||||
| test_logical.py:69 | ok | test_and | s |
|
||||
| test_logical.py:73 | ok | test_and | s |
|
||||
| test_logical.py:75 | fail | 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 | fail | test_nesting_not_with_and_true | s |
|
||||
| test_logical.py:121 | fail | 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 | fail | test_nesting_not_with_and_true | s |
|
||||
| test_string_eq.py:16 | fail | const_eq_clears_taint | ts |
|
||||
| test_string_eq.py:18 | ok | const_eq_clears_taint | ts |
|
||||
| test_string_eq.py:20 | ok | const_eq_clears_taint | ts |
|
||||
| test_string_eq.py:27 | fail | const_eq_clears_taint2 | ts |
|
||||
| test_string_eq.py:33 | ok | non_const_eq_preserves_taint | ts |
|
||||
| test_string_eq.py:35 | ok | non_const_eq_preserves_taint | ts |
|
||||
| test_string_eq.py:45 | fail | const_eq_through_func | ts |
|
||||
| test_string_eq.py:47 | ok | const_eq_through_func | ts |
|
||||
| test_string_eq.py:49 | ok | const_eq_through_func | ts |
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
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
|
||||
|
||||
"""Testing logical constructs not/and/or works out of the box.
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def random_choice():
|
||||
return bool(random.randint(0, 1))
|
||||
|
||||
|
||||
def is_safe(arg):
|
||||
return arg == "safe"
|
||||
|
||||
|
||||
def test_basic():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if is_safe(s):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
|
||||
if not is_safe(s):
|
||||
ensure_tainted(s)
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_or():
|
||||
s = TAINTED_STRING
|
||||
|
||||
# x or y
|
||||
if is_safe(s) or random_choice():
|
||||
ensure_tainted(s) # might be tainted
|
||||
else:
|
||||
ensure_tainted(s) # must be tainted
|
||||
|
||||
# not (x or y)
|
||||
if not(is_safe(s) or random_choice()):
|
||||
ensure_tainted(s) # must be tainted
|
||||
else:
|
||||
ensure_tainted(s) # might be 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
|
||||
else:
|
||||
ensure_tainted(s) # might be tainted
|
||||
|
||||
|
||||
def test_and():
|
||||
s = TAINTED_STRING
|
||||
|
||||
# x and y
|
||||
if is_safe(s) and random_choice():
|
||||
ensure_not_tainted(s) # must not be tainted
|
||||
else:
|
||||
ensure_tainted(s) # might be tainted
|
||||
|
||||
# not (x and y)
|
||||
if not(is_safe(s) and random_choice()):
|
||||
ensure_tainted(s) # might be tainted
|
||||
else:
|
||||
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
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_tricky():
|
||||
s = TAINTED_STRING
|
||||
|
||||
x = is_safe(s)
|
||||
if x:
|
||||
ensure_not_tainted(s) # FP
|
||||
|
||||
s_ = s
|
||||
if is_safe(s):
|
||||
ensure_not_tainted(s_) # FP
|
||||
|
||||
|
||||
def test_nesting_not():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if not(not(is_safe(s))):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
|
||||
if not(not(not(is_safe(s)))):
|
||||
ensure_tainted(s)
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in
|
||||
# SanitizedEdges.expected and compare with `test_nesting_not` and `test_basic`
|
||||
def test_nesting_not_with_and_true():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if not(is_safe(s) and True):
|
||||
ensure_tainted(s)
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
if not(not(is_safe(s) and True)):
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s)
|
||||
|
||||
if not(not(not(is_safe(s) and True))):
|
||||
ensure_tainted(s)
|
||||
else:
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test_basic()
|
||||
test_or()
|
||||
test_and()
|
||||
test_tricky()
|
||||
test_nesting_not()
|
||||
test_nesting_not_with_and_true()
|
||||
@@ -14,15 +14,19 @@ def const_eq_clears_taint():
|
||||
ts = TAINTED_STRING
|
||||
if ts == "safe":
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
# ts should still be tainted after exiting the if block
|
||||
ensure_tainted(ts)
|
||||
|
||||
|
||||
def const_eq_clears_taint2():
|
||||
ts = TAINTED_STRING
|
||||
if ts != "safe":
|
||||
return
|
||||
ensure_not_tainted(ts)
|
||||
|
||||
|
||||
def non_const_eq_preserves_taint(x="foo"):
|
||||
ts = TAINTED_STRING
|
||||
if ts == ts:
|
||||
@@ -31,6 +35,20 @@ def non_const_eq_preserves_taint(x="foo"):
|
||||
ensure_tainted(ts)
|
||||
|
||||
|
||||
def is_safe(x):
|
||||
return x == "safe"
|
||||
|
||||
|
||||
def const_eq_through_func():
|
||||
ts = TAINTED_STRING
|
||||
if is_safe(ts):
|
||||
ensure_not_tainted(ts)
|
||||
else:
|
||||
ensure_tainted(ts)
|
||||
# ts should still be tainted after exiting the if block
|
||||
ensure_tainted(ts)
|
||||
|
||||
|
||||
# Make tests runable
|
||||
|
||||
const_eq_clears_taint()
|
||||
Reference in New Issue
Block a user