Python: Make Sanitizer available for urlsplit taint

It isn't used by default, it has to *actively* be enabled.
This commit is contained in:
Rasmus Wriedt Larsen
2020-02-21 15:18:53 +01:00
parent fd270cc02c
commit 31ff652cb3
4 changed files with 83 additions and 4 deletions

View File

@@ -195,3 +195,62 @@ class ExternalFileObject extends TaintKind {
name = "read" and result = this.getValue()
}
}
/**
* Temporary sanitizer for the tainted result from `urlsplit` and `urlparse`. Can be used to reduce FPs until
* we have better support for namedtuples.
*
* Will clear **all** taint on a test of the kind. That is, on the true edge of any matching test,
* all fields/indexes will be cleared of taint.
*
* Handles:
* - `if splitres.netloc == "KNOWN_VALUE"`
* - `if splitres[0] == "KNOWN_VALUE"`
*/
class UrlsplitUrlparseTempSanitizer extends Sanitizer {
// TODO: remove this once we have better support for named tuples
UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" }
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
(
taint instanceof ExternalUrlSplitResult
or
taint instanceof ExternalUrlParseResult
) and
exists(ControlFlowNode foobar |
foobar.(SubscriptNode).getObject() = test.getInput().getAUse()
or
foobar.(AttrNode).getObject() = test.getInput().getAUse()
|
clears_taint(_, foobar, test.getTest(), test.getSense())
)
}
private predicate clears_taint(ControlFlowNode final_test, ControlFlowNode tainted, ControlFlowNode test, boolean sense) {
test_equality_with_const(final_test, tainted, sense)
or
test.(UnaryExprNode).getNode().getOp() instanceof Not and
exists(ControlFlowNode nested_test |
nested_test = test.(UnaryExprNode).getOperand() and
clears_taint(final_test, tainted, nested_test, sense.booleanNot())
)
}
/** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */
private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode operand, boolean sense) {
exists(ControlFlowNode const, Cmpop op |
const.getNode() instanceof StrConst
|
(
cmp.operands(const, op, operand)
or
cmp.operands(operand, op, const)
) and (
op instanceof Eq and sense = true
or
op instanceof NotEq and sense = false
)
)
}
}

View File

@@ -25,3 +25,23 @@ class DictSource extends TaintSource {
override string toString() { result = "dict taint source" }
}
class TestConfig extends TaintTracking::Configuration {
TestConfig() { this = "TestConfig" }
override predicate isSanitizer(Sanitizer sanitizer) {
sanitizer instanceof UrlsplitUrlparseTempSanitizer
}
override predicate isSource(TaintTracking::Source source) {
source instanceof SimpleSource
or
source instanceof ListSource
or
source instanceof DictSource
}
override predicate isSink(TaintTracking::Sink sink) {
none()
}
}

View File

@@ -3,8 +3,8 @@
| test.py:13 | test_basic | c | externally controlled string |
| test.py:13 | test_basic | d | externally controlled string |
| test.py:13 | test_basic | urlsplit_res | [externally controlled string] |
| test.py:20 | test_sanitizer | Attribute | externally controlled string |
| test.py:23 | test_sanitizer | Subscript | externally controlled string |
| test.py:20 | test_sanitizer | Attribute | NO TAINT |
| test.py:23 | test_sanitizer | Subscript | NO TAINT |
| test.py:33 | test_namedtuple | a | NO TAINT |
| test.py:33 | test_namedtuple | b | NO TAINT |
| test.py:33 | test_namedtuple | c | NO TAINT |

View File

@@ -17,10 +17,10 @@ def test_sanitizer():
urlsplit_res = urlsplit(tainted_string)
if urlsplit_res.netloc == "OK":
test(urlsplit_res.netloc) # TODO: FP, should not be tainted here
test(urlsplit_res.netloc)
if urlsplit_res[2] == "OK":
test(urlsplit_res[0]) # TODO: FP, should not be tainted here
test(urlsplit_res[0])
def test_namedtuple():
tainted_string = TAINTED_STRING