Skip to content

Commit 5c6d83e

Browse files
authored
Merge pull request #20877 from joefarebrother/python-tornado-websocket
Python: Add models for websocket handlers for Tornado
2 parents 8ecae77 + 7cf3964 commit 5c6d83e

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Additional models for remote flow sources for `tornado.websocket.WebSocketHandler` have been added.

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ module Tornado {
135135
API::Node subclassRef() {
136136
result = web().getMember("RequestHandler").getASubclass*()
137137
or
138+
result = WebSocket::WebSocketHandler::subclassRef()
139+
or
138140
result = ModelOutput::getATypeNode("tornado.web.RequestHandler~Subclass").getASubclass*()
139141
}
140142

@@ -428,6 +430,49 @@ module Tornado {
428430
}
429431
}
430432
}
433+
434+
// ---------------------------------------------------------------------------
435+
// tornado.websocket
436+
// ---------------------------------------------------------------------------
437+
/** Gets a reference to the `tornado.websocket` module. */
438+
API::Node websocket() { result = Tornado::tornado().getMember("websocket") }
439+
440+
/** Provides models for the `tornado.websocket` module */
441+
module WebSocket {
442+
/**
443+
* Provides models for the `tornado.websocket.WebSocketHandler` class and subclasses.
444+
*
445+
* See https://www.tornadoweb.org/en/stable/websocket.html#tornado.websocket.WebSocketHandler.
446+
*/
447+
module WebSocketHandler {
448+
/** Gets a reference to the `tornado.websocket.WebSocketHandler` class or any subclass. */
449+
API::Node subclassRef() {
450+
result = websocket().getMember("WebSocketHandler").getASubclass*()
451+
or
452+
result =
453+
ModelOutput::getATypeNode("tornado.websocket.WebSocketHandler~Subclass").getASubclass*()
454+
}
455+
456+
/** A subclass of `tornado.websocket.WebSocketHandler`. */
457+
class WebSocketHandlerClass extends Web::RequestHandler::RequestHandlerClass {
458+
WebSocketHandlerClass() { this.getParent() = subclassRef().asSource().asExpr() }
459+
460+
override Function getARequestHandler() {
461+
result = super.getARequestHandler()
462+
or
463+
result = this.getAMethod() and
464+
result.getName() = "open"
465+
}
466+
467+
/** Gets a function that could handle incoming WebSocket events, if any. */
468+
Function getAWebSocketEventHandler() {
469+
result = this.getAMethod() and
470+
result.getName() =
471+
["on_message", "on_close", "on_ping", "on_pong", "select_subprotocol", "check_origin"]
472+
}
473+
}
474+
}
475+
}
431476
}
432477

433478
// ---------------------------------------------------------------------------
@@ -542,6 +587,27 @@ module Tornado {
542587
override string getFramework() { result = "Tornado" }
543588
}
544589

590+
/** A request handler for WebSocket events. */
591+
private class TornadoWebSocketEventHandler extends Http::Server::RequestHandler::Range {
592+
TornadoWebSocketEventHandler() {
593+
exists(TornadoModule::WebSocket::WebSocketHandler::WebSocketHandlerClass cls |
594+
cls.getAWebSocketEventHandler() = this
595+
)
596+
}
597+
598+
override Parameter getARoutedParameter() {
599+
// The `open` method is handled as a normal request handler in `TornadoRouteSetup` or `TornadoRequestHandlerWithoutKnownRoute`.
600+
// For other event handlers (such as `on_message`), all parameters should be remote flow sources, as they are not affected by routing.
601+
result in [
602+
this.getArg(_), this.getArgByName(_), this.getVararg().(Parameter),
603+
this.getKwarg().(Parameter)
604+
] and
605+
not result = this.getArg(0)
606+
}
607+
608+
override string getFramework() { result = "Tornado" }
609+
}
610+
545611
// ---------------------------------------------------------------------------
546612
// Response modeling
547613
// ---------------------------------------------------------------------------

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import tornado.web
22
import tornado.routing
3+
import tornado.websocket
34

45

56
class FooHandler(tornado.web.RequestHandler):
@@ -54,6 +55,26 @@ class PossiblyNotRouted(tornado.web.RequestHandler):
5455
def get(self): # $ requestHandler
5556
self.write("NotRouted") # $ HttpResponse
5657

58+
class WebSocket(tornado.websocket.WebSocketHandler):
59+
def open(self, x): # $ requestHandler routedParameter=x
60+
self.write_message("WebSocket open {}".format(x)) # $ MISSING: HttpResponse
61+
62+
def on_message(self, data): # $ requestHandler routedParameter=data
63+
self.write_message("WebSocket on_message {}".format(data)) # $ MISSING: HttpResponse
64+
65+
def on_ping(self, data): # $ requestHandler routedParameter=data
66+
print("ping", data)
67+
68+
def on_pong(self, data): # $ requestHandler routedParameter=data
69+
print("pong", data)
70+
71+
def select_subprotocol(self, subs): # $ requestHandler routedParameter=subs
72+
print("select_subprotocol", subs)
73+
74+
def check_origin(self, origin): # $ requestHandler routedParameter=origin
75+
print("check_origin", origin)
76+
return True
77+
5778

5879
def make_app():
5980
# see https://www.tornadoweb.org/en/stable/routing.html for even more examples
@@ -74,6 +95,7 @@ def make_app():
7495
(tornado.routing.HostMatches(r"(localhost|127\.0\.0\.1)"), [
7596
("/only-localhost", OnlyLocalhost) # $ routeSetup="/only-localhost"
7697
]),
98+
(r"/websocket/([0-9]+)", WebSocket), # $ routeSetup="/websocket/([0-9]+)"
7799

78100
],
79101
debug=True,

0 commit comments

Comments
 (0)