Skip to content

Commit 72a699e

Browse files
committed
Python: Add CallCfgNode class and rewrite using that class
I prefer this name to `CfgCallNode` as the latter will make autocomplete more difficult.
1 parent 46eb3fd commit 72a699e

File tree

2 files changed

+40
-36
lines changed

2 files changed

+40
-36
lines changed

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,23 @@ class CfgNode extends Node, TCfgNode {
165165
override Location getLocation() { result = node.getLocation() }
166166
}
167167

168+
/** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
169+
class CallCfgNode extends CfgNode {
170+
override CallNode node;
171+
172+
/**
173+
* Gets the data-flow node for the function component of the call corresponding to this data-flow
174+
* node.
175+
*/
176+
Node getFunction() { result.asCfgNode() = node.getFunction() }
177+
178+
/** Gets the data-flow node corresponding to the nth argument of the call corresponding to this data-flow node */
179+
Node getArg(int i) { result.asCfgNode() = node.getArg(i) }
180+
181+
/** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */
182+
Node getArgByName(string name) { result.asCfgNode() = node.getArgByName(name) }
183+
}
184+
168185
/**
169186
* An expression, viewed as a node in a data flow graph.
170187
*
@@ -481,7 +498,7 @@ class LocalSourceNode extends Node {
481498
/**
482499
* Gets a call to this node.
483500
*/
484-
Node getACall() { Cached::call(this, result) }
501+
CallCfgNode getACall() { Cached::call(this, result) }
485502
}
486503

487504
cached
@@ -526,10 +543,10 @@ private module Cached {
526543
* Holds if `func` flows to the callee of `call`.
527544
*/
528545
cached
529-
predicate call(LocalSourceNode func, Node call) {
546+
predicate call(LocalSourceNode func, CallCfgNode call) {
530547
exists(CfgNode n |
531548
func.flowsTo(n) and
532-
n.asCfgNode() = call.asCfgNode().(CallNode).getFunction()
549+
n = call.getFunction()
533550
)
534551
}
535552
}

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

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -125,23 +125,21 @@ private module FlaskModel {
125125
abstract class InstanceSource extends HTTP::Server::HttpResponse::Range, DataFlow::Node { }
126126

127127
/** A direct instantiation of `flask.Response`. */
128-
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
129-
override CallNode node;
130-
128+
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
131129
ClassInstantiation() { this = classRef().getACall() }
132130

133-
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
131+
override DataFlow::Node getBody() { result = this.getArg(0) }
134132

135133
override string getMimetypeDefault() { result = "text/html" }
136134

137135
/** Gets the argument passed to the `mimetype` parameter, if any. */
138136
private DataFlow::Node getMimetypeArg() {
139-
result.asCfgNode() in [node.getArg(3), node.getArgByName("mimetype")]
137+
result in [this.getArg(3), this.getArgByName("mimetype")]
140138
}
141139

142140
/** Gets the argument passed to the `content_type` parameter, if any. */
143141
private DataFlow::Node getContentTypeArg() {
144-
result.asCfgNode() in [node.getArg(4), node.getArgByName("content_type")]
142+
result in [this.getArg(4), this.getArgByName("content_type")]
145143
}
146144

147145
override DataFlow::Node getMimetypeOrContentTypeArg() {
@@ -228,13 +226,11 @@ private module FlaskModel {
228226
*
229227
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route
230228
*/
231-
private class FlaskAppRouteCall extends FlaskRouteSetup, DataFlow::CfgNode {
232-
override CallNode node;
233-
234-
FlaskAppRouteCall() { node.getFunction() = flask::Flask::route().getAUse().asCfgNode() }
229+
private class FlaskAppRouteCall extends FlaskRouteSetup, DataFlow::CallCfgNode {
230+
FlaskAppRouteCall() { this.getFunction() = flask::Flask::route().getAUse() }
235231

236232
override DataFlow::Node getUrlPatternArg() {
237-
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
233+
result in [this.getArg(0), this.getArgByName("rule")]
238234
}
239235

240236
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
@@ -245,20 +241,14 @@ private module FlaskModel {
245241
*
246242
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule
247243
*/
248-
private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup, DataFlow::CfgNode {
249-
override CallNode node;
250-
251-
FlaskAppAddUrlRuleCall() {
252-
node.getFunction() = flask::Flask::add_url_rule().getAUse().asCfgNode()
253-
}
244+
private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup, DataFlow::CallCfgNode {
245+
FlaskAppAddUrlRuleCall() { this.getFunction() = flask::Flask::add_url_rule().getAUse() }
254246

255247
override DataFlow::Node getUrlPatternArg() {
256-
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
248+
result in [this.getArg(0), this.getArgByName("rule")]
257249
}
258250

259-
DataFlow::Node getViewArg() {
260-
result.asCfgNode() in [node.getArg(2), node.getArgByName("view_func")]
261-
}
251+
DataFlow::Node getViewArg() { result in [this.getArg(2), this.getArgByName("view_func")] }
262252

263253
override Function getARequestHandler() {
264254
exists(DataFlow::LocalSourceNode func_src |
@@ -375,7 +365,7 @@ private module FlaskModel {
375365
// NOTE: `request -> request.tainted_method` part is handled as part of RequestInputAccess
376366
// tainted_method -> tainted_method()
377367
nodeFrom = FlaskRequestTracking::tainted_methods(_).getAUse() and
378-
nodeTo.asCfgNode().(CallNode).getFunction() = nodeFrom.asCfgNode()
368+
nodeTo.(DataFlow::CallCfgNode).getFunction() = nodeFrom
379369
}
380370
}
381371

@@ -403,16 +393,15 @@ private module FlaskModel {
403393
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.make_response
404394
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response
405395
*/
406-
private class FlaskMakeResponseCall extends HTTP::Server::HttpResponse::Range, DataFlow::CfgNode {
407-
override CallNode node;
408-
396+
private class FlaskMakeResponseCall extends HTTP::Server::HttpResponse::Range,
397+
DataFlow::CallCfgNode {
409398
FlaskMakeResponseCall() {
410-
node.getFunction() = flask::make_response().getAUse().asCfgNode()
399+
this.getFunction() = flask::make_response().getAUse()
411400
or
412-
node.getFunction() = flask::Flask::make_response_().getAUse().asCfgNode()
401+
this.getFunction() = flask::Flask::make_response_().getAUse()
413402
}
414403

415-
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
404+
override DataFlow::Node getBody() { result = this.getArg(0) }
416405

417406
override string getMimetypeDefault() { result = "text/html" }
418407

@@ -440,13 +429,11 @@ private module FlaskModel {
440429
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.redirect
441430
*/
442431
private class FlaskRedirectCall extends HTTP::Server::HttpRedirectResponse::Range,
443-
DataFlow::CfgNode {
444-
override CallNode node;
445-
446-
FlaskRedirectCall() { node.getFunction() = flask_attr("redirect").getAUse().asCfgNode() }
432+
DataFlow::CallCfgNode {
433+
FlaskRedirectCall() { this.getFunction() = flask_attr("redirect").getAUse() }
447434

448435
override DataFlow::Node getRedirectLocation() {
449-
result.asCfgNode() in [node.getArg(0), node.getArgByName("location")]
436+
result in [this.getArg(0), this.getArgByName("location")]
450437
}
451438

452439
override DataFlow::Node getBody() { none() }

0 commit comments

Comments
 (0)