Skip to content

Commit 812ea5d

Browse files
committed
Python: Tornado: Model request handlers without known route
1 parent 1849b9e commit 812ea5d

File tree

3 files changed

+31
-7
lines changed

3 files changed

+31
-7
lines changed

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ private module Tornado {
142142
class RequestHandlerClass extends Class {
143143
RequestHandlerClass() { this.getParent() = subclassRef().asExpr() }
144144

145+
/** Gets a function that could handle incoming requests, if any. */
146+
Function getARequestHandler() {
147+
// TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
148+
// points-to and `.lookup`, which would handle `post = my_post_handler` inside class def
149+
result = this.getAMethod() and
150+
result.getName() = HTTP::httpVerbLower()
151+
}
152+
145153
/** Gets a reference to this class. */
146154
private DataFlow::Node getARef(DataFlow::TypeTracker t) {
147155
t.start() and
@@ -489,9 +497,7 @@ private module Tornado {
489497
override Function getARequestHandler() {
490498
exists(tornado::web::RequestHandler::RequestHandlerClass cls |
491499
cls.getARef().asCfgNode() = node.getElement(1) and
492-
// TODO: Proper MRO
493-
result = cls.getAMethod() and
494-
result.getName() = HTTP::httpVerbLower()
500+
result = cls.getARequestHandler()
495501
)
496502
}
497503

@@ -516,4 +522,22 @@ private module Tornado {
516522
)
517523
}
518524
}
525+
526+
/** A request handler defined in a tornado RequestHandler class, that has no known route. */
527+
private class TornadoRequestHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range {
528+
TornadoRequestHandlerWithoutKnownRoute() {
529+
exists(tornado::web::RequestHandler::RequestHandlerClass cls |
530+
cls.getARequestHandler() = this
531+
) and
532+
not exists(TornadoRouteSetup setup | setup.getARequestHandler() = this)
533+
}
534+
535+
override Parameter getARoutedParameter() {
536+
// Since we don't know the URL pattern, we simply mark all parameters as a routed
537+
// parameter. This should give us more RemoteFlowSources but could also lead to
538+
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
539+
result in [this.getArg(_), this.getArgByName(_)] and
540+
not result = this.getArg(0)
541+
}
542+
}
519543
}

python/ql/test/experimental/library-tests/frameworks/tornado/basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def get(self): # $ requestHandler
2929

3030

3131
class BaseReverseInheritance(tornado.web.RequestHandler):
32-
def get(self): # $ MISSING: requestHandler
32+
def get(self): # $ requestHandler
3333
self.write("hello from BaseReverseInheritance")
3434

3535

python/ql/test/experimental/library-tests/frameworks/tornado/routing_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ def get(self, x, y=None, not_used=None): # $ requestHandler routedParameter=x r
88

99

1010
class BarHandler(tornado.web.RequestHandler):
11-
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
11+
def get(self, x, y=None, not_used=None): # $ requestHandler routedParameter=x routedParameter=y SPURIOUS: routedParameter=not_used
1212
self.write("BarHandler {} {}".format(x, y))
1313

1414

1515
class BazHandler(tornado.web.RequestHandler):
16-
def get(self, x, y=None, not_used=None): # $ MISSING: requestHandler routedParameter=x routedParameter=y
16+
def get(self, x, y=None, not_used=None): # $ requestHandler routedParameter=x routedParameter=y SPURIOUS: routedParameter=not_used
1717
self.write("BazHandler {} {}".format(x, y))
1818

1919

@@ -51,7 +51,7 @@ class PossiblyNotRouted(tornado.web.RequestHandler):
5151
# Even if our analysis can't find a route-setup for this class, we should still
5252
# consider it to be a handle incoming HTTP requests
5353

54-
def get(self): # $ MISSING: requestHandler
54+
def get(self): # $ requestHandler
5555
self.write("NotRouted")
5656

5757

0 commit comments

Comments
 (0)