Skip to content

Commit ba61099

Browse files
committed
Python: flask.make_response as InstanceSource of flask.Response
1 parent e3d530d commit ba61099

1 file changed

Lines changed: 39 additions & 31 deletions

File tree

  • python/ql/src/semmle/python/frameworks

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

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ private module FlaskModel {
8989
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Response.
9090
*/
9191
module Response {
92-
/** Gets a reference to the `flask.Response` class. */
92+
/**
93+
* Gets a reference to the `flask.Response` class, possibly through the
94+
* `response_class` class attribute on a flask application (which by is an alias for
95+
* `flask.Response` by default).
96+
*/
9397
API::Node classRef() {
9498
result = API::moduleImport("flask").getMember("Response")
9599
or
@@ -134,8 +138,32 @@ private module FlaskModel {
134138
}
135139
}
136140

137-
/** Gets a reference to an instance of `flask.Response`. */
138-
API::Node instance() { result = classRef().getReturn() }
141+
/**
142+
* A call to either `flask.make_response` function, or the `make_response` method on
143+
* an instance of `flask.Flask`. This creates an instance of the `flask_response`
144+
* class (class-attribute on a flask application), which by default is
145+
* `flask.Response`.
146+
*
147+
* See
148+
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.make_response
149+
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response
150+
*/
151+
private class FlaskMakeResponseCall extends InstanceSource, DataFlow::CallCfgNode {
152+
FlaskMakeResponseCall() {
153+
this = API::moduleImport("flask").getMember("make_response").getACall()
154+
or
155+
this = FlaskApp::instance().getMember("make_response").getACall()
156+
}
157+
158+
override DataFlow::Node getBody() { result = this.getArg(0) }
159+
160+
override string getMimetypeDefault() { result = "text/html" }
161+
162+
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
163+
}
164+
// TODO: Enable again, but take `InstanceSource` into account
165+
// /** Gets a reference to an instance of `flask.Response`. */
166+
// API::Node instance() { result = classRef().getReturn() }
139167
}
140168

141169
// ---------------------------------------------------------------------------
@@ -366,39 +394,16 @@ private module FlaskModel {
366394
}
367395

368396
private class RequestInputFiles extends RequestInputMultiDict {
397+
// TODO: Somehow specify that elements of `RequestInputFiles` are
398+
// Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
399+
// AND that the 0-indexed argument to its' save method is a sink for path-injection.
400+
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
369401
RequestInputFiles() { attr_name = "files" }
370402
}
371403

372-
// TODO: Somehow specify that elements of `RequestInputFiles` are
373-
// Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
374-
// AND that the 0-indexed argument to its' save method is a sink for path-injection.
375-
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
376404
// ---------------------------------------------------------------------------
377-
// Response modeling
405+
// Implicit response from returns of flask request handlers
378406
// ---------------------------------------------------------------------------
379-
/**
380-
* A call to either `flask.make_response` function, or the `make_response` method on
381-
* an instance of `flask.Flask`.
382-
*
383-
* See
384-
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.make_response
385-
* - https://flask.palletsprojects.com/en/1.1.x/api/#flask.make_response
386-
*/
387-
private class FlaskMakeResponseCall extends HTTP::Server::HttpResponse::Range,
388-
DataFlow::CallCfgNode {
389-
FlaskMakeResponseCall() {
390-
this = API::moduleImport("flask").getMember("make_response").getACall()
391-
or
392-
this = FlaskApp::instance().getMember("make_response").getACall()
393-
}
394-
395-
override DataFlow::Node getBody() { result = this.getArg(0) }
396-
397-
override string getMimetypeDefault() { result = "text/html" }
398-
399-
override DataFlow::Node getMimetypeOrContentTypeArg() { none() }
400-
}
401-
402407
private class FlaskRouteHandlerReturn extends HTTP::Server::HttpResponse::Range, DataFlow::CfgNode {
403408
FlaskRouteHandlerReturn() {
404409
exists(Function routeHandler |
@@ -414,6 +419,9 @@ private module FlaskModel {
414419
override string getMimetypeDefault() { result = "text/html" }
415420
}
416421

422+
// ---------------------------------------------------------------------------
423+
// flask.redirect
424+
// ---------------------------------------------------------------------------
417425
/**
418426
* A call to the `flask.redirect` function.
419427
*

0 commit comments

Comments
 (0)