Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c7ec569

Browse files
committed
Python taint-tracking: make sure all features of legacy extensions are supported.
1 parent 64c160b commit c7ec569

19 files changed

Lines changed: 390 additions & 299 deletions

python/ql/src/semmle/python/dataflow/Configuration.qll

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,24 @@ module TaintTracking {
3737
* Holds if `source` is a source of taint of `kind` that is relevant
3838
* for this configuration.
3939
*/
40-
predicate isSource(DataFlow::Node source, TaintKind kind) { none() }
40+
predicate isSource(DataFlow::Node node, TaintKind kind) {
41+
exists(TaintSource source |
42+
this.isSource(source) and
43+
node.asCfgNode() = source and
44+
source.isSourceOf(kind)
45+
)
46+
}
4147

4248
/**
4349
* Holds if `sink` is a sink of taint of `kind` that is relevant
4450
* for this configuration.
4551
*/
46-
predicate isSink(DataFlow::Node sink, TaintKind kind) { none() }
52+
predicate isSink(DataFlow::Node node, TaintKind kind) {
53+
exists(TaintSink sink |
54+
node.asCfgNode() = sink and
55+
sink.sinks(kind)
56+
)
57+
}
4758

4859
/**
4960
* Holds if `src -> dest` should be considered as a flow edge
@@ -60,12 +71,30 @@ module TaintTracking {
6071

6172
predicate isBarrier(DataFlow::Node node) { none() }
6273

63-
predicate isBarrier(DataFlow::Node node, TaintKind kind) { none() }
74+
predicate isBarrier(DataFlow::Node node, TaintKind kind) {
75+
exists(Sanitizer sanitizer |
76+
this.isSanitizer(sanitizer)
77+
|
78+
sanitizer.sanitizingNode(kind, node.asCfgNode())
79+
or
80+
sanitizer.sanitizingEdge(kind, node.asVariable())
81+
or
82+
sanitizer.sanitizingSingleEdge(kind, node.asVariable())
83+
or
84+
sanitizer.sanitizingDefinition(kind, node.asVariable())
85+
or
86+
exists(MethodCallsiteRefinement call, FunctionObject callee |
87+
call = node.asVariable().getDefinition() and
88+
callee.getACall() = call.getCall() and
89+
sanitizer.sanitizingCall(kind, callee)
90+
)
91+
)
92+
}
6493

6594
/**
6695
* Holds if flow from `src` to `dest` is prohibited.
6796
*/
68-
predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node trg) { none() }
97+
predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) { none() }
6998

7099
/**
71100
* Holds if control flow from `test` along the `isTrue` edge is prohibited.

python/ql/src/semmle/python/dataflow/Implementation.qll

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -238,29 +238,13 @@ class TaintTrackingImplementation extends string {
238238

239239
predicate flowSource(DataFlow::Node node, TaintTrackingContext context, AttributePath path, TaintKind kind) {
240240
context = TNoParam() and path = TNoAttribute() and
241-
(
242-
this.(TaintTracking::Configuration).isSource(node, kind)
243-
or
244-
exists(TaintSource source |
245-
this.(TaintTracking::Configuration).isSource(source) and
246-
node.asCfgNode() = source and
247-
source.isSourceOf(kind)
248-
)
249-
)
241+
this.(TaintTracking::Configuration).isSource(node, kind)
250242
}
251243

252244

253245
predicate flowSink(DataFlow::Node node, AttributePath path, TaintKind kind) {
254246
path = TNoAttribute() and
255-
(
256-
this.(TaintTracking::Configuration).isSink(node, kind)
257-
or
258-
exists(TaintSink sink |
259-
this.(TaintTracking::Configuration).isSink(sink) and
260-
node.asCfgNode() = sink and
261-
sink.sinks(kind)
262-
)
263-
)
247+
this.(TaintTracking::Configuration).isSink(node, kind)
264248
}
265249

266250
predicate isPathSource(TaintTrackingNode source) {
@@ -293,28 +277,6 @@ class TaintTrackingImplementation extends string {
293277
)
294278
}
295279

296-
predicate flowBarrier(DataFlow::Node node, TaintKind kind) {
297-
this.(TaintTracking::Configuration).isBarrier(node, kind)
298-
or
299-
exists(Sanitizer sanitizer |
300-
this.(TaintTracking::Configuration).isSanitizer(sanitizer)
301-
|
302-
sanitizer.sanitizingNode(kind, node.asCfgNode())
303-
or
304-
sanitizer.sanitizingDefinition(kind, node.asVariable().getDefinition())
305-
or
306-
exists(MethodCallsiteRefinement call, FunctionObject callee |
307-
call = node.asVariable().getDefinition() and
308-
callee.getACall() = call.getCall() and
309-
sanitizer.sanitizingCall(kind, callee)
310-
)
311-
or
312-
sanitizer.sanitizingEdge(kind, node.asVariable().getDefinition())
313-
or
314-
sanitizer.sanitizingSingleEdge(kind, node.asVariable().getDefinition())
315-
)
316-
}
317-
318280
/** Gets the boolean value that `test` evaluates to when `use` is tainted with `kind`
319281
* and `test` and `use` are part of a test in a branch.
320282
*/
@@ -334,9 +296,14 @@ class TaintTrackingImplementation extends string {
334296
Filters::isinstance(test, c, use) and
335297
c.pointsTo(cls)
336298
|
337-
kind.getType().getASuperType() = cls and result = true
299+
exists(ClassValue scls |
300+
scls = kind.getType() |
301+
scls.getASuperType() = cls and result = true
302+
or
303+
not scls.getASuperType() = cls and result = false
304+
)
338305
or
339-
not kind.getType().getASuperType() = cls and result = false
306+
not exists(kind.getType()) and result = maybe()
340307
)
341308
}
342309

@@ -379,7 +346,7 @@ class TaintTrackingImplementation extends string {
379346
(
380347
not path = TNoAttribute()
381348
or
382-
not this.flowBarrier(node, kind) and
349+
not this.(TaintTracking::Configuration).isBarrier(node, kind) and
383350
exists(DataFlow::Node srcnode, TaintKind srckind |
384351
src = TTaintTrackingNode_(srcnode, _, _, srckind, this) and
385352
not this.prunedEdge(srcnode, node, srckind, kind)
@@ -687,15 +654,18 @@ class TaintTrackingImplementation extends string {
687654
this.taintedExceptionCapture(src, defn, context, path, kind)
688655
or
689656
this.taintedScopeEntryDefinition(src, defn, context, path, kind)
657+
or
658+
this.taintedWith(src, defn, context, path, kind)
690659
}
691660

692661
pragma [noinline]
693662
predicate taintedPhi(TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path, TaintKind kind) {
694-
exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar |
663+
exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi |
695664
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
665+
defn = phi.asVariable().getDefinition() and
696666
predvar = defn.getInput(pred) and
697667
not pred.unlikelySuccessor(defn.getBasicBlock()) and
698-
not predvar.(DataFlowExtension::DataFlowVariable).prunedSuccessor(defn.getVariable()) and
668+
not this.(TaintTracking::Configuration).isBarrierEdge(srcnode, phi) and
699669
srcnode.asVariable() = predvar
700670
)
701671
}
@@ -791,6 +761,14 @@ class TaintTrackingImplementation extends string {
791761
)
792762
}
793763

764+
pragma [noinline]
765+
predicate taintedWith(TaintTrackingNode src, WithDefinition defn, TaintTrackingContext context, AttributePath path, TaintKind kind) {
766+
exists(DataFlow::Node srcnode |
767+
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
768+
with_flow(_, srcnode.asCfgNode(), defn.getDefiningNode())
769+
)
770+
}
771+
794772
predicate moduleAttributeTainted(ModuleValue m, string name, TaintTrackingNode taint) {
795773
exists(DataFlow::Node srcnode, EssaVariable var |
796774
taint = TTaintTrackingNode_(srcnode, TNoParam(), _, _, this) and
@@ -803,6 +781,13 @@ class TaintTrackingImplementation extends string {
803781

804782
}
805783

784+
/* Helper predicate for tainted_with */
785+
private predicate with_flow(With with, ControlFlowNode contextManager, ControlFlowNode var) {
786+
with.getContextExpr() = contextManager.getNode() and
787+
with.getOptionalVars() = var.getNode() and
788+
contextManager.strictlyDominates(var)
789+
}
790+
806791
/* Backwards compatibility with config-less taint-tracking */
807792
private class LegacyConfiguration extends TaintTracking::Configuration {
808793

@@ -811,20 +796,14 @@ private class LegacyConfiguration extends TaintTracking::Configuration {
811796
this = "Semmle: Internal legacy configuration"
812797
}
813798

814-
override predicate isSource(DataFlow::Node source, TaintKind kind) {
799+
override predicate isSource(TaintSource src) {
815800
isValid() and
816-
exists(TaintSource src |
817-
source.asCfgNode() = src and
818-
src.isSourceOf(kind)
819-
)
801+
src = src
820802
}
821803

822-
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
804+
override predicate isSink(TaintSink sink) {
823805
isValid() and
824-
exists(TaintSink snk |
825-
sink.asCfgNode() = snk and
826-
snk.sinks(kind)
827-
)
806+
sink = sink
828807
}
829808

830809
override predicate isSanitizer(Sanitizer sanitizer) {
@@ -836,6 +815,45 @@ private class LegacyConfiguration extends TaintTracking::Configuration {
836815
not exists(TaintTracking::Configuration config | config != this)
837816
}
838817

818+
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) {
819+
isValid() and
820+
exists(DataFlowExtension::DataFlowNode legacyExtension |
821+
src.asCfgNode() = legacyExtension
822+
|
823+
dest.asCfgNode() = legacyExtension.getASuccessorNode()
824+
or
825+
dest.asVariable() = legacyExtension.getASuccessorVariable()
826+
or
827+
dest.asCfgNode() = legacyExtension.getAReturnSuccessorNode(_)
828+
or
829+
dest.asCfgNode() = legacyExtension.getACalleeSuccessorNode(_)
830+
)
831+
}
832+
833+
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) {
834+
isValid() and
835+
exists(DataFlowExtension::DataFlowNode legacyExtension |
836+
src.asCfgNode() = legacyExtension
837+
|
838+
dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind)
839+
)
840+
}
841+
842+
override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest) {
843+
isValid() and
844+
(
845+
exists(DataFlowExtension::DataFlowVariable legacyExtension |
846+
src.asVariable() = legacyExtension and
847+
legacyExtension.prunedSuccessor(dest.asVariable())
848+
)
849+
or
850+
exists(DataFlowExtension::DataFlowNode legacyExtension |
851+
src.asCfgNode() = legacyExtension and
852+
legacyExtension.prunedSuccessor(dest.asCfgNode())
853+
)
854+
)
855+
}
856+
839857
}
840858

841859
module Implementation {

python/ql/test/library-tests/taint/config/RockPaperScissors.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ edges
7272
| test.py:126:13:126:25 | simple.test | test.py:130:21:130:21 | simple.test |
7373
| test.py:128:13:128:18 | simple.test | test.py:132:14:132:14 | simple.test |
7474
| test.py:155:20:155:38 | simple.test | test.py:156:6:156:11 | simple.test |
75+
| test.py:159:10:159:15 | simple.test | test.py:160:14:160:14 | simple.test |
7576
| test.py:163:9:163:14 | simple.test | test.py:165:10:165:10 | simple.test |
7677
| test.py:178:9:178:14 | simple.test | test.py:180:14:180:14 | simple.test |
7778
| test.py:178:9:178:14 | simple.test | test.py:186:14:186:14 | simple.test |
79+
| test.py:195:9:195:14 | simple.test | test.py:197:14:197:14 | simple.test |
7880
| test.py:195:9:195:14 | simple.test | test.py:199:14:199:14 | simple.test |
7981
| test.py:208:11:208:18 | sequence of simple.test | test.py:209:14:209:16 | sequence of simple.test |
8082
| test.py:208:12:208:17 | simple.test | test.py:208:11:208:18 | sequence of simple.test |

python/ql/test/library-tests/taint/config/Simple.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ edges
7272
| test.py:126:13:126:25 | simple.test | test.py:130:21:130:21 | simple.test |
7373
| test.py:128:13:128:18 | simple.test | test.py:132:14:132:14 | simple.test |
7474
| test.py:155:20:155:38 | simple.test | test.py:156:6:156:11 | simple.test |
75+
| test.py:159:10:159:15 | simple.test | test.py:160:14:160:14 | simple.test |
7576
| test.py:163:9:163:14 | simple.test | test.py:165:10:165:10 | simple.test |
7677
| test.py:178:9:178:14 | simple.test | test.py:180:14:180:14 | simple.test |
7778
| test.py:178:9:178:14 | simple.test | test.py:186:14:186:14 | simple.test |
79+
| test.py:195:9:195:14 | simple.test | test.py:197:14:197:14 | simple.test |
7880
| test.py:195:9:195:14 | simple.test | test.py:199:14:199:14 | simple.test |
7981
| test.py:208:11:208:18 | sequence of simple.test | test.py:209:14:209:16 | sequence of simple.test |
8082
| test.py:208:12:208:17 | simple.test | test.py:208:11:208:18 | sequence of simple.test |
@@ -104,8 +106,10 @@ edges
104106
| test.py:111:10:111:12 | Attribute | module.py:3:13:3:18 | simple.test | test.py:111:10:111:12 | simple.test | $@ flows to $@. | module.py:3:13:3:18 | SOURCE | simple.test | test.py:111:10:111:12 | Attribute | simple.test |
105107
| test.py:132:14:132:14 | t | test.py:128:13:128:18 | simple.test | test.py:132:14:132:14 | simple.test | $@ flows to $@. | test.py:128:13:128:18 | SOURCE | simple.test | test.py:132:14:132:14 | t | simple.test |
106108
| test.py:156:6:156:11 | unsafe | module.py:3:13:3:18 | simple.test | test.py:156:6:156:11 | simple.test | $@ flows to $@. | module.py:3:13:3:18 | SOURCE | simple.test | test.py:156:6:156:11 | unsafe | simple.test |
109+
| test.py:160:14:160:14 | t | test.py:159:10:159:15 | simple.test | test.py:160:14:160:14 | simple.test | $@ flows to $@. | test.py:159:10:159:15 | SOURCE | simple.test | test.py:160:14:160:14 | t | simple.test |
107110
| test.py:165:10:165:10 | s | test.py:163:9:163:14 | simple.test | test.py:165:10:165:10 | simple.test | $@ flows to $@. | test.py:163:9:163:14 | SOURCE | simple.test | test.py:165:10:165:10 | s | simple.test |
108111
| test.py:180:14:180:14 | t | test.py:178:9:178:14 | simple.test | test.py:180:14:180:14 | simple.test | $@ flows to $@. | test.py:178:9:178:14 | SOURCE | simple.test | test.py:180:14:180:14 | t | simple.test |
109112
| test.py:186:14:186:14 | t | test.py:178:9:178:14 | simple.test | test.py:186:14:186:14 | simple.test | $@ flows to $@. | test.py:178:9:178:14 | SOURCE | simple.test | test.py:186:14:186:14 | t | simple.test |
113+
| test.py:197:14:197:14 | t | test.py:195:9:195:14 | simple.test | test.py:197:14:197:14 | simple.test | $@ flows to $@. | test.py:195:9:195:14 | SOURCE | simple.test | test.py:197:14:197:14 | t | simple.test |
110114
| test.py:199:14:199:14 | t | test.py:195:9:195:14 | simple.test | test.py:199:14:199:14 | simple.test | $@ flows to $@. | test.py:195:9:195:14 | SOURCE | simple.test | test.py:199:14:199:14 | t | simple.test |
111115
| test.py:214:14:214:14 | x | test.py:208:12:208:17 | simple.test | test.py:214:14:214:14 | simple.test | $@ flows to $@. | test.py:208:12:208:17 | SOURCE | simple.test | test.py:214:14:214:14 | x | simple.test |

python/ql/test/library-tests/taint/config/TestNode.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@
268268
| simple.test | test.py:156 | GSSA Variable unsafe | no attribute | |
269269
| simple.test | test.py:156 | unsafe | no attribute | |
270270
| simple.test | test.py:159 | SOURCE | no attribute | |
271+
| simple.test | test.py:159 | SSA variable t | no attribute | |
272+
| simple.test | test.py:160 | SSA variable t | no attribute | |
273+
| simple.test | test.py:160 | t | no attribute | |
271274
| simple.test | test.py:163 | SOURCE | no attribute | |
272275
| simple.test | test.py:163 | SSA variable s | no attribute | |
273276
| simple.test | test.py:164 | SSA variable s | no attribute | |
@@ -289,6 +292,8 @@
289292
| simple.test | test.py:195 | SOURCE | no attribute | |
290293
| simple.test | test.py:195 | SSA variable t | no attribute | |
291294
| simple.test | test.py:196 | t | no attribute | |
295+
| simple.test | test.py:197 | SSA variable t | no attribute | |
296+
| simple.test | test.py:197 | t | no attribute | |
292297
| simple.test | test.py:199 | SSA variable t | no attribute | |
293298
| simple.test | test.py:199 | t | no attribute | |
294299
| simple.test | test.py:208 | SOURCE | no attribute | |

python/ql/test/library-tests/taint/config/TestStep.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
| Simple config: | simple.test | test.py:138 | SOURCE | | --> | simple.test | test.py:140 | t | |
123123
| Simple config: | simple.test | test.py:148 | SOURCE | | --> | simple.test | test.py:149 | t | |
124124
| Simple config: | simple.test | test.py:155 | ImportMember | | --> | simple.test | test.py:156 | unsafe | |
125+
| Simple config: | simple.test | test.py:159 | SOURCE | | --> | simple.test | test.py:160 | t | |
125126
| Simple config: | simple.test | test.py:163 | SOURCE | | --> | simple.test | test.py:164 | s | |
126127
| Simple config: | simple.test | test.py:163 | SOURCE | | --> | simple.test | test.py:165 | s | |
127128
| Simple config: | simple.test | test.py:168 | SOURCE | | --> | [simple.test] | test.py:168 | List | |
@@ -131,6 +132,7 @@
131132
| Simple config: | simple.test | test.py:178 | SOURCE | | --> | simple.test | test.py:183 | t | |
132133
| Simple config: | simple.test | test.py:178 | SOURCE | | --> | simple.test | test.py:186 | t | |
133134
| Simple config: | simple.test | test.py:195 | SOURCE | | --> | simple.test | test.py:196 | t | |
135+
| Simple config: | simple.test | test.py:195 | SOURCE | | --> | simple.test | test.py:197 | t | |
134136
| Simple config: | simple.test | test.py:195 | SOURCE | | --> | simple.test | test.py:199 | t | |
135137
| Simple config: | simple.test | test.py:208 | SOURCE | | --> | [simple.test] | test.py:208 | List | |
136138
| Simple config: | simple.test | test.py:209 | For | | --> | simple.test | test.py:210 | i | |
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
| Taint simple.test | visitor.py:10 | arg | visitor.py:26 |
2-
| Taint simple.test | visitor.py:13 | arg | visitor.py:26 |
3-
| Taint simple.test | visitor.py:18 | arg | visitor.py:26 |
4-
| Taint simple.test | visitor.py:19 | arg | visitor.py:26 |
5-
| Taint simple.test | visitor.py:21 | arg | visitor.py:26 |
1+
WARNING: Predicate getNode has been deprecated and may be removed in future (TestNode.ql:7,77-84)
2+
| Taint simple.test | visitor.py:10 | arg | p2 = simple.test |
3+
| Taint simple.test | visitor.py:13 | arg | p2 = simple.test |
4+
| Taint simple.test | visitor.py:18 | arg | |
5+
| Taint simple.test | visitor.py:19 | arg | |
6+
| Taint simple.test | visitor.py:21 | arg | |
67
| Taint simple.test | visitor.py:26 | Attribute() | |
78
| Taint simple.test | visitor.py:26 | SOURCE | |
89
| Taint simple.test | visitor.py:27 | x | |

python/ql/test/library-tests/taint/extensions/TestNode.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import ExtensionsLib
44

55

66
from TaintedNode n
7-
select n.getTrackedValue(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext()
7+
select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().getNode().toString(), n.getContext()
88

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
| Taint simple.test | visitor.py:10 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:13 | arg | visitor.py:26 |
2-
| Taint simple.test | visitor.py:18 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:19 | arg | visitor.py:26 |
3-
| Taint simple.test | visitor.py:19 | arg | visitor.py:26 | --> | Taint simple.test | visitor.py:26 | Attribute() | |
1+
WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:9,74-81)
2+
WARNING: Predicate getNode has been deprecated and may be removed in future (TestStep.ql:11,74-81)
3+
| Taint simple.test | visitor.py:10 | arg | p2 = simple.test | --> | Taint simple.test | visitor.py:13 | arg | p2 = simple.test |
4+
| Taint simple.test | visitor.py:18 | arg | | --> | Taint simple.test | visitor.py:19 | arg | |
5+
| Taint simple.test | visitor.py:19 | arg | | --> | Taint simple.test | visitor.py:26 | Attribute() | |
46
| Taint simple.test | visitor.py:26 | Attribute() | | --> | Taint simple.test | visitor.py:27 | x | |
5-
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:10 | arg | visitor.py:26 |
6-
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:18 | arg | visitor.py:26 |
7-
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:21 | arg | visitor.py:26 |
7+
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:10 | arg | p2 = simple.test |
8+
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:18 | arg | |
9+
| Taint simple.test | visitor.py:26 | SOURCE | | --> | Taint simple.test | visitor.py:21 | arg | |

0 commit comments

Comments
 (0)