Skip to content

Commit 98d936d

Browse files
authored
Python: Tornado cleanup using API graphs
I wasn't able to roll out API graphs as widely in Tornado as I had hoped, since we're lacking the "def" part. This means most of the `InstanceSource` machinery will have to stay.
1 parent f341d50 commit 98d936d

File tree

1 file changed

+32
-163
lines changed

1 file changed

+32
-163
lines changed

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

Lines changed: 32 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88
private import semmle.python.dataflow.new.RemoteFlowSources
99
private import semmle.python.dataflow.new.TaintTracking
1010
private import semmle.python.Concepts
11+
private import semmle.python.ApiGraphs
1112
private import semmle.python.regex
1213

1314
/**
@@ -19,104 +20,29 @@ private module Tornado {
1920
// tornado
2021
// ---------------------------------------------------------------------------
2122
/** Gets a reference to the `tornado` module. */
22-
private DataFlow::Node tornado(DataFlow::TypeTracker t) {
23-
t.start() and
24-
result = DataFlow::importNode("tornado")
25-
or
26-
exists(DataFlow::TypeTracker t2 | result = tornado(t2).track(t2, t))
27-
}
28-
29-
/** Gets a reference to the `tornado` module. */
30-
DataFlow::Node tornado() { result = tornado(DataFlow::TypeTracker::end()) }
31-
32-
/**
33-
* Gets a reference to the attribute `attr_name` of the `tornado` module.
34-
* WARNING: Only holds for a few predefined attributes.
35-
*/
36-
private DataFlow::Node tornado_attr(DataFlow::TypeTracker t, string attr_name) {
37-
attr_name in ["web", "httputil"] and
38-
(
39-
t.start() and
40-
result = DataFlow::importNode("tornado" + "." + attr_name)
41-
or
42-
t.startInAttr(attr_name) and
43-
result = tornado()
44-
)
45-
or
46-
// Due to bad performance when using normal setup with `tornado_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-
tornado_attr_first_join(t2, attr_name, result, summary) and
51-
t = t2.append(summary)
52-
)
53-
)
54-
}
55-
56-
pragma[nomagic]
57-
private predicate tornado_attr_first_join(
58-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
59-
) {
60-
DataFlow::StepSummary::step(tornado_attr(t2, attr_name), res, summary)
61-
}
23+
API::Node tornado() { result = API::moduleImport("tornado") }
6224

6325
/**
6426
* Gets a reference to the attribute `attr_name` of the `tornado` module.
6527
* WARNING: Only holds for a few predefined attributes.
6628
*/
67-
private DataFlow::Node tornado_attr(string attr_name) {
68-
result = tornado_attr(DataFlow::TypeTracker::end(), attr_name)
69-
}
29+
private API::Node tornado_attr(string attr_name) { result = tornado().getMember(attr_name) }
7030

7131
/** Provides models for the `tornado` module. */
7232
module tornado {
7333
// -------------------------------------------------------------------------
7434
// tornado.web
7535
// -------------------------------------------------------------------------
7636
/** Gets a reference to the `tornado.web` module. */
77-
DataFlow::Node web() { result = tornado_attr("web") }
37+
API::Node web() { result = tornado_attr("web") }
7838

7939
/** Provides models for the `tornado.web` module */
8040
module web {
8141
/**
8242
* Gets a reference to the attribute `attr_name` of the `tornado.web` module.
8343
* WARNING: Only holds for a few predefined attributes.
8444
*/
85-
private DataFlow::Node web_attr(DataFlow::TypeTracker t, string attr_name) {
86-
attr_name in ["RequestHandler", "Application"] and
87-
(
88-
t.start() and
89-
result = DataFlow::importNode("tornado.web" + "." + attr_name)
90-
or
91-
t.startInAttr(attr_name) and
92-
result = web()
93-
)
94-
or
95-
// Due to bad performance when using normal setup with `web_attr(t2, attr_name).track(t2, t)`
96-
// we have inlined that code and forced a join
97-
exists(DataFlow::TypeTracker t2 |
98-
exists(DataFlow::StepSummary summary |
99-
web_attr_first_join(t2, attr_name, result, summary) and
100-
t = t2.append(summary)
101-
)
102-
)
103-
}
104-
105-
pragma[nomagic]
106-
private predicate web_attr_first_join(
107-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
108-
DataFlow::StepSummary summary
109-
) {
110-
DataFlow::StepSummary::step(web_attr(t2, attr_name), res, summary)
111-
}
112-
113-
/**
114-
* Gets a reference to the attribute `attr_name` of the `tornado.web` module.
115-
* WARNING: Only holds for a few predefined attributes.
116-
*/
117-
private DataFlow::Node web_attr(string attr_name) {
118-
result = web_attr(DataFlow::TypeTracker::end(), attr_name)
119-
}
45+
private API::Node web_attr(string attr_name) { result = web().getMember(attr_name) }
12046

12147
/**
12248
* Provides models for the `tornado.web.RequestHandler` class and subclasses.
@@ -125,22 +51,11 @@ private module Tornado {
12551
*/
12652
module RequestHandler {
12753
/** Gets a reference to the `tornado.web.RequestHandler` class or any subclass. */
128-
private DataFlow::Node subclassRef(DataFlow::TypeTracker t) {
129-
t.start() and
130-
result = web_attr("RequestHandler")
131-
or
132-
// subclasses in project code
133-
result.asExpr().(ClassExpr).getABase() = subclassRef(t.continue()).asExpr()
134-
or
135-
exists(DataFlow::TypeTracker t2 | result = subclassRef(t2).track(t2, t))
136-
}
137-
138-
/** Gets a reference to the `tornado.web.RequestHandler` class or any subclass. */
139-
DataFlow::Node subclassRef() { result = subclassRef(DataFlow::TypeTracker::end()) }
54+
API::Node subclassRef() { result = web_attr("RequestHandler").getASubclass*() }
14055

14156
/** A RequestHandler class (most likely in project code). */
14257
class RequestHandlerClass extends Class {
143-
RequestHandlerClass() { this.getParent() = subclassRef().asExpr() }
58+
RequestHandlerClass() { this.getParent() = subclassRef().getAUse().asExpr() }
14459

14560
/** Gets a function that could handle incoming requests, if any. */
14661
Function getARequestHandler() {
@@ -151,15 +66,15 @@ private module Tornado {
15166
}
15267

15368
/** Gets a reference to this class. */
154-
private DataFlow::Node getARef(DataFlow::TypeTracker t) {
69+
private DataFlow::LocalSourceNode getARef(DataFlow::TypeTracker t) {
15570
t.start() and
15671
result.asExpr().(ClassExpr) = this.getParent()
15772
or
15873
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
15974
}
16075

16176
/** Gets a reference to this class. */
162-
DataFlow::Node getARef() { result = this.getARef(DataFlow::TypeTracker::end()) }
77+
DataFlow::Node getARef() { this.getARef(DataFlow::TypeTracker::end()).flowsTo(result) }
16378
}
16479

16580
/**
@@ -184,29 +99,31 @@ private module Tornado {
18499
}
185100

186101
/** Gets a reference to an instance of the `tornado.web.RequestHandler` class or any subclass. */
187-
private DataFlow::Node instance(DataFlow::TypeTracker t) {
102+
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
188103
t.start() and
189104
result instanceof InstanceSource
190105
or
191106
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
192107
}
193108

194109
/** Gets a reference to an instance of the `tornado.web.RequestHandler` class or any subclass. */
195-
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
110+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
196111

197112
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
198-
private DataFlow::Node argumentMethod(DataFlow::TypeTracker t) {
113+
private DataFlow::LocalSourceNode argumentMethod(DataFlow::TypeTracker t) {
199114
t.startInAttr(["get_argument", "get_body_argument", "get_query_argument"]) and
200115
result = instance()
201116
or
202117
exists(DataFlow::TypeTracker t2 | result = argumentMethod(t2).track(t2, t))
203118
}
204119

205120
/** Gets a reference to one of the methods `get_argument`, `get_body_argument`, `get_query_argument`. */
206-
DataFlow::Node argumentMethod() { result = argumentMethod(DataFlow::TypeTracker::end()) }
121+
DataFlow::Node argumentMethod() {
122+
argumentMethod(DataFlow::TypeTracker::end()).flowsTo(result)
123+
}
207124

208125
/** Gets a reference to one of the methods `get_arguments`, `get_body_arguments`, `get_query_arguments`. */
209-
private DataFlow::Node argumentsMethod(DataFlow::TypeTracker t) {
126+
private DataFlow::LocalSourceNode argumentsMethod(DataFlow::TypeTracker t) {
210127
t.startInAttr(["get_arguments", "get_body_arguments", "get_query_arguments"]) and
211128
result = instance()
212129
or
@@ -217,26 +134,28 @@ private module Tornado {
217134
DataFlow::Node argumentsMethod() { result = argumentsMethod(DataFlow::TypeTracker::end()) }
218135

219136
/** Gets a reference the `redirect` method. */
220-
private DataFlow::Node redirectMethod(DataFlow::TypeTracker t) {
137+
private DataFlow::LocalSourceNode redirectMethod(DataFlow::TypeTracker t) {
221138
t.startInAttr("redirect") and
222139
result = instance()
223140
or
224141
exists(DataFlow::TypeTracker t2 | result = redirectMethod(t2).track(t2, t))
225142
}
226143

227144
/** Gets a reference the `redirect` method. */
228-
DataFlow::Node redirectMethod() { result = redirectMethod(DataFlow::TypeTracker::end()) }
145+
DataFlow::Node redirectMethod() {
146+
redirectMethod(DataFlow::TypeTracker::end()).flowsTo(result)
147+
}
229148

230149
/** Gets a reference to the `write` method. */
231-
private DataFlow::Node writeMethod(DataFlow::TypeTracker t) {
150+
private DataFlow::LocalSourceNode writeMethod(DataFlow::TypeTracker t) {
232151
t.startInAttr("write") and
233152
result = instance()
234153
or
235154
exists(DataFlow::TypeTracker t2 | result = writeMethod(t2).track(t2, t))
236155
}
237156

238157
/** Gets a reference to the `write` method. */
239-
DataFlow::Node writeMethod() { result = writeMethod(DataFlow::TypeTracker::end()) }
158+
DataFlow::Node writeMethod() { writeMethod(DataFlow::TypeTracker::end()).flowsTo(result) }
240159

241160
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
242161
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
@@ -279,15 +198,7 @@ private module Tornado {
279198
*/
280199
module Application {
281200
/** Gets a reference to the `tornado.web.Application` class. */
282-
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
283-
t.start() and
284-
result = web_attr("Application")
285-
or
286-
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
287-
}
288-
289-
/** Gets a reference to the `tornado.web.Application` class. */
290-
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
201+
API::Node classRef() { result = web_attr("Application") }
291202

292203
/**
293204
* A source of instances of `tornado.web.Application`, extend this class to model new instances.
@@ -304,80 +215,46 @@ private module Tornado {
304215
class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
305216
override CallNode node;
306217

307-
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
218+
ClassInstantiation() { this = classRef().getACall() }
308219
}
309220

310221
/** Gets a reference to an instance of `tornado.web.Application`. */
311-
private DataFlow::Node instance(DataFlow::TypeTracker t) {
222+
private DataFlow::LocalSourceNode instance(DataFlow::TypeTracker t) {
312223
t.start() and
313224
result instanceof InstanceSource
314225
or
315226
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
316227
}
317228

318229
/** Gets a reference to an instance of `tornado.web.Application`. */
319-
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
230+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
320231

321232
/** Gets a reference to the `add_handlers` method. */
322-
private DataFlow::Node add_handlers(DataFlow::TypeTracker t) {
233+
private DataFlow::LocalSourceNode add_handlers(DataFlow::TypeTracker t) {
323234
t.startInAttr("add_handlers") and
324235
result = instance()
325236
or
326237
exists(DataFlow::TypeTracker t2 | result = add_handlers(t2).track(t2, t))
327238
}
328239

329240
/** Gets a reference to the `add_handlers` method. */
330-
DataFlow::Node add_handlers() { result = add_handlers(DataFlow::TypeTracker::end()) }
241+
DataFlow::Node add_handlers() { add_handlers(DataFlow::TypeTracker::end()).flowsTo(result) }
331242
}
332243
}
333244

334245
// -------------------------------------------------------------------------
335246
// tornado.httputil
336247
// -------------------------------------------------------------------------
337248
/** Gets a reference to the `tornado.httputil` module. */
338-
DataFlow::Node httputil() { result = tornado_attr("httputil") }
249+
API::Node httputil() { result = tornado_attr("httputil") }
339250

340251
/** Provides models for the `tornado.httputil` module */
341252
module httputil {
342253
/**
343254
* Gets a reference to the attribute `attr_name` of the `tornado.httputil` module.
344255
* WARNING: Only holds for a few predefined attributes.
345256
*/
346-
private DataFlow::Node httputil_attr(DataFlow::TypeTracker t, string attr_name) {
347-
attr_name in ["HTTPServerRequest"] and
348-
(
349-
t.start() and
350-
result = DataFlow::importNode("tornado.httputil" + "." + attr_name)
351-
or
352-
t.startInAttr(attr_name) and
353-
result = httputil()
354-
)
355-
or
356-
// Due to bad performance when using normal setup with `httputil_attr(t2, attr_name).track(t2, t)`
357-
// we have inlined that code and forced a join
358-
exists(DataFlow::TypeTracker t2 |
359-
exists(DataFlow::StepSummary summary |
360-
httputil_attr_first_join(t2, attr_name, result, summary) and
361-
t = t2.append(summary)
362-
)
363-
)
364-
}
365-
366-
pragma[nomagic]
367-
private predicate httputil_attr_first_join(
368-
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
369-
DataFlow::StepSummary summary
370-
) {
371-
DataFlow::StepSummary::step(httputil_attr(t2, attr_name), res, summary)
372-
}
373-
374-
/**
375-
* Gets a reference to the attribute `attr_name` of the `tornado.httputil` module.
376-
* WARNING: Only holds for a few predefined attributes.
377-
*/
378-
private DataFlow::Node httputil_attr(string attr_name) {
379-
result = httputil_attr(DataFlow::TypeTracker::end(), attr_name)
380-
}
257+
private API::Node httputil_attr(string attr_name) { result = httputil().getMember(attr_name) }
381258

382259
/**
383260
* Provides models for the `tornado.httputil.HttpServerRequest` class
@@ -386,15 +263,7 @@ private module Tornado {
386263
*/
387264
module HttpServerRequest {
388265
/** Gets a reference to the `tornado.httputil.HttpServerRequest` class. */
389-
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
390-
t.start() and
391-
result = httputil_attr("HttpServerRequest")
392-
or
393-
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
394-
}
395-
396-
/** Gets a reference to the `tornado.httputil.HttpServerRequest` class. */
397-
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
266+
API::Node classRef() { result = httputil_attr("HttpServerRequest") }
398267

399268
/**
400269
* A source of instances of `tornado.httputil.HttpServerRequest`, extend this class to model new instances.
@@ -411,7 +280,7 @@ private module Tornado {
411280
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
412281
override CallNode node;
413282

414-
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
283+
ClassInstantiation() { this = classRef().getACall() }
415284
}
416285

417286
/** Gets a reference to an instance of `tornado.httputil.HttpServerRequest`. */

0 commit comments

Comments
 (0)