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

Skip to content

Commit b39cbf8

Browse files
committed
Python: Port Flask models to use API graphs
Most of the type trackers in this model were easily replaceable with uses of the API graph, but the ones for tracking subclasses are problematic, as these take us out of the API graph.
1 parent 7fef1a8 commit b39cbf8

1 file changed

Lines changed: 27 additions & 214 deletions

File tree

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

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

Lines changed: 27 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.dataflow.new.TaintTracking
1010
private import semmle.python.Concepts
1111
private import semmle.python.frameworks.Werkzeug
12+
private import semmle.python.ApiGraphs
1213

1314
/**
1415
* Provides models for the `flask` PyPI package.
@@ -19,62 +20,20 @@ private module FlaskModel {
1920
// flask
2021
// ---------------------------------------------------------------------------
2122
/** Gets a reference to the `flask` module. */
22-
private DataFlow::Node flask(DataFlow::TypeTracker t) {
23-
t.start() and
24-
result = DataFlow::importNode("flask")
25-
or
26-
exists(DataFlow::TypeTracker t2 | result = flask(t2).track(t2, t))
27-
}
28-
29-
/** Gets a reference to the `flask` module. */
30-
DataFlow::Node flask() { result = flask(DataFlow::TypeTracker::end()) }
23+
API::Node flask() { result = API::moduleImport("flask") }
3124

3225
/**
3326
* Gets a reference to the attribute `attr_name` of the `flask` module.
34-
* WARNING: Only holds for a few predefined attributes.
3527
*/
36-
private DataFlow::Node flask_attr(DataFlow::TypeTracker t, string attr_name) {
37-
attr_name in ["request", "make_response", "Response", "views"] and
38-
(
39-
t.start() and
40-
result = DataFlow::importNode("flask" + "." + attr_name)
41-
or
42-
t.startInAttr(attr_name) and
43-
result = flask()
44-
)
45-
or
46-
// Due to bad performance when using normal setup with `flask_attr(t2, attr_name).track(t2, t)`
47-
// we have inlined that code and forced a join
48-
exists(DataFlow::TypeTracker t2 |
49-
exists(DataFlow::StepSummary summary |
50-
flask_attr_first_join(t2, attr_name, result, summary) and
51-
t = t2.append(summary)
52-
)
53-
)
54-
}
55-
56-
pragma[nomagic]
57-
private predicate flask_attr_first_join(
58-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
59-
) {
60-
DataFlow::StepSummary::step(flask_attr(t2, attr_name), res, summary)
61-
}
62-
63-
/**
64-
* Gets a reference to the attribute `attr_name` of the `flask` module.
65-
* WARNING: Only holds for a few predefined attributes.
66-
*/
67-
private DataFlow::Node flask_attr(string attr_name) {
68-
result = flask_attr(DataFlow::TypeTracker::end(), attr_name)
69-
}
28+
private API::Node flask_attr(string attr_name) { result = flask().getMember(attr_name) }
7029

7130
/** Provides models for the `flask` module. */
7231
module flask {
7332
/** Gets a reference to the `flask.request` object. */
74-
DataFlow::Node request() { result = flask_attr("request") }
33+
API::Node request() { result = flask_attr("request") }
7534

7635
/** Gets a reference to the `flask.make_response` function. */
77-
DataFlow::Node make_response() { result = flask_attr("make_response") }
36+
DataFlow::Node make_response() { result = flask_attr("make_response").getAUse() }
7837

7938
/**
8039
* Provides models for the `flask.Flask` class
@@ -83,157 +42,49 @@ private module FlaskModel {
8342
*/
8443
module Flask {
8544
/** Gets a reference to the `flask.Flask` class. */
86-
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
87-
t.start() and
88-
result = DataFlow::importNode("flask.Flask")
89-
or
90-
t.startInAttr("Flask") and
91-
result = flask()
92-
or
93-
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
94-
}
95-
96-
/** Gets a reference to the `flask.Flask` class. */
97-
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
98-
99-
/**
100-
* A source of instances of `flask.Flask`, extend this class to model new instances.
101-
*
102-
* This can include instantiations of the class, return values from function
103-
* calls, or a special parameter that will be set when functions are called by an external
104-
* library.
105-
*
106-
* Use the predicate `Flask::instance()` to get references to instances of `flask.Flask`.
107-
*/
108-
abstract class InstanceSource extends DataFlow::Node { }
109-
110-
/** A direct instantiation of `flask.Flask`. */
111-
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
112-
override CallNode node;
113-
114-
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
115-
}
45+
API::Node classRef() { result = flask().getMember("Flask") }
11646

11747
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
118-
private DataFlow::Node instance(DataFlow::TypeTracker t) {
119-
t.start() and
120-
result instanceof InstanceSource
121-
or
122-
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
123-
}
124-
125-
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
126-
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
127-
128-
/**
129-
* Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
130-
* WARNING: Only holds for a few predefined attributes.
131-
*/
132-
private DataFlow::Node instance_attr(DataFlow::TypeTracker t, string attr_name) {
133-
attr_name in ["route", "add_url_rule", "make_response"] and
134-
t.startInAttr(attr_name) and
135-
result = flask::Flask::instance()
136-
or
137-
// Due to bad performance when using normal setup with `instance_attr(t2, attr_name).track(t2, t)`
138-
// we have inlined that code and forced a join
139-
exists(DataFlow::TypeTracker t2 |
140-
exists(DataFlow::StepSummary summary |
141-
instance_attr_first_join(t2, attr_name, result, summary) and
142-
t = t2.append(summary)
143-
)
144-
)
145-
}
146-
147-
pragma[nomagic]
148-
private predicate instance_attr_first_join(
149-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
150-
DataFlow::StepSummary summary
151-
) {
152-
DataFlow::StepSummary::step(instance_attr(t2, attr_name), res, summary)
153-
}
48+
API::Node instance() { result = classRef().getReturn() }
15449

15550
/**
15651
* Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
157-
* WARNING: Only holds for a few predefined attributes.
15852
*/
159-
private DataFlow::Node instance_attr(string attr_name) {
160-
result = instance_attr(DataFlow::TypeTracker::end(), attr_name)
161-
}
53+
private API::Node instance_attr(string attr_name) { result = instance().getMember(attr_name) }
16254

16355
/** Gets a reference to the `route` method on an instance of `flask.Flask`. */
164-
DataFlow::Node route() { result = instance_attr("route") }
56+
API::Node route() { result = instance_attr("route") }
16557

16658
/** Gets a reference to the `add_url_rule` method on an instance of `flask.Flask`. */
167-
DataFlow::Node add_url_rule() { result = instance_attr("add_url_rule") }
59+
API::Node add_url_rule() { result = instance_attr("add_url_rule") }
16860

16961
/** Gets a reference to the `make_response` method on an instance of `flask.Flask`. */
17062
// HACK: We can't call this predicate `make_response` since shadowing is
17163
// completely disallowed in QL. I added an underscore to move things forward for
17264
// now :(
173-
DataFlow::Node make_response_() { result = instance_attr("make_response") }
174-
175-
/** Gets a reference to the `response_class` attribute on the `flask.Flask` class or an instance. */
176-
private DataFlow::Node response_class(DataFlow::TypeTracker t) {
177-
t.startInAttr("response_class") and
178-
result in [classRef(), instance()]
179-
or
180-
exists(DataFlow::TypeTracker t2 | result = response_class(t2).track(t2, t))
181-
}
65+
API::Node make_response_() { result = instance_attr("make_response") }
18266

18367
/**
18468
* Gets a reference to the `response_class` attribute on the `flask.Flask` class or an instance.
18569
*
18670
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.response_class
18771
*/
188-
DataFlow::Node response_class() { result = response_class(DataFlow::TypeTracker::end()) }
72+
API::Node response_class() { result = [classRef(), instance()].getMember("response_class") }
18973
}
19074

19175
// -------------------------------------------------------------------------
19276
// flask.views
19377
// -------------------------------------------------------------------------
19478
/** Gets a reference to the `flask.views` module. */
195-
DataFlow::Node views() { result = flask_attr("views") }
79+
DataFlow::Node views() { result = flask_attr("views").getAUse() }
19680

19781
/** Provides models for the `flask.views` module */
19882
module views {
19983
/**
20084
* Gets a reference to the attribute `attr_name` of the `flask.views` module.
201-
* WARNING: Only holds for a few predefined attributes.
202-
*/
203-
private DataFlow::Node views_attr(DataFlow::TypeTracker t, string attr_name) {
204-
attr_name in ["View", "MethodView"] and
205-
(
206-
t.start() and
207-
result = DataFlow::importNode("flask.views" + "." + attr_name)
208-
or
209-
t.startInAttr(attr_name) and
210-
result = views()
211-
)
212-
or
213-
// Due to bad performance when using normal setup with `views_attr(t2, attr_name).track(t2, t)`
214-
// we have inlined that code and forced a join
215-
exists(DataFlow::TypeTracker t2 |
216-
exists(DataFlow::StepSummary summary |
217-
views_attr_first_join(t2, attr_name, result, summary) and
218-
t = t2.append(summary)
219-
)
220-
)
221-
}
222-
223-
pragma[nomagic]
224-
private predicate views_attr_first_join(
225-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
226-
DataFlow::StepSummary summary
227-
) {
228-
DataFlow::StepSummary::step(views_attr(t2, attr_name), res, summary)
229-
}
230-
231-
/**
232-
* Gets a reference to the attribute `attr_name` of the `flask.views` module.
233-
* WARNING: Only holds for a few predefined attributes.
23485
*/
23586
private DataFlow::Node views_attr(string attr_name) {
236-
result = views_attr(DataFlow::TypeTracker::end(), attr_name)
87+
result = flask().getMember("views").getMember(attr_name).getAUse()
23788
}
23889

23990
/**
@@ -287,15 +138,7 @@ private module FlaskModel {
287138
*/
288139
module Response {
289140
/** Gets a reference to the `flask.Response` class. */
290-
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
291-
t.start() and
292-
result in [flask_attr("Response"), flask::Flask::response_class()]
293-
or
294-
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
295-
}
296-
297-
/** Gets a reference to the `flask.Response` class. */
298-
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
141+
API::Node classRef() { result = [flask_attr("Response"), flask::Flask::response_class()] }
299142

300143
/**
301144
* A source of instances of `flask.Response`, extend this class to model new instances.
@@ -312,7 +155,7 @@ private module FlaskModel {
312155
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
313156
override CallNode node;
314157

315-
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
158+
ClassInstantiation() { node.getFunction() = classRef().getAUse().asCfgNode() }
316159

317160
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }
318161

@@ -338,15 +181,7 @@ private module FlaskModel {
338181
}
339182

340183
/** Gets a reference to an instance of `flask.Response`. */
341-
private DataFlow::Node instance(DataFlow::TypeTracker t) {
342-
t.start() and
343-
result instanceof InstanceSource
344-
or
345-
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
346-
}
347-
348-
/** Gets a reference to an instance of `flask.Response`. */
349-
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
184+
API::Node instance() { result = classRef().getReturn() }
350185
}
351186

352187
// ---------------------------------------------------------------------------
@@ -445,7 +280,7 @@ private module FlaskModel {
445280
private class FlaskAppRouteCall extends FlaskRouteSetup, DataFlow::CfgNode {
446281
override CallNode node;
447282

448-
FlaskAppRouteCall() { node.getFunction() = flask::Flask::route().asCfgNode() }
283+
FlaskAppRouteCall() { node.getFunction() = flask::Flask::route().getAUse().asCfgNode() }
449284

450285
override DataFlow::Node getUrlPatternArg() {
451286
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
@@ -462,7 +297,9 @@ private module FlaskModel {
462297
private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup, DataFlow::CfgNode {
463298
override CallNode node;
464299

465-
FlaskAppAddUrlRuleCall() { node.getFunction() = flask::Flask::add_url_rule().asCfgNode() }
300+
FlaskAppAddUrlRuleCall() {
301+
node.getFunction() = flask::Flask::add_url_rule().getAUse().asCfgNode()
302+
}
466303

467304
override DataFlow::Node getUrlPatternArg() {
468305
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
@@ -511,41 +348,16 @@ private module FlaskModel {
511348
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
512349
*/
513350
private class RequestSource extends RemoteFlowSource::Range {
514-
RequestSource() { this = flask::request() }
351+
RequestSource() { this = flask::request().getAUse() }
515352

516353
override string getSourceType() { result = "flask.request" }
517354
}
518355

519356
private module FlaskRequestTracking {
520-
/** Gets a reference to the `get_data` attribute of a Flask request. */
521-
private DataFlow::Node get_data(DataFlow::TypeTracker t) {
522-
t.startInAttr("get_data") and
523-
result = flask::request()
524-
or
525-
exists(DataFlow::TypeTracker t2 | result = get_data(t2).track(t2, t))
526-
}
527-
528-
/** Gets a reference to the `get_data` attribute of a Flask request. */
529-
DataFlow::Node get_data() { result = get_data(DataFlow::TypeTracker::end()) }
530-
531-
/** Gets a reference to the `get_json` attribute of a Flask request. */
532-
private DataFlow::Node get_json(DataFlow::TypeTracker t) {
533-
t.startInAttr("get_json") and
534-
result = flask::request()
535-
or
536-
exists(DataFlow::TypeTracker t2 | result = get_json(t2).track(t2, t))
537-
}
538-
539-
/** Gets a reference to the `get_json` attribute of a Flask request. */
540-
DataFlow::Node get_json() { result = get_json(DataFlow::TypeTracker::end()) }
541-
542357
/** Gets a reference to either of the `get_json` or `get_data` attributes of a Flask request. */
543358
DataFlow::Node tainted_methods(string attr_name) {
544-
result = get_data() and
545-
attr_name = "get_data"
546-
or
547-
result = get_json() and
548-
attr_name = "get_json"
359+
attr_name in ["get_data", "get_json"] and
360+
result = flask::request().getMember(attr_name).getAUse()
549361
}
550362
}
551363

@@ -560,7 +372,8 @@ private module FlaskModel {
560372
RequestInputAccess() {
561373
// attributes
562374
exists(AttrNode attr |
563-
this.asCfgNode() = attr and attr.getObject(attr_name) = flask::request().asCfgNode()
375+
this.asCfgNode() = attr and
376+
attr.getObject(attr_name) = flask::request().getAUse().asCfgNode()
564377
|
565378
attr_name in [
566379
// str
@@ -645,7 +458,7 @@ private module FlaskModel {
645458
FlaskMakeResponseCall() {
646459
node.getFunction() = flask::make_response().asCfgNode()
647460
or
648-
node.getFunction() = flask::Flask::make_response_().asCfgNode()
461+
node.getFunction() = flask::Flask::make_response_().getAUse().asCfgNode()
649462
}
650463

651464
override DataFlow::Node getBody() { result.asCfgNode() = node.getArg(0) }

0 commit comments

Comments
 (0)