mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Resolve merge conflict
This commit is contained in:
17
python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
Normal file
17
python/ql/test/experimental/dataflow/ApiGraphs/async_test.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import pkg # $ use=moduleImport("pkg")
|
||||
|
||||
async def foo():
|
||||
coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
|
||||
coro # $ use=moduleImport("pkg").getMember("async_func").getReturn()
|
||||
result = await coro # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
|
||||
result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
|
||||
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
|
||||
|
||||
async def bar():
|
||||
result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
|
||||
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
|
||||
|
||||
def check_annotations():
|
||||
# Just to make sure how annotations should look like :)
|
||||
result = pkg.sync_func() # $ use=moduleImport("pkg").getMember("sync_func").getReturn()
|
||||
return result # $ use=moduleImport("pkg").getMember("sync_func").getReturn()
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --lang=3
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=1
|
||||
|
||||
@@ -122,14 +122,18 @@ def redefine_print():
|
||||
print = my_print
|
||||
print("these words")
|
||||
|
||||
def local_redefine_range():
|
||||
range = 5
|
||||
return range
|
||||
def local_redefine_chr():
|
||||
chr = 5
|
||||
return chr
|
||||
|
||||
def global_redefine_range():
|
||||
global range
|
||||
range = 6
|
||||
return range #$ SPURIOUS: use=moduleImport("builtins").getMember("range")
|
||||
def global_redefine_chr():
|
||||
global chr
|
||||
chr = 6
|
||||
return chr
|
||||
|
||||
def what_is_chr_now():
|
||||
# If global_redefine_chr has been run, then the following is _not_ a reference to the built-in chr
|
||||
return chr(123) #$ MISSING: use=moduleImport("builtins").getMember("chr").getReturn()
|
||||
|
||||
def obscured_print():
|
||||
p = print #$ use=moduleImport("builtins").getMember("print")
|
||||
|
||||
@@ -9,7 +9,12 @@ 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 and
|
||||
exists(l.getFile().getRelativePath())
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
string prettyExp(Expr e) {
|
||||
string prettyExpr(Expr e) {
|
||||
not e instanceof Num and
|
||||
not e instanceof StrConst and
|
||||
not e instanceof Subscript and
|
||||
@@ -15,17 +15,41 @@ string prettyExp(Expr e) {
|
||||
e.(StrConst).getPrefix() + e.(StrConst).getText() +
|
||||
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
|
||||
or
|
||||
result = prettyExp(e.(Subscript).getObject()) + "[" + prettyExp(e.(Subscript).getIndex()) + "]"
|
||||
result = prettyExpr(e.(Subscript).getObject()) + "[" + prettyExpr(e.(Subscript).getIndex()) + "]"
|
||||
or
|
||||
(
|
||||
if exists(e.(Call).getAnArg()) or exists(e.(Call).getANamedArg())
|
||||
then result = prettyExp(e.(Call).getFunc()) + "(..)"
|
||||
else result = prettyExp(e.(Call).getFunc()) + "()"
|
||||
then result = prettyExpr(e.(Call).getFunc()) + "(..)"
|
||||
else result = prettyExpr(e.(Call).getFunc()) + "()"
|
||||
)
|
||||
or
|
||||
result = prettyExp(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
|
||||
result = prettyExpr(e.(Attribute).getObject()) + "." + e.(Attribute).getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pretty-printed version of the DataFlow::Node `node`
|
||||
*/
|
||||
bindingset[node]
|
||||
string prettyNode(DataFlow::Node node) {
|
||||
if exists(node.asExpr()) then result = prettyExp(node.asExpr()) else result = node.toString()
|
||||
if exists(node.asExpr()) then result = prettyExpr(node.asExpr()) else result = node.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets pretty-printed version of the DataFlow::Node `node`, that is suitable for use
|
||||
* with `TestUtilities.InlineExpectationsTest` (that is, no spaces unless required).
|
||||
*/
|
||||
bindingset[node]
|
||||
string prettyNodeForInlineTest(DataFlow::Node node) {
|
||||
exists(node.asExpr()) and
|
||||
result = prettyExpr(node.asExpr())
|
||||
or
|
||||
exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() |
|
||||
// since PostUpdateNode both has space in the `[post <thing>]` annotation, and does
|
||||
// not pretty print the pre-update node, we do custom handling of this.
|
||||
result = "[post]" + prettyExpr(e)
|
||||
)
|
||||
or
|
||||
not exists(node.asExpr()) and
|
||||
not exists(Expr e | e = node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) and
|
||||
result = node.toString()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// /**
|
||||
// * @kind path-problem
|
||||
// */
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.dataflow.new.SensitiveDataSources
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
class SensitiveDataSourcesTest extends InlineExpectationsTest {
|
||||
SensitiveDataSourcesTest() { this = "SensitiveDataSourcesTest" }
|
||||
|
||||
override string getARelevantTag() { result in ["SensitiveDataSource", "SensitiveUse"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(SensitiveDataSource source |
|
||||
location = source.getLocation() and
|
||||
element = source.toString() and
|
||||
value = source.getClassification() and
|
||||
tag = "SensitiveDataSource"
|
||||
or
|
||||
exists(DataFlow::Node use |
|
||||
any(SensitiveUseConfiguration config).hasFlow(source, use) and
|
||||
location = use.getLocation() and
|
||||
element = use.toString() and
|
||||
value = source.getClassification() and
|
||||
tag = "SensitiveUse"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SensitiveUseConfiguration extends TaintTracking::Configuration {
|
||||
SensitiveUseConfiguration() { this = "SensitiveUseConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof SensitiveDataSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node = API::builtin("print").getACall().getArg(_)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
sensitiveDataExtraStepForCalls(node1, node2)
|
||||
}
|
||||
}
|
||||
// import DataFlow::PathGraph
|
||||
// from SensitiveUseConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
// where cfg.hasFlowPath(source, sink)
|
||||
// select sink, source, sink, "taint from $@", source.getNode(), "here"
|
||||
80
python/ql/test/experimental/dataflow/sensitive-data/test.py
Normal file
80
python/ql/test/experimental/dataflow/sensitive-data/test.py
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
from not_found import get_passwd # $ SensitiveDataSource=password
|
||||
from not_found import account_id # $ SensitiveDataSource=id
|
||||
|
||||
def get_password():
|
||||
pass
|
||||
|
||||
def get_secret():
|
||||
pass
|
||||
|
||||
def fetch_certificate():
|
||||
pass
|
||||
|
||||
def encrypt_password(pwd):
|
||||
pass
|
||||
|
||||
get_password() # $ SensitiveDataSource=password
|
||||
get_passwd() # $ SensitiveDataSource=password
|
||||
get_secret() # $ SensitiveDataSource=secret
|
||||
fetch_certificate() # $ SensitiveDataSource=certificate
|
||||
account_id() # $ SensitiveDataSource=id
|
||||
safe_to_store = encrypt_password(pwd)
|
||||
|
||||
f = get_password
|
||||
f() # $ SensitiveDataSource=password
|
||||
|
||||
# more tests of functions we don't have definition for
|
||||
x = unkown_func_not_even_imported_get_password() # $ SensitiveDataSource=password
|
||||
print(x) # $ SensitiveUse=password
|
||||
|
||||
f = get_passwd
|
||||
x = f()
|
||||
print(x) # $ SensitiveUse=password
|
||||
|
||||
import not_found
|
||||
f = not_found.get_passwd # $ SensitiveDataSource=password
|
||||
x = f()
|
||||
print(x) # $ SensitiveUse=password
|
||||
|
||||
def my_func(non_sensitive_name):
|
||||
x = non_sensitive_name()
|
||||
print(x) # $ SensitiveUse=password
|
||||
f = not_found.get_passwd # $ SensitiveDataSource=password
|
||||
my_func(f)
|
||||
|
||||
# attributes
|
||||
foo = ObjectFromDatabase()
|
||||
foo.secret # $ SensitiveDataSource=secret
|
||||
foo.username # $ SensitiveDataSource=id
|
||||
|
||||
getattr(foo, "password") # $ SensitiveDataSource=password
|
||||
x = "password"
|
||||
getattr(foo, x) # $ SensitiveDataSource=password
|
||||
|
||||
# based on variable/parameter names
|
||||
def my_func(password): # $ SensitiveDataSource=password
|
||||
print(password) # $ SensitiveUse=password
|
||||
|
||||
password = some_function() # $ SensitiveDataSource=password
|
||||
print(password) # $ SensitiveUse=password
|
||||
|
||||
for password in some_function2(): # $ SensitiveDataSource=password
|
||||
print(password) # $ SensitiveUse=password
|
||||
|
||||
with some_function3() as password: # $ SensitiveDataSource=password
|
||||
print(password) # $ SensitiveUse=password
|
||||
|
||||
|
||||
# Special handling of lookups of sensitive properties
|
||||
request.args["password"], # $ SensitiveDataSource=password
|
||||
request.args.get("password") # $ SensitiveDataSource=password
|
||||
|
||||
x = "password"
|
||||
request.args.get(x) # $ SensitiveDataSource=password
|
||||
|
||||
# I don't think handling `getlist` is super important, just included it to show what we don't handle
|
||||
request.args.getlist("password")[0] # $ MISSING: SensitiveDataSource=password
|
||||
|
||||
from not_found import password2 as foo # $ SensitiveDataSource=password
|
||||
print(foo) # $ SensitiveUse=password
|
||||
@@ -46,6 +46,6 @@ query predicate test_taint(string arg_location, string test_res, string scope_na
|
||||
arg_location = arg.getLocation().toString() and
|
||||
test_res = test_res and
|
||||
scope_name = call.getScope().getName() and
|
||||
repr = prettyExp(arg)
|
||||
repr = prettyExpr(arg)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -1,4 +1,4 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import experimental.meta.InlineTaintTest
|
||||
import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
|
||||
@@ -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() |
|
||||
@@ -1,4 +1,4 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
import experimental.meta.InlineTaintTest
|
||||
|
||||
class IsSafeCheck extends DataFlow::BarrierGuard {
|
||||
IsSafeCheck() {
|
||||
@@ -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() |
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -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 | fail | test_basic | tainted_path |
|
||||
| test_pathlib.py:28 | fail | test_basic | tainted_pure_path |
|
||||
| test_pathlib.py:29 | fail | test_basic | tainted_pure_posix_path |
|
||||
| test_pathlib.py:30 | fail | test_basic | tainted_pure_windows_path |
|
||||
| test_pathlib.py:32 | fail | test_basic | BinaryExpr |
|
||||
| test_pathlib.py:33 | fail | test_basic | BinaryExpr |
|
||||
| test_pathlib.py:35 | fail | test_basic | tainted_path.joinpath(..) |
|
||||
| test_pathlib.py:36 | fail | test_basic | pathlib.Path(..).joinpath(..) |
|
||||
| test_pathlib.py:37 | fail | test_basic | pathlib.Path(..).joinpath(..) |
|
||||
| test_pathlib.py:39 | fail | test_basic | str(..) |
|
||||
| test_pathlib.py:49 | fail | test_basic | tainted_posix_path |
|
||||
| test_pathlib.py:55 | fail | 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 |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -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 |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -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
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
# 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 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
|
||||
import json
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
def non_syntacical():
|
||||
print("\n# non_syntacical")
|
||||
ts = TAINTED_STRING
|
||||
|
||||
# a less syntactical approach
|
||||
from json import load, loads, dumps
|
||||
|
||||
dumps_alias = dumps
|
||||
|
||||
ensure_tainted(
|
||||
dumps(ts),
|
||||
dumps_alias(ts),
|
||||
loads(dumps(ts)),
|
||||
)
|
||||
|
||||
# For Python2, need to convert to unicode for StringIO to work
|
||||
tainted_filelike = StringIO(unicode(dumps(ts)))
|
||||
|
||||
ensure_tainted(
|
||||
tainted_filelike,
|
||||
load(tainted_filelike),
|
||||
)
|
||||
|
||||
# Make tests runable
|
||||
|
||||
test()
|
||||
non_syntacical()
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
argumentToEnsureNotTaintedNotMarkedAsSpurious
|
||||
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
|
||||
failures
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -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()
|
||||
|
||||
@@ -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 |
|
||||
@@ -1 +0,0 @@
|
||||
import experimental.dataflow.tainttracking.TestTaintLib
|
||||
@@ -30,8 +30,28 @@ def test_incompatible_types():
|
||||
x.field = str("Hello") # $str=field str SPURIOUS: int=field int
|
||||
expects_string(x) # $ str=field SPURIOUS: int=field
|
||||
|
||||
# set in different function
|
||||
def set_foo(some_class_instance): # $ tracked=foo
|
||||
some_class_instance.foo = tracked # $ tracked=foo tracked
|
||||
|
||||
def test_set_x():
|
||||
x = SomeClass() # $ MISSING: tracked=foo
|
||||
set_foo(x) # $ MISSING: tracked=foo
|
||||
print(x.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
# return from a different function
|
||||
def create_with_foo():
|
||||
x = SomeClass() # $ tracked=foo
|
||||
x.foo = tracked # $ tracked=foo tracked
|
||||
return x # $ tracked=foo
|
||||
|
||||
def test_create_with_foo():
|
||||
x = create_with_foo() # $ tracked=foo
|
||||
print(x.foo) # $ tracked=foo tracked
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Attributes assigned statically to a class
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
class MyClass: # $tracked=field
|
||||
field = tracked # $tracked
|
||||
@@ -40,7 +60,9 @@ lookup = MyClass.field # $tracked tracked=field
|
||||
instance = MyClass() # $tracked=field
|
||||
lookup2 = instance.field # MISSING: tracked
|
||||
|
||||
## Dynamic attribute access
|
||||
# ------------------------------------------------------------------------------
|
||||
# Dynamic attribute access
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Via `getattr`/`setattr`
|
||||
|
||||
@@ -99,3 +121,41 @@ def dunder_dict_indirect_read():
|
||||
do_stuff(y) # $ MISSING: tracked
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tracking of attribute on class instance
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# attribute set in method
|
||||
# inspired by https://github.com/github/codeql/pull/6023
|
||||
|
||||
class MyClass2(object):
|
||||
def __init__(self): # $ tracked=foo
|
||||
self.foo = tracked # $ tracked=foo tracked
|
||||
|
||||
def print_foo(self): # $ MISSING: tracked=foo
|
||||
print(self.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
def possibly_uncalled_method(self): # $ MISSING: tracked=foo
|
||||
print(self.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
instance = MyClass2()
|
||||
print(instance.foo) # $ MISSING: tracked=foo tracked
|
||||
instance.print_foo() # $ MISSING: tracked=foo
|
||||
|
||||
|
||||
# attribute set from outside of class
|
||||
|
||||
class MyClass3(object):
|
||||
def print_self(self): # $ tracked=foo
|
||||
print(self) # $ tracked=foo
|
||||
|
||||
def print_foo(self): # $ tracked=foo
|
||||
print(self.foo) # $ tracked=foo tracked
|
||||
|
||||
def possibly_uncalled_method(self): # $ MISSING: tracked=foo
|
||||
print(self.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
instance = MyClass3() # $ tracked=foo
|
||||
instance.print_self() # $ tracked=foo
|
||||
instance.foo = tracked # $ tracked=foo tracked
|
||||
instance.print_foo() # $ tracked=foo
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -2,19 +2,7 @@ import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.Concepts
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
string value_from_expr(Expr e) {
|
||||
// TODO: This one is starting to look like `repr` predicate from TestTaintLib
|
||||
result =
|
||||
e.(StrConst).getPrefix() + e.(StrConst).getText() +
|
||||
e.(StrConst).getPrefix().regexpReplaceAll("[a-zA-Z]+", "")
|
||||
or
|
||||
result = e.(Name).getId()
|
||||
or
|
||||
not e instanceof StrConst and
|
||||
not e instanceof Name and
|
||||
result = e.toString()
|
||||
}
|
||||
import experimental.dataflow.TestUtil.PrintNode
|
||||
|
||||
class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" }
|
||||
@@ -27,7 +15,7 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
command = sce.getCommand() and
|
||||
location = command.getLocation() and
|
||||
element = command.toString() and
|
||||
value = value_from_expr(command.asExpr()) and
|
||||
value = prettyNodeForInlineTest(command) and
|
||||
tag = "getCommand"
|
||||
)
|
||||
}
|
||||
@@ -46,7 +34,7 @@ class DecodingTest extends InlineExpectationsTest {
|
||||
exists(DataFlow::Node data |
|
||||
location = data.getLocation() and
|
||||
element = data.toString() and
|
||||
value = value_from_expr(data.asExpr()) and
|
||||
value = prettyNodeForInlineTest(data) and
|
||||
(
|
||||
data = d.getAnInput() and
|
||||
tag = "decodeInput"
|
||||
@@ -84,7 +72,7 @@ class EncodingTest extends InlineExpectationsTest {
|
||||
exists(DataFlow::Node data |
|
||||
location = data.getLocation() and
|
||||
element = data.toString() and
|
||||
value = value_from_expr(data.asExpr()) and
|
||||
value = prettyNodeForInlineTest(data) and
|
||||
(
|
||||
data = e.getAnInput() and
|
||||
tag = "encodeInput"
|
||||
@@ -117,7 +105,7 @@ class CodeExecutionTest extends InlineExpectationsTest {
|
||||
code = ce.getCode() and
|
||||
location = code.getLocation() and
|
||||
element = code.toString() and
|
||||
value = value_from_expr(code.asExpr()) and
|
||||
value = prettyNodeForInlineTest(code) and
|
||||
tag = "getCode"
|
||||
)
|
||||
}
|
||||
@@ -135,7 +123,7 @@ class SqlExecutionTest extends InlineExpectationsTest {
|
||||
sql = e.getSql() and
|
||||
location = e.getLocation() and
|
||||
element = sql.toString() and
|
||||
value = value_from_expr(sql.asExpr()) and
|
||||
value = prettyNodeForInlineTest(sql) and
|
||||
tag = "getSql"
|
||||
)
|
||||
}
|
||||
@@ -218,7 +206,7 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest {
|
||||
exists(HTTP::Server::HttpResponse response |
|
||||
location = response.getLocation() and
|
||||
element = response.toString() and
|
||||
value = value_from_expr(response.getBody().asExpr()) and
|
||||
value = prettyNodeForInlineTest(response.getBody()) and
|
||||
tag = "responseBody"
|
||||
)
|
||||
or
|
||||
@@ -257,7 +245,7 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest {
|
||||
exists(HTTP::Server::HttpRedirectResponse redirect |
|
||||
location = redirect.getLocation() and
|
||||
element = redirect.toString() and
|
||||
value = value_from_expr(redirect.getRedirectLocation().asExpr()) and
|
||||
value = prettyNodeForInlineTest(redirect.getRedirectLocation()) and
|
||||
tag = "redirectLocation"
|
||||
)
|
||||
)
|
||||
@@ -275,7 +263,7 @@ class FileSystemAccessTest extends InlineExpectationsTest {
|
||||
path = a.getAPathArgument() and
|
||||
location = a.getLocation() and
|
||||
element = path.toString() and
|
||||
value = value_from_expr(path.asExpr()) and
|
||||
value = prettyNodeForInlineTest(path) and
|
||||
tag = "getAPathArgument"
|
||||
)
|
||||
}
|
||||
@@ -309,7 +297,7 @@ class SafeAccessCheckTest extends InlineExpectationsTest {
|
||||
location = c.getLocation() and
|
||||
(
|
||||
element = checks.toString() and
|
||||
value = value_from_expr(checks.asExpr()) and
|
||||
value = prettyNodeForInlineTest(checks) and
|
||||
tag = "checks"
|
||||
or
|
||||
element = branch.toString() and
|
||||
@@ -341,3 +329,33 @@ class PublicKeyGenerationTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CryptographicOperationTest extends InlineExpectationsTest {
|
||||
CryptographicOperationTest() { this = "CryptographicOperationTest" }
|
||||
|
||||
override string getARelevantTag() {
|
||||
result in [
|
||||
"CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm"
|
||||
]
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(Cryptography::CryptographicOperation cryptoOperation |
|
||||
location = cryptoOperation.getLocation() and
|
||||
(
|
||||
element = cryptoOperation.toString() and
|
||||
value = "" and
|
||||
tag = "CryptographicOperation"
|
||||
or
|
||||
element = cryptoOperation.toString() and
|
||||
value = prettyNodeForInlineTest(cryptoOperation.getAnInput()) and
|
||||
tag = "CryptographicOperationInput"
|
||||
or
|
||||
element = cryptoOperation.toString() and
|
||||
value = cryptoOperation.getAlgorithm().getName() and
|
||||
tag = "CryptographicOperationAlgorithm"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
94
python/ql/test/experimental/meta/InlineTaintTest.qll
Normal file
94
python/ql/test/experimental/meta/InlineTaintTest.qll
Normal 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 = prettyExpr(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 = prettyExpr(sink.asExpr()) and
|
||||
not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and
|
||||
location = sink.getLocation() and
|
||||
not exists(FalseNegativeExpectation missingResult |
|
||||
missingResult.getLocation().getStartLine() = location.getStartLine()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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= |
|
||||
@@ -0,0 +1 @@
|
||||
import experimental.meta.InlineTaintTest
|
||||
@@ -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
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
| NamingConventionsClasses.py:2:1:2:14 | Class badName | Class names should start in uppercase. |
|
||||
@@ -0,0 +1,11 @@
|
||||
# BAD, do not start class or interface name with lowercase letter
|
||||
class badName:
|
||||
|
||||
def hello(self):
|
||||
print("hello")
|
||||
|
||||
# Good, class name starts with capital letter
|
||||
class GoodName:
|
||||
|
||||
def hello(self):
|
||||
print("hello")
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Classes/NamingConventionsClasses.ql
|
||||
@@ -0,0 +1 @@
|
||||
| NamingConventionsFunctions.py:4:5:4:25 | Function HelloWorld | Function names should start in lowercase. |
|
||||
@@ -0,0 +1,9 @@
|
||||
class Test:
|
||||
|
||||
# BAD, do not start function name with uppercase letter
|
||||
def HelloWorld(self):
|
||||
print("hello world")
|
||||
|
||||
# GOOD, function name starts with lowercase letter
|
||||
def hello_world(self):
|
||||
print("hello world")
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Functions/NamingConventionsFunctions.ql
|
||||
@@ -0,0 +1,98 @@
|
||||
edges
|
||||
| ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:13:17:13:28 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:14:21:14:27 | ControlFlowNode for request |
|
||||
| ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:14:21:14:32 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:13:17:13:28 | ControlFlowNode for Attribute | ldap3_bad.py:13:17:13:34 | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:13:17:13:34 | ControlFlowNode for Subscript | ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn |
|
||||
| ldap3_bad.py:14:21:14:27 | ControlFlowNode for request | ldap3_bad.py:14:21:14:32 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:14:21:14:32 | ControlFlowNode for Attribute | ldap3_bad.py:14:21:14:44 | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:14:21:14:44 | ControlFlowNode for Subscript | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter |
|
||||
| ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | ldap3_bad.py:30:17:30:28 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | ldap3_bad.py:31:21:31:27 | ControlFlowNode for request |
|
||||
| ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | ldap3_bad.py:31:21:31:32 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:30:17:30:28 | ControlFlowNode for Attribute | ldap3_bad.py:30:17:30:34 | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:30:17:30:34 | ControlFlowNode for Subscript | ldap3_bad.py:38:9:38:10 | ControlFlowNode for dn |
|
||||
| ldap3_bad.py:31:21:31:27 | ControlFlowNode for request | ldap3_bad.py:31:21:31:32 | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:31:21:31:32 | ControlFlowNode for Attribute | ldap3_bad.py:31:21:31:44 | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:31:21:31:44 | ControlFlowNode for Subscript | ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:13:17:13:23 | ControlFlowNode for request | ldap_bad.py:13:17:13:28 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:13:17:13:23 | ControlFlowNode for request | ldap_bad.py:14:21:14:27 | ControlFlowNode for request |
|
||||
| ldap_bad.py:13:17:13:23 | ControlFlowNode for request | ldap_bad.py:14:21:14:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:13:17:13:28 | ControlFlowNode for Attribute | ldap_bad.py:13:17:13:34 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:13:17:13:34 | ControlFlowNode for Subscript | ldap_bad.py:21:9:21:10 | ControlFlowNode for dn |
|
||||
| ldap_bad.py:14:21:14:27 | ControlFlowNode for request | ldap_bad.py:14:21:14:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:14:21:14:32 | ControlFlowNode for Attribute | ldap_bad.py:14:21:14:44 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:14:21:14:44 | ControlFlowNode for Subscript | ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:30:17:30:23 | ControlFlowNode for request | ldap_bad.py:30:17:30:28 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:30:17:30:23 | ControlFlowNode for request | ldap_bad.py:31:21:31:27 | ControlFlowNode for request |
|
||||
| ldap_bad.py:30:17:30:23 | ControlFlowNode for request | ldap_bad.py:31:21:31:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:30:17:30:28 | ControlFlowNode for Attribute | ldap_bad.py:30:17:30:34 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:30:17:30:34 | ControlFlowNode for Subscript | ldap_bad.py:37:9:37:10 | ControlFlowNode for dn |
|
||||
| ldap_bad.py:31:21:31:27 | ControlFlowNode for request | ldap_bad.py:31:21:31:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:31:21:31:32 | ControlFlowNode for Attribute | ldap_bad.py:31:21:31:44 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:31:21:31:44 | ControlFlowNode for Subscript | ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:47:17:47:23 | ControlFlowNode for request | ldap_bad.py:47:17:47:28 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:47:17:47:23 | ControlFlowNode for request | ldap_bad.py:48:21:48:27 | ControlFlowNode for request |
|
||||
| ldap_bad.py:47:17:47:23 | ControlFlowNode for request | ldap_bad.py:48:21:48:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:47:17:47:28 | ControlFlowNode for Attribute | ldap_bad.py:47:17:47:34 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:47:17:47:34 | ControlFlowNode for Subscript | ldap_bad.py:55:9:55:10 | ControlFlowNode for dn |
|
||||
| ldap_bad.py:48:21:48:27 | ControlFlowNode for request | ldap_bad.py:48:21:48:32 | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:48:21:48:32 | ControlFlowNode for Attribute | ldap_bad.py:48:21:48:44 | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:48:21:48:44 | ControlFlowNode for Subscript | ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter |
|
||||
nodes
|
||||
| ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap3_bad.py:13:17:13:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:13:17:13:34 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:14:21:14:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap3_bad.py:14:21:14:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:14:21:14:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
| ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap3_bad.py:30:17:30:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:30:17:30:34 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:31:21:31:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap3_bad.py:31:21:31:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap3_bad.py:31:21:31:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap3_bad.py:38:9:38:10 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:13:17:13:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:13:17:13:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:13:17:13:34 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:14:21:14:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:14:21:14:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:14:21:14:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:21:9:21:10 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:30:17:30:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:30:17:30:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:30:17:30:34 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:31:21:31:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:31:21:31:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:31:21:31:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:37:9:37:10 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
| ldap_bad.py:47:17:47:23 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:47:17:47:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:47:17:47:34 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:48:21:48:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| ldap_bad.py:48:21:48:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| ldap_bad.py:48:21:48:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| ldap_bad.py:55:9:55:10 | ControlFlowNode for dn | semmle.label | ControlFlowNode for dn |
|
||||
| ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | semmle.label | ControlFlowNode for search_filter |
|
||||
#select
|
||||
| ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap3_bad.py:21:17:21:18 | ControlFlowNode for dn | This | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | This | ldap3_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | ldap3_bad.py:14:21:14:27 | ControlFlowNode for request | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:21:21:21:33 | ControlFlowNode for search_filter | This | ldap3_bad.py:14:21:14:27 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:38:9:38:10 | ControlFlowNode for dn | ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | ldap3_bad.py:38:9:38:10 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap3_bad.py:38:9:38:10 | ControlFlowNode for dn | This | ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | This | ldap3_bad.py:30:17:30:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | ldap3_bad.py:31:21:31:27 | ControlFlowNode for request | ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap3_bad.py:38:13:38:25 | ControlFlowNode for search_filter | This | ldap3_bad.py:31:21:31:27 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:21:9:21:10 | ControlFlowNode for dn | ldap_bad.py:13:17:13:23 | ControlFlowNode for request | ldap_bad.py:21:9:21:10 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:21:9:21:10 | ControlFlowNode for dn | This | ldap_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | ldap_bad.py:13:17:13:23 | ControlFlowNode for request | ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | This | ldap_bad.py:13:17:13:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | ldap_bad.py:14:21:14:27 | ControlFlowNode for request | ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:21:33:21:45 | ControlFlowNode for search_filter | This | ldap_bad.py:14:21:14:27 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:37:9:37:10 | ControlFlowNode for dn | ldap_bad.py:30:17:30:23 | ControlFlowNode for request | ldap_bad.py:37:9:37:10 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:37:9:37:10 | ControlFlowNode for dn | This | ldap_bad.py:30:17:30:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | ldap_bad.py:30:17:30:23 | ControlFlowNode for request | ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | This | ldap_bad.py:30:17:30:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | ldap_bad.py:31:21:31:27 | ControlFlowNode for request | ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:37:33:37:45 | ControlFlowNode for search_filter | This | ldap_bad.py:31:21:31:27 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:55:9:55:10 | ControlFlowNode for dn | ldap_bad.py:47:17:47:23 | ControlFlowNode for request | ldap_bad.py:55:9:55:10 | ControlFlowNode for dn | $@ LDAP query parameter comes from $@. | ldap_bad.py:55:9:55:10 | ControlFlowNode for dn | This | ldap_bad.py:47:17:47:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | ldap_bad.py:47:17:47:23 | ControlFlowNode for request | ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | This | ldap_bad.py:47:17:47:23 | ControlFlowNode for request | a user-provided value |
|
||||
| ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | ldap_bad.py:48:21:48:27 | ControlFlowNode for request | ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | $@ LDAP query parameter comes from $@. | ldap_bad.py:55:43:55:55 | ControlFlowNode for search_filter | This | ldap_bad.py:48:21:48:27 | ControlFlowNode for request | a user-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-090/LDAPInjection.ql
|
||||
@@ -0,0 +1,41 @@
|
||||
from flask import request, Flask
|
||||
import ldap3
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/normal")
|
||||
def normal():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as DN and search filter
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
dn = "dc={}".format(unsafe_dc)
|
||||
search_filter = "(user={})".format(unsafe_filter)
|
||||
|
||||
srv = ldap3.Server('ldap://127.0.0.1')
|
||||
conn = ldap3.Connection(srv, user=dn, auto_bind=True)
|
||||
conn.search(dn, search_filter)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as DN and search filter using a oneline call to .search
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
dn = "dc={}".format(unsafe_dc)
|
||||
search_filter = "(user={})".format(unsafe_filter)
|
||||
|
||||
srv = ldap3.Server('ldap://127.0.0.1')
|
||||
conn = ldap3.Connection(srv, user=dn, auto_bind=True).search(
|
||||
dn, search_filter)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,49 @@
|
||||
from flask import request, Flask
|
||||
import ldap3
|
||||
from ldap3.utils.dn import escape_rdn
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/normal")
|
||||
def normal():
|
||||
"""
|
||||
A RemoteFlowSource is sanitized and used as DN and search filter
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
safe_dc = escape_rdn(unsafe_dc)
|
||||
safe_filter = escape_filter_chars(unsafe_filter)
|
||||
|
||||
dn = "dc={}".format(safe_dc)
|
||||
search_filter = "(user={})".format(safe_filter)
|
||||
|
||||
srv = ldap3.Server('ldap://127.0.0.1')
|
||||
conn = ldap3.Connection(srv, user=dn, auto_bind=True)
|
||||
conn.search(dn, search_filter)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is sanitized and used as DN and search filter using a oneline call to .search
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
safe_dc = escape_rdn(unsafe_dc)
|
||||
safe_filter = escape_filter_chars(unsafe_filter)
|
||||
|
||||
dn = "dc={}".format(safe_dc)
|
||||
search_filter = "(user={})".format(safe_filter)
|
||||
|
||||
srv = ldap3.Server('ldap://127.0.0.1')
|
||||
conn = ldap3.Connection(srv, user=dn, auto_bind=True).search(
|
||||
dn, search_filter)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,59 @@
|
||||
from flask import request, Flask
|
||||
import ldap
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/normal")
|
||||
def normal():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as DN and search filter
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
dn = "dc={}".format(unsafe_dc)
|
||||
search_filter = "(user={})".format(unsafe_filter)
|
||||
|
||||
ldap_connection = ldap.initialize("ldap://127.0.0.1")
|
||||
user = ldap_connection.search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, search_filter)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as DN and search filter using a oneline call to .search_s
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
dn = "dc={}".format(unsafe_dc)
|
||||
search_filter = "(user={})".format(unsafe_filter)
|
||||
|
||||
user = ldap.initialize("ldap://127.0.0.1").search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, search_filter)
|
||||
|
||||
|
||||
@app.route("/normal_argbyname")
|
||||
def normal_argbyname():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as DN and search filter, while the search filter is specified as
|
||||
an argument by name
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
dn = "dc={}".format(unsafe_dc)
|
||||
search_filter = "(user={})".format(unsafe_filter)
|
||||
|
||||
ldap_connection = ldap.initialize("ldap://127.0.0.1")
|
||||
user = ldap_connection.search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, filterstr=search_filter)
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,70 @@
|
||||
from flask import request, Flask
|
||||
import ldap
|
||||
import ldap.filter
|
||||
import ldap.dn
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/normal")
|
||||
def normal():
|
||||
"""
|
||||
A RemoteFlowSource is sanitized and used as DN and search filter
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
safe_dc = ldap.dn.escape_dn_chars(unsafe_dc)
|
||||
safe_filter = ldap.filter.escape_filter_chars(unsafe_filter)
|
||||
|
||||
dn = "dc={}".format(safe_dc)
|
||||
search_filter = "(user={})".format(safe_filter)
|
||||
|
||||
ldap_connection = ldap.initialize("ldap://127.0.0.1")
|
||||
user = ldap_connection.search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, search_filter)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is sanitized and used as DN and search filter using a oneline call to .search_s
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
safe_dc = ldap.dn.escape_dn_chars(unsafe_dc)
|
||||
safe_filter = ldap.filter.escape_filter_chars(unsafe_filter)
|
||||
|
||||
dn = "dc={}".format(safe_dc)
|
||||
search_filter = "(user={})".format(safe_filter)
|
||||
|
||||
user = ldap.initialize("ldap://127.0.0.1").search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, search_filter, ["testAttr1", "testAttr2"])
|
||||
|
||||
|
||||
@app.route("/normal_argbyname")
|
||||
def normal_argbyname():
|
||||
"""
|
||||
A RemoteFlowSource is sanitized and used as DN and search filter, while the search filter is specified as
|
||||
an argument by name
|
||||
"""
|
||||
|
||||
unsafe_dc = request.args['dc']
|
||||
unsafe_filter = request.args['username']
|
||||
|
||||
safe_dc = ldap.dn.escape_dn_chars(unsafe_dc)
|
||||
safe_filter = ldap.filter.escape_filter_chars(unsafe_filter)
|
||||
|
||||
dn = "dc={}".format(safe_dc)
|
||||
search_filter = "(user={})".format(safe_filter)
|
||||
|
||||
ldap_connection = ldap.initialize("ldap://127.0.0.1")
|
||||
user = ldap_connection.search_s(
|
||||
dn, ldap.SCOPE_SUBTREE, filterstr=search_filter)
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,27 @@
|
||||
edges
|
||||
| re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:13:22:13:33 | ControlFlowNode for Attribute |
|
||||
| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | re_bad.py:13:22:13:44 | ControlFlowNode for Subscript |
|
||||
| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern |
|
||||
| re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:24:22:24:33 | ControlFlowNode for Attribute |
|
||||
| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | re_bad.py:24:22:24:44 | ControlFlowNode for Subscript |
|
||||
| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern |
|
||||
| re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:36:22:36:33 | ControlFlowNode for Attribute |
|
||||
| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | re_bad.py:36:22:36:44 | ControlFlowNode for Subscript |
|
||||
| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern |
|
||||
nodes
|
||||
| re_bad.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| re_bad.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| re_bad.py:13:22:13:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern |
|
||||
| re_bad.py:24:22:24:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| re_bad.py:24:22:24:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| re_bad.py:24:22:24:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern |
|
||||
| re_bad.py:36:22:36:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| re_bad.py:36:22:36:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| re_bad.py:36:22:36:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | semmle.label | ControlFlowNode for unsafe_pattern |
|
||||
#select
|
||||
| re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | re_bad.py:13:22:13:28 | ControlFlowNode for request | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:14:15:14:28 | ControlFlowNode for unsafe_pattern | This | re_bad.py:13:22:13:28 | ControlFlowNode for request | user-provided value | re_bad.py:14:5:14:13 | Attribute | re.search |
|
||||
| re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | re_bad.py:24:22:24:28 | ControlFlowNode for request | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:25:35:25:48 | ControlFlowNode for unsafe_pattern | This | re_bad.py:24:22:24:28 | ControlFlowNode for request | user-provided value | re_bad.py:26:5:26:27 | Attribute | re.search |
|
||||
| re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | re_bad.py:36:22:36:28 | ControlFlowNode for request | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | $@ regular expression is constructed from a $@ and executed by $@. | re_bad.py:37:16:37:29 | ControlFlowNode for unsafe_pattern | This | re_bad.py:36:22:36:28 | ControlFlowNode for request | user-provided value | re_bad.py:37:5:37:37 | Attribute | re.search |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-730/RegexInjection.ql
|
||||
@@ -0,0 +1,40 @@
|
||||
from flask import request, Flask
|
||||
import re
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as re.search's pattern
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args["pattern"]
|
||||
re.search(unsafe_pattern, "")
|
||||
|
||||
|
||||
@app.route("/compile")
|
||||
def compile():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as re.compile's pattern
|
||||
which also executes .search()
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args["pattern"]
|
||||
compiled_pattern = re.compile(unsafe_pattern)
|
||||
compiled_pattern.search("")
|
||||
|
||||
|
||||
@app.route("/compile_direct")
|
||||
def compile_direct():
|
||||
"""
|
||||
A RemoteFlowSource is used directly as re.compile's pattern
|
||||
which also executes .search() in the same line
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args["pattern"]
|
||||
re.compile(unsafe_pattern).search("")
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
@@ -0,0 +1,45 @@
|
||||
from flask import request, Flask
|
||||
import re
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/direct")
|
||||
def direct():
|
||||
"""
|
||||
A RemoteFlowSource is escaped by re.escape and then used as
|
||||
re'search pattern
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args['pattern']
|
||||
safe_pattern = re.escape(unsafe_pattern)
|
||||
re.search(safe_pattern, "")
|
||||
|
||||
|
||||
@app.route("/compile")
|
||||
def compile():
|
||||
"""
|
||||
A RemoteFlowSource is escaped by re.escape and used as re.compile's
|
||||
pattern which also executes .search()
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args['pattern']
|
||||
safe_pattern = re.escape(unsafe_pattern)
|
||||
compiled_pattern = re.compile(safe_pattern)
|
||||
compiled_pattern.search("")
|
||||
|
||||
|
||||
@app.route("/compile_direct")
|
||||
def compile_direct():
|
||||
"""
|
||||
A RemoteFlowSource is escaped by re.escape and then used as re.compile's
|
||||
pattern which also executes .search() in the same line
|
||||
"""
|
||||
|
||||
unsafe_pattern = request.args['pattern']
|
||||
safe_pattern = re.escape(unsafe_pattern)
|
||||
re.compile(safe_pattern).search("")
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# app.run(debug=True)
|
||||
Reference in New Issue
Block a user