mirror of
https://github.com/github/codeql.git
synced 2025-12-20 18:56:32 +01:00
Python: Add example for how to write your own sanitizer
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:13 | Pi(s_0) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:18 | Pi(s_5) [false] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:28 | Pi(s_0) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:34 | Pi(s_10) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:40 | Pi(s_12) [false] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:50 | Pi(s_0) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:56 | Pi(s_10) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:62 | Pi(s_12) [false] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:76 | Pi(s_3) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:82 | Pi(s_0) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:87 | Pi(s_5) [false] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:97 | Pi(s_0) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:102 | Pi(s_7) [true] |
|
||||||
|
| MySanitizerHandlingNot | externally controlled string | test.py:107 | Pi(s_12) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:13 | Pi(s_0) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:28 | Pi(s_0) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:34 | Pi(s_10) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:50 | Pi(s_0) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:56 | Pi(s_10) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:76 | Pi(s_3) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:97 | Pi(s_0) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:102 | Pi(s_7) [true] |
|
||||||
|
| MySimpleSanitizer | externally controlled string | test.py:107 | Pi(s_12) [true] |
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import python
|
||||||
|
import Taint
|
||||||
|
|
||||||
|
from Sanitizer s, TaintKind taint, PyEdgeRefinement test
|
||||||
|
where s.sanitizingEdge(taint, test)
|
||||||
|
select s, taint, test.getTest().getLocation().toString(), test.getRepresentation()
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import python
|
||||||
|
import semmle.python.security.TaintTracking
|
||||||
|
import semmle.python.security.strings.Untrusted
|
||||||
|
|
||||||
|
class SimpleSource extends TaintSource {
|
||||||
|
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
|
||||||
|
|
||||||
|
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||||
|
|
||||||
|
override string toString() { result = "taint source" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySimpleSanitizer extends Sanitizer {
|
||||||
|
MySimpleSanitizer() { this = "MySimpleSanitizer" }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The test `if is_safe(arg):` sanitizes `arg` on its `true` edge.
|
||||||
|
*
|
||||||
|
* Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||||
|
taint instanceof ExternalStringKind and
|
||||||
|
exists(CallNode call |
|
||||||
|
test.getTest() = call and test.getSense() = true
|
||||||
|
|
|
||||||
|
call = Value::named("test.is_safe").getACall() and
|
||||||
|
test.getInput().getAUse() = call.getAnArg()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySanitizerHandlingNot extends Sanitizer {
|
||||||
|
MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" }
|
||||||
|
|
||||||
|
/** The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */
|
||||||
|
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||||
|
taint instanceof ExternalStringKind and
|
||||||
|
exists(CallNode call |
|
||||||
|
clears_taint_on_true(call, test.getTest(), test.getSense())
|
||||||
|
|
|
||||||
|
call = Value::named("test.is_safe").getACall() and
|
||||||
|
test.getInput().getAUse() = call.getAnArg()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate clears_taint_on_true(ControlFlowNode final_test, ControlFlowNode test, boolean sense) {
|
||||||
|
final_test = test and
|
||||||
|
sense = true
|
||||||
|
or
|
||||||
|
test.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||||
|
exists(ControlFlowNode nested_test |
|
||||||
|
nested_test = test.(UnaryExprNode).getOperand() and
|
||||||
|
clears_taint_on_true(final_test, nested_test, sense.booleanNot())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestConfig extends TaintTracking::Configuration {
|
||||||
|
TestConfig() { this = "TestConfig" }
|
||||||
|
|
||||||
|
override predicate isSanitizer(Sanitizer sanitizer) {
|
||||||
|
sanitizer instanceof MySanitizerHandlingNot
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSource(TaintTracking::Source source) {
|
||||||
|
source instanceof SimpleSource
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(TaintTracking::Sink sink) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
| test.py:14 | test_basic | s | <NO TAINT> | ok |
|
||||||
|
| test.py:16 | test_basic | s | externally controlled string | ok |
|
||||||
|
| test.py:19 | test_basic | s | externally controlled string | ok |
|
||||||
|
| test.py:21 | test_basic | s | <NO TAINT> | ok |
|
||||||
|
| test.py:29 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:31 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:35 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:37 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:41 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:43 | test_or | s | externally controlled string | ok |
|
||||||
|
| test.py:51 | test_and | s | <NO TAINT> | ok |
|
||||||
|
| test.py:53 | test_and | s | externally controlled string | ok |
|
||||||
|
| test.py:57 | test_and | s | externally controlled string | ok |
|
||||||
|
| test.py:59 | test_and | s | <NO TAINT> | ok |
|
||||||
|
| test.py:63 | test_and | s | externally controlled string | ok |
|
||||||
|
| test.py:65 | test_and | s | <NO TAINT> | ok |
|
||||||
|
| test.py:73 | test_tricky | s | externally controlled string | failure |
|
||||||
|
| test.py:77 | test_tricky | s_ | externally controlled string | failure |
|
||||||
|
| test.py:83 | test_nesting_not | s | <NO TAINT> | ok |
|
||||||
|
| test.py:85 | test_nesting_not | s | externally controlled string | ok |
|
||||||
|
| test.py:88 | test_nesting_not | s | externally controlled string | ok |
|
||||||
|
| test.py:90 | test_nesting_not | s | <NO TAINT> | ok |
|
||||||
|
| test.py:98 | test_nesting_not_with_and_true | s | externally controlled string | ok |
|
||||||
|
| test.py:100 | test_nesting_not_with_and_true | s | <NO TAINT> | ok |
|
||||||
|
| test.py:103 | test_nesting_not_with_and_true | s | <NO TAINT> | ok |
|
||||||
|
| test.py:105 | test_nesting_not_with_and_true | s | externally controlled string | ok |
|
||||||
|
| test.py:108 | test_nesting_not_with_and_true | s | externally controlled string | ok |
|
||||||
|
| test.py:110 | test_nesting_not_with_and_true | s | <NO TAINT> | ok |
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import python
|
||||||
|
import semmle.python.security.TaintTracking
|
||||||
|
import Taint
|
||||||
|
|
||||||
|
from Call call, Expr arg,
|
||||||
|
boolean expected_taint, boolean has_taint, string test_res,
|
||||||
|
string taint_string
|
||||||
|
where
|
||||||
|
call.getLocation().getFile().getShortName() = "test.py" and
|
||||||
|
(
|
||||||
|
call.getFunc().(Name).getId() = "ensure_tainted" and
|
||||||
|
expected_taint = true
|
||||||
|
or
|
||||||
|
call.getFunc().(Name).getId() = "ensure_not_tainted" and
|
||||||
|
expected_taint = false
|
||||||
|
) and
|
||||||
|
arg = call.getAnArg() and
|
||||||
|
(
|
||||||
|
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||||
|
taint_string = "<NO TAINT>" and
|
||||||
|
has_taint = false
|
||||||
|
or
|
||||||
|
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||||
|
taint_string = tainted.getTaintKind().toString()
|
||||||
|
) and
|
||||||
|
has_taint = true
|
||||||
|
) and
|
||||||
|
if expected_taint = has_taint then test_res = "ok" else test_res = "failure"
|
||||||
|
// if expected_taint = has_taint then test_res = "✓" else test_res = "✕"
|
||||||
|
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(),
|
||||||
|
taint_string, test_res
|
||||||
110
python/ql/test/library-tests/examples/custom-sanitizer/test.py
Normal file
110
python/ql/test/library-tests/examples/custom-sanitizer/test.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
def random_choice():
|
||||||
|
return bool(GLOBAL_UNKOWN_VAR)
|
||||||
|
|
||||||
|
def is_safe(arg):
|
||||||
|
return UNKNOWN_FUNC(arg)
|
||||||
|
|
||||||
|
def true_func():
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_basic():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
if is_safe(s):
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_tainted(s)
|
||||||
|
|
||||||
|
if not is_safe(s):
|
||||||
|
ensure_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
|
||||||
|
|
||||||
|
def test_or():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
# x or y
|
||||||
|
if is_safe(s) or random_choice():
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
else:
|
||||||
|
ensure_tainted(s) # must be tainted
|
||||||
|
|
||||||
|
# not (x or y)
|
||||||
|
if not(is_safe(s) or random_choice()):
|
||||||
|
ensure_tainted(s) # must be tainted
|
||||||
|
else:
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
|
||||||
|
# not (x or y) == not x and not y [de Morgan's laws]
|
||||||
|
if not is_safe(s) and not random_choice():
|
||||||
|
ensure_tainted(s) # must be tainted
|
||||||
|
else:
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
|
||||||
|
|
||||||
|
def test_and():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
# x and y
|
||||||
|
if is_safe(s) and random_choice():
|
||||||
|
ensure_not_tainted(s) # must not be tainted
|
||||||
|
else:
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
|
||||||
|
# not (x and y)
|
||||||
|
if not(is_safe(s) and random_choice()):
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
|
||||||
|
# not (x and y) == not x or not y [de Morgan's laws]
|
||||||
|
if not is_safe(s) or not random_choice():
|
||||||
|
ensure_tainted(s) # might be tainted
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tricky():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
x = is_safe(s)
|
||||||
|
if x:
|
||||||
|
ensure_not_tainted(s) # FP
|
||||||
|
|
||||||
|
s_ = s
|
||||||
|
if is_safe(s):
|
||||||
|
ensure_not_tainted(s_) # FP
|
||||||
|
|
||||||
|
def test_nesting_not():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
if not(not(is_safe(s))):
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_tainted(s)
|
||||||
|
|
||||||
|
if not(not(not(is_safe(s)))):
|
||||||
|
ensure_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
|
||||||
|
# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in
|
||||||
|
# SanitizedEdges.expected and compare with `test_nesting_not` and `test_basic`
|
||||||
|
def test_nesting_not_with_and_true():
|
||||||
|
s = TAINTED_STRING
|
||||||
|
|
||||||
|
if not(is_safe(s) and True):
|
||||||
|
ensure_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
|
||||||
|
if not(not(is_safe(s) and True)):
|
||||||
|
ensure_not_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_tainted(s)
|
||||||
|
|
||||||
|
if not(not(not(is_safe(s) and True))):
|
||||||
|
ensure_tainted(s)
|
||||||
|
else:
|
||||||
|
ensure_not_tainted(s)
|
||||||
Reference in New Issue
Block a user