Skip to content

Commit d282f6a

Browse files
authored
Merge pull request #6218 from tausbn/python-add-typetrackingnode
Approved by RasmusWL
2 parents dd03d81 + 693a479 commit d282f6a

30 files changed

+154
-100
lines changed

python/.vscode/ql.code-snippets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
"prefix": "type tracking",
107107
"body": [
108108
"/** Gets a reference to ${3:a thing}. */",
109-
"private DataFlow::LocalSourceNode ${1:myType}(DataFlow::TypeTracker t) {",
109+
"private DataFlow::TypeTrackingNode ${1:myType}(DataFlow::TypeTracker t) {",
110110
" t.start() and",
111111
" result = ${2:value}",
112112
" or",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* The `track` and `backtrack` methods on `LocalSourceNode` have been deprecated. When writing
3+
type trackers, the corresponding methods on `TypeTrackingNode` should be used instead.

python/ql/src/Security/CVE-2018-1281/BindToAllInterfaces.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private string vulnerableHostname() {
2727
}
2828

2929
/** Gets a reference to a hostname that can be used to bind to all interfaces. */
30-
private DataFlow::LocalSourceNode vulnerableHostnameRef(DataFlow::TypeTracker t, string hostname) {
30+
private DataFlow::TypeTrackingNode vulnerableHostnameRef(DataFlow::TypeTracker t, string hostname) {
3131
t.start() and
3232
exists(StrConst allInterfacesStrConst | hostname = vulnerableHostname() |
3333
allInterfacesStrConst.getText() = hostname and
@@ -43,7 +43,7 @@ DataFlow::Node vulnerableHostnameRef(string hostname) {
4343
}
4444

4545
/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */
46-
private DataFlow::LocalSourceNode vulnerableAddressTuple(DataFlow::TypeTracker t, string hostname) {
46+
private DataFlow::TypeTrackingNode vulnerableAddressTuple(DataFlow::TypeTracker t, string hostname) {
4747
t.start() and
4848
result.asExpr() = any(Tuple tup | tup.getElt(0) = vulnerableHostnameRef(hostname).asExpr())
4949
or

python/ql/src/Security/CWE-215/FlaskDebug.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import semmle.python.ApiGraphs
1717
import semmle.python.frameworks.Flask
1818

1919
/** Gets a reference to a truthy literal. */
20-
private DataFlow::LocalSourceNode truthyLiteral(DataFlow::TypeTracker t) {
20+
private DataFlow::TypeTrackingNode truthyLiteral(DataFlow::TypeTracker t) {
2121
t.start() and
2222
result.asExpr().(ImmutableLiteral).booleanValue() = true
2323
or

python/ql/src/semmle/python/ApiGraphs.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ module API {
512512
*
513513
* The flow from `src` to that node may be inter-procedural.
514514
*/
515-
private DataFlow::LocalSourceNode trackUseNode(
515+
private DataFlow::TypeTrackingNode trackUseNode(
516516
DataFlow::LocalSourceNode src, DataFlow::TypeTracker t
517517
) {
518518
t.start() and
@@ -530,7 +530,6 @@ module API {
530530
cached
531531
DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) {
532532
result = trackUseNode(src, DataFlow::TypeTracker::end()) and
533-
// We exclude module variable nodes, as these do not correspond to real uses.
534533
not result instanceof DataFlow::ModuleVariableNode
535534
}
536535

python/ql/src/semmle/python/Concepts.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ module Cryptography {
758758
/** Provides classes for modeling new key-pair generation APIs. */
759759
module KeyGeneration {
760760
/** Gets a back-reference to the keysize argument `arg` that was used to generate a new key-pair. */
761-
private DataFlow::LocalSourceNode keysizeBacktracker(
761+
private DataFlow::TypeTrackingNode keysizeBacktracker(
762762
DataFlow::TypeBackTracker t, DataFlow::Node arg
763763
) {
764764
t.start() and

python/ql/src/semmle/python/dataflow/new/SensitiveDataSources.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private module SensitiveDataModeling {
5555
* Gets a reference to a function that is considered to be a sensitive source of
5656
* `classification`.
5757
*/
58-
private DataFlow::LocalSourceNode sensitiveFunction(
58+
private DataFlow::TypeTrackingNode sensitiveFunction(
5959
DataFlow::TypeTracker t, SensitiveDataClassification classification
6060
) {
6161
t.start() and
@@ -109,7 +109,7 @@ private module SensitiveDataModeling {
109109
*
110110
* Also see `extraStepForCalls`.
111111
*/
112-
private DataFlow::LocalSourceNode possibleSensitiveCallable(DataFlow::TypeTracker t) {
112+
private DataFlow::TypeTrackingNode possibleSensitiveCallable(DataFlow::TypeTracker t) {
113113
t.start() and
114114
result instanceof SensitiveDataSource
115115
or

python/ql/src/semmle/python/dataflow/new/TypeTracker.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class OptionalAttributeName = Internal::OptionalContentName;
2323
* It is recommended that all uses of this type are written in the following form,
2424
* for tracking some type `myType`:
2525
* ```ql
26-
* DataFlow::LocalSourceNode myType(DataFlow::TypeTracker t) {
26+
* DataFlow::TypeTrackingNode myType(DataFlow::TypeTracker t) {
2727
* t.start() and
2828
* result = < source of myType >
2929
* or

python/ql/src/semmle/python/dataflow/new/internal/LocalSources.qll

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ class LocalSourceNode extends Node {
3636
this instanceof ExprNode and
3737
not simpleLocalFlowStep(_, this)
3838
or
39-
// Module variable nodes must be local source nodes, otherwise type trackers cannot step through
40-
// them.
39+
// We include all module variable nodes, as these act as stepping stones between writes and
40+
// reads of global variables. Without them, type tracking based on `LocalSourceNode`s would be
41+
// unable to track across global variables.
42+
//
43+
// Once the `track` and `backtrack` methods have been fully deprecated, this disjunct can be
44+
// removed, and the entire class can extend `ExprNode`. At that point, `TypeTrackingNode` should
45+
// be used for type tracking instead of `LocalSourceNode`.
4146
this instanceof ModuleVariableNode
4247
or
4348
// We explicitly include any read of a global variable, as some of these may have local flow going
@@ -98,21 +103,68 @@ class LocalSourceNode extends Node {
98103
result = this.getAnAttributeRead(methodName).getACall()
99104
}
100105

106+
/**
107+
* DEPRECATED. Use `TypeTrackingNode::track` instead.
108+
*
109+
* Gets a node that this node may flow to using one heap and/or interprocedural step.
110+
*
111+
* See `TypeTracker` for more details about how to use this.
112+
*/
113+
pragma[inline]
114+
deprecated LocalSourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
115+
116+
/**
117+
* DEPRECATED. Use `TypeTrackingNode::backtrack` instead.
118+
*
119+
* Gets a node that may flow into this one using one heap and/or interprocedural step.
120+
*
121+
* See `TypeBackTracker` for more details about how to use this.
122+
*/
123+
pragma[inline]
124+
deprecated LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
125+
t2 = t.step(result, this)
126+
}
127+
}
128+
129+
/**
130+
* A node that can be used for type tracking or type back-tracking.
131+
*
132+
* All steps made during type tracking should be between instances of this class.
133+
*/
134+
class TypeTrackingNode extends Node {
135+
TypeTrackingNode() {
136+
this instanceof LocalSourceNode
137+
or
138+
this instanceof ModuleVariableNode
139+
}
140+
141+
/**
142+
* Holds if this node can flow to `nodeTo` in one or more local flow steps.
143+
*
144+
* For `ModuleVariableNode`s, the only "local" step is to the node itself.
145+
* For `LocalSourceNode`s, this is the usual notion of local flow.
146+
*/
147+
predicate flowsTo(Node node) {
148+
this instanceof ModuleVariableNode and this = node
149+
or
150+
this.(LocalSourceNode).flowsTo(node)
151+
}
152+
101153
/**
102154
* Gets a node that this node may flow to using one heap and/or interprocedural step.
103155
*
104156
* See `TypeTracker` for more details about how to use this.
105157
*/
106158
pragma[inline]
107-
LocalSourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
159+
TypeTrackingNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
108160

109161
/**
110162
* Gets a node that may flow into this one using one heap and/or interprocedural step.
111163
*
112164
* See `TypeBackTracker` for more details about how to use this.
113165
*/
114166
pragma[inline]
115-
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
167+
TypeTrackingNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
116168
}
117169

118170
cached

python/ql/src/semmle/python/dataflow/new/internal/TypeTracker.qll

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private module Cached {
5959
* Steps contained in this predicate should _not_ depend on the call graph.
6060
*/
6161
cached
62-
predicate stepNoCall(LocalSourceNode nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
62+
predicate stepNoCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
6363
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepNoCall(mid, nodeTo, summary))
6464
}
6565

@@ -68,7 +68,7 @@ private module Cached {
6868
* inter-procedural step from `nodeFrom` to `nodeTo`.
6969
*/
7070
cached
71-
predicate stepCall(LocalSourceNode nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
71+
predicate stepCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
7272
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepCall(mid, nodeTo, summary))
7373
}
7474
}
@@ -96,7 +96,7 @@ class StepSummary extends TStepSummary {
9696
}
9797

9898
pragma[noinline]
99-
private predicate smallstepNoCall(Node nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
99+
private predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
100100
jumpStep(nodeFrom, nodeTo) and
101101
summary = LevelStep()
102102
or
@@ -109,7 +109,7 @@ private predicate smallstepNoCall(Node nodeFrom, LocalSourceNode nodeTo, StepSum
109109
}
110110

111111
pragma[noinline]
112-
private predicate smallstepCall(Node nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
112+
private predicate smallstepCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
113113
callStep(nodeFrom, nodeTo) and summary = CallStep()
114114
or
115115
returnStep(nodeFrom, nodeTo) and
@@ -129,7 +129,7 @@ module StepSummary {
129129
* call graph.
130130
*/
131131
pragma[inline]
132-
predicate step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
132+
predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
133133
stepNoCall(nodeFrom, nodeTo, summary)
134134
or
135135
stepCall(nodeFrom, nodeTo, summary)
@@ -143,7 +143,7 @@ module StepSummary {
143143
* type-preserving steps.
144144
*/
145145
pragma[inline]
146-
predicate smallstep(Node nodeFrom, LocalSourceNode nodeTo, StepSummary summary) {
146+
predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
147147
smallstepNoCall(nodeFrom, nodeTo, summary)
148148
or
149149
smallstepCall(nodeFrom, nodeTo, summary)
@@ -174,7 +174,7 @@ module StepSummary {
174174
* function. This means we will track the fact that `x.attr` can have the type of `y` into the
175175
* assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
176176
*/
177-
predicate localSourceStoreStep(Node nodeFrom, LocalSourceNode nodeTo, string content) {
177+
predicate localSourceStoreStep(Node nodeFrom, TypeTrackingNode nodeTo, string content) {
178178
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
179179
}
180180
}
@@ -192,7 +192,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentNam
192192
* It is recommended that all uses of this type are written in the following form,
193193
* for tracking some type `myType`:
194194
* ```ql
195-
* DataFlow::LocalSourceNode myType(DataFlow::TypeTracker t) {
195+
* DataFlow::TypeTrackingNode myType(DataFlow::TypeTracker t) {
196196
* t.start() and
197197
* result = < source of myType >
198198
* or
@@ -275,7 +275,7 @@ class TypeTracker extends TTypeTracker {
275275
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
276276
*/
277277
pragma[inline]
278-
TypeTracker step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo) {
278+
TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
279279
exists(StepSummary summary |
280280
StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and
281281
result = this.append(pragma[only_bind_into](summary))
@@ -342,7 +342,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional
342342
* for back-tracking some callback type `myCallback`:
343343
*
344344
* ```ql
345-
* DataFlow::LocalSourceNode myCallback(DataFlow::TypeBackTracker t) {
345+
* DataFlow::TypeTrackingNode myCallback(DataFlow::TypeBackTracker t) {
346346
* t.start() and
347347
* result = (< some API call >).getArgument(< n >).getALocalSource()
348348
* or
@@ -351,7 +351,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional
351351
* )
352352
* }
353353
*
354-
* DataFlow::LocalSourceNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) }
354+
* DataFlow::TypeTrackingNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) }
355355
* ```
356356
*
357357
* Instead of `result = myCallback(t2).backtrack(t2, t)`, you can also use the equivalent
@@ -418,7 +418,7 @@ class TypeBackTracker extends TTypeBackTracker {
418418
* heap and/or inter-procedural step from `nodeTo` to `nodeFrom`.
419419
*/
420420
pragma[inline]
421-
TypeBackTracker step(LocalSourceNode nodeFrom, LocalSourceNode nodeTo) {
421+
TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
422422
exists(StepSummary summary |
423423
StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and
424424
this = result.prepend(pragma[only_bind_into](summary))
@@ -431,7 +431,7 @@ class TypeBackTracker extends TTypeBackTracker {
431431
*
432432
* Unlike `TypeBackTracker::step`, this predicate exposes all edges
433433
* in the flowgraph, and not just the edges between
434-
* `LocalSourceNode`s. It may therefore be less performant.
434+
* `TypeTrackingNode`s. It may therefore be less performant.
435435
*
436436
* Type tracking predicates using small steps typically take the following form:
437437
* ```ql

python/ql/src/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr
88

99
class Node = DataFlowPublic::Node;
1010

11-
class LocalSourceNode = DataFlowPublic::LocalSourceNode;
11+
class TypeTrackingNode = DataFlowPublic::TypeTrackingNode;
1212

1313
predicate simpleLocalFlowStep = DataFlowPrivate::simpleLocalFlowStep/2;
1414

python/ql/src/semmle/python/frameworks/Aiohttp.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ module AiohttpWebModel {
114114
* Gets a reference to a class, that has been backtracked from the view-class handler
115115
* argument `origin` (to a route-setup for view-classes).
116116
*/
117-
private DataFlow::LocalSourceNode viewClassBackTracker(
117+
private DataFlow::TypeTrackingNode viewClassBackTracker(
118118
DataFlow::TypeBackTracker t, DataFlow::Node origin
119119
) {
120120
t.start() and
@@ -284,7 +284,7 @@ module AiohttpWebModel {
284284
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
285285

286286
/** Gets a reference to an instance of `aiohttp.web.Request`. */
287-
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
287+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
288288
t.start() and
289289
result instanceof InstanceSource
290290
or
@@ -314,7 +314,7 @@ module AiohttpWebModel {
314314
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
315315

316316
/** Gets a reference to an instance of `aiohttp.web.Response`. */
317-
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
317+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
318318
t.start() and
319319
result instanceof InstanceSource
320320
or
@@ -344,7 +344,7 @@ module AiohttpWebModel {
344344
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
345345

346346
/** Gets a reference to an instance of `aiohttp.StreamReader`. */
347-
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
347+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
348348
t.start() and
349349
result instanceof InstanceSource
350350
or

python/ql/src/semmle/python/frameworks/Cryptography.qll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ private module CryptographyModel {
7575
}
7676

7777
/** Gets a reference to a predefined curve class with a specific key size (in bits), as well as the origin of the class. */
78-
private DataFlow::LocalSourceNode curveClassWithKeySize(
78+
private DataFlow::TypeTrackingNode curveClassWithKeySize(
7979
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
8080
) {
8181
t.start() and
@@ -93,7 +93,7 @@ private module CryptographyModel {
9393
}
9494

9595
/** Gets a reference to a predefined curve class instance with a specific key size (in bits), as well as the origin of the class. */
96-
private DataFlow::LocalSourceNode curveClassInstanceWithKeySize(
96+
private DataFlow::TypeTrackingNode curveClassInstanceWithKeySize(
9797
DataFlow::TypeTracker t, int keySize, DataFlow::Node origin
9898
) {
9999
t.start() and
@@ -202,7 +202,7 @@ private module CryptographyModel {
202202
}
203203

204204
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
205-
DataFlow::LocalSourceNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) {
205+
DataFlow::TypeTrackingNode cipherInstance(DataFlow::TypeTracker t, string algorithmName) {
206206
t.start() and
207207
exists(DataFlow::CallCfgNode call | result = call |
208208
call =
@@ -226,7 +226,7 @@ private module CryptographyModel {
226226
}
227227

228228
/** Gets a reference to the encryptor of a Cipher instance using algorithm with `algorithmName`. */
229-
DataFlow::LocalSourceNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) {
229+
DataFlow::TypeTrackingNode cipherEncryptor(DataFlow::TypeTracker t, string algorithmName) {
230230
t.start() and
231231
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "encryptor")
232232
or
@@ -243,7 +243,7 @@ private module CryptographyModel {
243243
}
244244

245245
/** Gets a reference to the dncryptor of a Cipher instance using algorithm with `algorithmName`. */
246-
DataFlow::LocalSourceNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) {
246+
DataFlow::TypeTrackingNode cipherDecryptor(DataFlow::TypeTracker t, string algorithmName) {
247247
t.start() and
248248
result.(DataFlow::MethodCallNode).calls(cipherInstance(algorithmName), "decryptor")
249249
or
@@ -298,7 +298,7 @@ private module CryptographyModel {
298298
}
299299

300300
/** Gets a reference to a Hash instance using algorithm with `algorithmName`. */
301-
private DataFlow::LocalSourceNode hashInstance(DataFlow::TypeTracker t, string algorithmName) {
301+
private DataFlow::TypeTrackingNode hashInstance(DataFlow::TypeTracker t, string algorithmName) {
302302
t.start() and
303303
exists(DataFlow::CallCfgNode call | result = call |
304304
call =

0 commit comments

Comments
 (0)