Skip to content

Commit 0b2f44b

Browse files
authored
Merge pull request #1052 from markshannon/python-taint-tracking-configuration
Python: Add taint-tracking configuration.
2 parents 7386ca9 + ab23a15 commit 0b2f44b

File tree

2 files changed

+171
-28
lines changed

2 files changed

+171
-28
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* For compatibility with other language implementations */
2+
3+
import semmle.python.security.TaintTracking

python/ql/src/semmle/python/security/TaintTracking.qll

Lines changed: 168 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,6 @@ abstract class TaintKind extends string {
125125
*/
126126
predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar) { none() }
127127

128-
/** Holds if this kind of taint can start from `expr`.
129-
* In other words, is `expr` a source of this kind of taint.
130-
*/
131-
final predicate startsFrom(ControlFlowNode expr) {
132-
expr.(TaintSource).isSourceOf(this, _)
133-
}
134-
135128
/** Holds if this kind of taint "taints" `expr`.
136129
*/
137130
final predicate taints(ControlFlowNode expr) {
@@ -318,6 +311,18 @@ abstract class Sanitizer extends string {
318311

319312
}
320313

314+
/** Hold if `sanitizer` is valid. A sanitizer is valid if there is
315+
* a `TaintTracking::Configuration` that declares `sanitizer` or
316+
* there are no `TaintTracking::Configuration`s.
317+
*/
318+
private predicate valid_sanitizer(Sanitizer sanitizer) {
319+
not exists(TaintTracking::Configuration c)
320+
or
321+
exists(DataFlow::Configuration c | c.isSanitizer(sanitizer))
322+
or
323+
exists(TaintTracking::Configuration c | c.isSanitizer(sanitizer))
324+
}
325+
321326
/** DEPRECATED -- Use DataFlowExtension instead.
322327
* An extension to taint-flow. For adding library or framework specific flows.
323328
* Examples include flow from a request to untrusted part of that request or
@@ -584,12 +589,19 @@ private newtype TTaintedNode =
584589
n.(TaintSource).isSourceOf(kind, context)
585590
)
586591
or
592+
exists(DataFlow::Configuration config, TaintKind kind |
593+
taint = TaintFlowImplementation::TTrackedTaint(kind) and
594+
config.isSource(n) and context.getDepth() = 0 and
595+
kind instanceof GenericFlowType
596+
)
597+
or
587598
TaintFlowImplementation::step(_, taint, context, n) and
588599
exists(TaintKind kind |
589600
kind = taint.(TaintFlowImplementation::TrackedTaint).getKind()
590601
or
591602
kind = taint.(TaintFlowImplementation::TrackedAttribute).getKind(_) |
592603
not exists(Sanitizer sanitizer |
604+
valid_sanitizer(sanitizer) and
593605
sanitizer.sanitizingNode(kind, n)
594606
)
595607
)
@@ -839,26 +851,37 @@ library module TaintFlowImplementation {
839851
or
840852
call_taint_step(fromnode, totaint, tocontext, tonode)
841853
or
842-
fromnode.getNode().(DataFlowNode).getASuccessorNode() = tonode and
843-
fromnode.getContext() = tocontext and
844-
totaint = fromnode.getTrackedValue()
845-
or
846-
exists(CallNode call |
847-
fromnode.getNode().(DataFlowNode).getAReturnSuccessorNode(call) = tonode and
848-
fromnode.getContext() = tocontext.getCallee(call) and
849-
totaint = fromnode.getTrackedValue()
850-
)
851-
or
852-
exists(CallNode call |
853-
fromnode.getNode().(DataFlowNode).getACalleeSuccessorNode(call) = tonode and
854-
fromnode.getContext().getCallee(call) = tocontext and
854+
exists(DataFlowNode fromnodenode |
855+
fromnodenode = fromnode.getNode() and
856+
(
857+
not exists(TaintTracking::Configuration c)
858+
or
859+
exists(DataFlow::Configuration c | c.isExtension(fromnodenode))
860+
or
861+
exists(TaintTracking::Configuration c | c.isExtension(fromnodenode))
862+
)
863+
|
864+
fromnodenode.getASuccessorNode() = tonode and
865+
fromnode.getContext() = tocontext and
855866
totaint = fromnode.getTrackedValue()
856-
)
857-
or
858-
exists(TaintKind tokind |
859-
fromnode.getNode().(DataFlowNode).getASuccessorNode(fromnode.getTaintKind(), tokind) = tonode and
860-
totaint = fromnode.getTrackedValue().toKind(tokind) and
861-
tocontext = fromnode.getContext()
867+
or
868+
exists(CallNode call |
869+
fromnodenode.getAReturnSuccessorNode(call) = tonode and
870+
fromnode.getContext() = tocontext.getCallee(call) and
871+
totaint = fromnode.getTrackedValue()
872+
)
873+
or
874+
exists(CallNode call |
875+
fromnodenode.getACalleeSuccessorNode(call) = tonode and
876+
fromnode.getContext().getCallee(call) = tocontext and
877+
totaint = fromnode.getTrackedValue()
878+
)
879+
or
880+
exists(TaintKind tokind |
881+
fromnodenode.getASuccessorNode(fromnode.getTaintKind(), tokind) = tonode and
882+
totaint = fromnode.getTrackedValue().toKind(tokind) and
883+
tocontext = fromnode.getContext()
884+
)
862885
)
863886
or
864887
exists(TaintKind tokind |
@@ -1038,8 +1061,18 @@ library module TaintFlowImplementation {
10381061
prev.(DataFlowVariable).getASuccessorVariable() = var
10391062
)
10401063
or
1041-
origin.getNode().(DataFlowNode).getASuccessorVariable() = var and
1042-
context = origin.getContext()
1064+
exists(DataFlowNode originnode |
1065+
originnode = origin.getNode() and
1066+
(
1067+
not exists(TaintTracking::Configuration c)
1068+
or
1069+
exists(DataFlow::Configuration c | c.isExtension(originnode))
1070+
or
1071+
exists(TaintTracking::Configuration c | c.isExtension(originnode))
1072+
) and
1073+
originnode.getASuccessorVariable() = var and
1074+
context = origin.getContext()
1075+
)
10431076
or
10441077
exists(TrackedTaint taint, EssaVariable prev |
10451078
tainted_var(prev, context, origin) and
@@ -1062,6 +1095,7 @@ library module TaintFlowImplementation {
10621095
exists(TaintKind kind |
10631096
kind = origin.getTaintKind() and
10641097
not exists(Sanitizer san |
1098+
valid_sanitizer(san) |
10651099
san.sanitizingDefinition(kind, def)
10661100
or
10671101
san.sanitizingNode(kind, def.(EssaNodeDefinition).getDefiningNode())
@@ -1184,6 +1218,7 @@ library module TaintFlowImplementation {
11841218
exists(TaintKind kind |
11851219
kind = origin.getTaintKind() |
11861220
not exists(FunctionObject callee, Sanitizer sanitizer |
1221+
valid_sanitizer(sanitizer) and
11871222
callee.getACall() = call.getCall() and
11881223
sanitizer.sanitizingCall(kind, callee)
11891224
)
@@ -1197,6 +1232,7 @@ library module TaintFlowImplementation {
11971232
var = test.getInput() and
11981233
tainted_var(var, context, origin) and
11991234
not exists(Sanitizer sanitizer |
1235+
valid_sanitizer(sanitizer) and
12001236
sanitizer.sanitizingEdge(kind, test)
12011237
)
12021238
|
@@ -1246,6 +1282,7 @@ library module TaintFlowImplementation {
12461282
var = uniphi.getInput() and
12471283
tainted_var(var, context, origin) and
12481284
not exists(Sanitizer sanitizer |
1285+
valid_sanitizer(sanitizer) and
12491286
sanitizer.sanitizingSingleEdge(kind, uniphi)
12501287
)
12511288
)
@@ -1438,6 +1475,109 @@ class CallContext extends TCallContext {
14381475

14391476
}
14401477

1478+
1479+
/** Data flow module providing an interface compatible with
1480+
* the other language implementations.
1481+
*/
1482+
module DataFlow {
1483+
1484+
class FlowType = TaintKind;
1485+
1486+
/** Generic taint kind, source and sink classes for convenience and
1487+
* compatibility with other language libraries
1488+
*/
1489+
1490+
class Node = ControlFlowNode;
1491+
1492+
class PathNode = TaintedNode;
1493+
1494+
class Extension = DataFlowExtension::DataFlowNode;
1495+
1496+
abstract class Configuration extends string {
1497+
1498+
bindingset[this]
1499+
Configuration() { this = this }
1500+
1501+
abstract predicate isSource(Node source);
1502+
1503+
abstract predicate isSink(Node sink);
1504+
1505+
predicate isSanitizer(Sanitizer sanitizer) { none() }
1506+
1507+
predicate isExtension(Extension extension) { none() }
1508+
1509+
predicate hasFlowPath(PathNode source, PathNode sink) {
1510+
this.isSource(source.getNode()) and
1511+
this.isSink(sink.getNode()) and
1512+
source.getTaintKind() instanceof GenericFlowType and
1513+
sink.getTaintKind() instanceof GenericFlowType
1514+
}
1515+
1516+
predicate hasFlow(Node source, Node sink) {
1517+
exists(PathNode psource, PathNode psink |
1518+
psource.getNode() = source and
1519+
psink.getNode() = sink and
1520+
this.isSource(source) and
1521+
this.isSink(sink) and
1522+
this.hasFlowPath(psource, psink)
1523+
)
1524+
}
1525+
1526+
}
1527+
1528+
}
1529+
1530+
private class GenericFlowType extends DataFlow::FlowType {
1531+
1532+
GenericFlowType() {
1533+
this = "Generic taint kind" and
1534+
exists(DataFlow::Configuration c)
1535+
}
1536+
1537+
}
1538+
1539+
module TaintTracking {
1540+
1541+
class Source = TaintSource;
1542+
1543+
class Sink = TaintSink;
1544+
1545+
class PathSource = TaintedPathSource;
1546+
1547+
class PathSink = TaintedPathSink;
1548+
1549+
class Extension = DataFlowExtension::DataFlowNode;
1550+
1551+
abstract class Configuration extends string {
1552+
1553+
bindingset[this]
1554+
Configuration() { this = this }
1555+
1556+
abstract predicate isSource(Source source);
1557+
1558+
abstract predicate isSink(Sink sink);
1559+
1560+
predicate isSanitizer(Sanitizer sanitizer) { none() }
1561+
1562+
predicate isExtension(Extension extension) { none() }
1563+
1564+
predicate hasFlowPath(PathSource source, PathSink sink) {
1565+
this.isSource(source.getNode()) and
1566+
this.isSink(sink.getNode()) and
1567+
source.flowsTo(sink)
1568+
}
1569+
1570+
predicate hasFlow(Source source, Sink sink) {
1571+
this.isSource(source) and
1572+
this.isSink(sink) and
1573+
source.flowsToSink(sink)
1574+
}
1575+
1576+
}
1577+
1578+
}
1579+
1580+
14411581
pragma [noinline]
14421582
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
14431583
dictnode.(DictNode).getAValue() = itemnode

0 commit comments

Comments
 (0)