Skip to content

Commit bbda22c

Browse files
authored
Merge pull request #4534 from RasmusWL/python-update-flask-modeling
Approved by tausbn
2 parents 08bf464 + ed0fe29 commit bbda22c

File tree

4 files changed

+204
-124
lines changed

4 files changed

+204
-124
lines changed

python/ql/src/experimental/semmle/python/frameworks/Django.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ private module Django {
558558
* A source of an instance of `django.http.request.HttpRequest`.
559559
*
560560
* This can include instantiation of the class, return value from function
561-
* calls, or a special parameter that will be set when functions are call by external
561+
* calls, or a special parameter that will be set when functions are called by an external
562562
* library.
563563
*
564564
* Use `django::http::request::HttpRequest::instance()` predicate to get

python/ql/src/experimental/semmle/python/frameworks/Fabric.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ private module FabricV2 {
459459
* A source of an instance of a subclass of `fabric.group.Group`
460460
*
461461
* This can include instantiation of a class, return value from function
462-
* calls, or a special parameter that will be set when functions are call by external
462+
* calls, or a special parameter that will be set when functions are called by an external
463463
* library.
464464
*
465465
* Use `Group::subclassInstance()` predicate to get references to an instance of a subclass of `fabric.group.Group`.

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

Lines changed: 97 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ private import experimental.dataflow.TaintTracking
1010
private import experimental.semmle.python.Concepts
1111
private import experimental.semmle.python.frameworks.Werkzeug
1212

13-
// for old improved impl see
14-
// https://github.com/github/codeql/blob/9f95212e103c68d0c1dfa4b6f30fb5d53954ccef/python/ql/src/semmle/python/web/flask/Request.qll
1513
/**
1614
* Provides models for the `flask` PyPI package.
1715
* See https://flask.palletsprojects.com/en/1.1.x/.
1816
*/
19-
private module Flask {
17+
private module FlaskModel {
2018
/** Gets a reference to the `flask` module. */
2119
private DataFlow::Node flask(DataFlow::TypeTracker t) {
2220
t.start() and
@@ -44,69 +42,101 @@ private module Flask {
4442
/** Gets a reference to the `flask.request` object. */
4543
DataFlow::Node request() { result = request(DataFlow::TypeTracker::end()) }
4644

47-
/** Gets a reference to the `flask.Flask` class. */
48-
private DataFlow::Node classFlask(DataFlow::TypeTracker t) {
49-
t.start() and
50-
result = DataFlow::importNode("flask.Flask")
51-
or
52-
t.startInAttr("Flask") and
53-
result = flask()
54-
or
55-
exists(DataFlow::TypeTracker t2 | result = classFlask(t2).track(t2, t))
56-
}
57-
58-
/** Gets a reference to the `flask.Flask` class. */
59-
DataFlow::Node classFlask() { result = classFlask(DataFlow::TypeTracker::end()) }
60-
61-
/** Gets a reference to an instance of `flask.Flask` (a Flask application). */
62-
private DataFlow::Node app(DataFlow::TypeTracker t) {
63-
t.start() and
64-
result.asCfgNode().(CallNode).getFunction() = flask::classFlask().asCfgNode()
65-
or
66-
exists(DataFlow::TypeTracker t2 | result = app(t2).track(t2, t))
45+
/**
46+
* Provides models for the `flask.Flask` class
47+
*
48+
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
49+
*/
50+
module Flask {
51+
/** Gets a reference to the `flask.Flask` class. */
52+
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
53+
t.start() and
54+
result = DataFlow::importNode("flask.Flask")
55+
or
56+
t.startInAttr("Flask") and
57+
result = flask()
58+
or
59+
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
60+
}
61+
62+
/** Gets a reference to the `flask.Flask` class. */
63+
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
64+
65+
/**
66+
* A source of an instance of `flask.Flask`.
67+
*
68+
* This can include instantiation of the class, return value from function
69+
* calls, or a special parameter that will be set when functions are call by external
70+
* library.
71+
*
72+
* Use `Flask::instance()` predicate to get references to instances of `flask.Flask`.
73+
*/
74+
abstract class InstanceSource extends DataFlow::Node { }
75+
76+
/** A direct instantiation of `flask.Flask`. */
77+
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
78+
override CallNode node;
79+
80+
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
81+
}
82+
83+
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
84+
private DataFlow::Node instance(DataFlow::TypeTracker t) {
85+
t.start() and
86+
result instanceof InstanceSource
87+
or
88+
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
89+
}
90+
91+
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
92+
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
93+
94+
/**
95+
* Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
96+
* WARNING: Only holds for a few predefined attributes.
97+
*/
98+
private DataFlow::Node instance_attr(DataFlow::TypeTracker t, string attr_name) {
99+
attr_name in ["route", "add_url_rule"] and
100+
t.startInAttr(attr_name) and
101+
result = flask::Flask::instance()
102+
or
103+
// Due to bad performance when using normal setup with `instance_attr(t2, attr_name).track(t2, t)`
104+
// we have inlined that code and forced a join
105+
exists(DataFlow::TypeTracker t2 |
106+
exists(DataFlow::StepSummary summary |
107+
instance_attr_first_join(t2, attr_name, result, summary) and
108+
t = t2.append(summary)
109+
)
110+
)
111+
}
112+
113+
pragma[nomagic]
114+
private predicate instance_attr_first_join(
115+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
116+
DataFlow::StepSummary summary
117+
) {
118+
DataFlow::StepSummary::step(instance_attr(t2, attr_name), res, summary)
119+
}
120+
121+
/**
122+
* Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
123+
* WARNING: Only holds for a few predefined attributes.
124+
*/
125+
private DataFlow::Node instance_attr(string attr_name) {
126+
result = instance_attr(DataFlow::TypeTracker::end(), attr_name)
127+
}
128+
129+
/** Gets a reference to the `route` method on an instance of `flask.Flask`. */
130+
DataFlow::Node route() { result = instance_attr("route") }
131+
132+
/** Gets a reference to the `add_url_rule` method on an instance of `flask.Flask`. */
133+
DataFlow::Node add_url_rule() { result = instance_attr("add_url_rule") }
67134
}
68-
69-
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
70-
DataFlow::Node app() { result = app(DataFlow::TypeTracker::end()) }
71135
}
72136

73137
// ---------------------------------------------------------------------------
74138
// routing modeling
75139
// ---------------------------------------------------------------------------
76-
/**
77-
* Gets a reference to the attribute `attr_name` of a flask application.
78-
* WARNING: Only holds for a few predefined attributes.
79-
*/
80-
private DataFlow::Node app_attr(DataFlow::TypeTracker t, string attr_name) {
81-
attr_name in ["route", "add_url_rule"] and
82-
t.startInAttr(attr_name) and
83-
result = flask::app()
84-
or
85-
// Due to bad performance when using normal setup with `app_attr(t2, attr_name).track(t2, t)`
86-
// we have inlined that code and forced a join
87-
exists(DataFlow::TypeTracker t2 |
88-
exists(DataFlow::StepSummary summary |
89-
app_attr_first_join(t2, attr_name, result, summary) and
90-
t = t2.append(summary)
91-
)
92-
)
93-
}
94-
95-
pragma[nomagic]
96-
private predicate app_attr_first_join(
97-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
98-
) {
99-
DataFlow::StepSummary::step(app_attr(t2, attr_name), res, summary)
100-
}
101-
102-
/**
103-
* Gets a reference to the attribute `attr_name` of a flask application.
104-
* WARNING: Only holds for a few predefined attributes.
105-
*/
106-
private DataFlow::Node app_attr(string attr_name) {
107-
result = app_attr(DataFlow::TypeTracker::end(), attr_name)
108-
}
109-
110140
private string werkzeug_rule_re() {
111141
// since flask uses werkzeug internally, we are using its routing rules from
112142
// https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
@@ -134,14 +164,14 @@ private module Flask {
134164
}
135165

136166
/**
137-
* A call to `flask.Flask.route`.
167+
* A call to the `route` method on an instance of `flask.Flask`.
138168
*
139169
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route
140170
*/
141171
private class FlaskAppRouteCall extends FlaskRouteSetup, DataFlow::CfgNode {
142172
override CallNode node;
143173

144-
FlaskAppRouteCall() { node.getFunction() = app_attr("route").asCfgNode() }
174+
FlaskAppRouteCall() { node.getFunction() = flask::Flask::route().asCfgNode() }
145175

146176
override DataFlow::Node getUrlPatternArg() {
147177
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
@@ -151,14 +181,14 @@ private module Flask {
151181
}
152182

153183
/**
154-
* A call to `flask.Flask.add_url_rule`.
184+
* A call to the `add_url_rule` method on an instance of `flask.Flask`.
155185
*
156186
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule
157187
*/
158-
private class FlaskAppAddUrlRule extends FlaskRouteSetup, DataFlow::CfgNode {
188+
private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup, DataFlow::CfgNode {
159189
override CallNode node;
160190

161-
FlaskAppAddUrlRule() { node.getFunction() = app_attr("add_url_rule").asCfgNode() }
191+
FlaskAppAddUrlRuleCall() { node.getFunction() = flask::Flask::add_url_rule().asCfgNode() }
162192

163193
override DataFlow::Node getUrlPatternArg() {
164194
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
@@ -287,15 +317,15 @@ private module Flask {
287317
}
288318

289319
private class RequestInputMultiDict extends RequestInputAccess,
290-
Werkzeug::Datastructures::MultiDict {
320+
Werkzeug::werkzeug::datastructures::MultiDict::InstanceSource {
291321
RequestInputMultiDict() { attr_name in ["args", "values", "form", "files"] }
292322
}
293323

294324
private class RequestInputFiles extends RequestInputMultiDict {
295325
RequestInputFiles() { attr_name = "files" }
296326
}
297327
// TODO: Somehow specify that elements of `RequestInputFiles` are
298-
// Werkzeug::Datastructures::FileStorage and should have those additional taint steps
328+
// Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
299329
// AND that the 0-indexed argument to its' save method is a sink for path-injection.
300330
// https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
301331
}

0 commit comments

Comments
 (0)