@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
88private import semmle.python.dataflow.new.RemoteFlowSources
99private import semmle.python.dataflow.new.TaintTracking
1010private import semmle.python.Concepts
11+ private import semmle.python.regex
1112
1213/**
1314 * Provides models for the `tornado` PyPI package.
@@ -82,7 +83,7 @@ private module Tornado {
8283 * WARNING: Only holds for a few predefined attributes.
8384 */
8485 private DataFlow:: Node web_attr ( DataFlow:: TypeTracker t , string attr_name ) {
85- attr_name in [ "RequestHandler" ] and
86+ attr_name in [ "RequestHandler" , "Application" ] and
8687 (
8788 t .start ( ) and
8889 result = DataFlow:: importNode ( "tornado.web" + "." + attr_name )
@@ -138,8 +139,19 @@ private module Tornado {
138139 DataFlow:: Node subclassRef ( ) { result = subclassRef ( DataFlow:: TypeTracker:: end ( ) ) }
139140
140141 /** A RequestHandler class (most likely in project code). */
141- private class RequestHandlerClass extends Class {
142+ class RequestHandlerClass extends Class {
142143 RequestHandlerClass ( ) { this .getParent ( ) = subclassRef ( ) .asExpr ( ) }
144+
145+ /** Gets a reference to this class. */
146+ private DataFlow:: Node getARef ( DataFlow:: TypeTracker t ) {
147+ t .start ( ) and
148+ result .asExpr ( ) .( ClassExpr ) = this .getParent ( )
149+ or
150+ exists ( DataFlow:: TypeTracker t2 | result = this .getARef ( t2 ) .track ( t2 , t ) )
151+ }
152+
153+ /** Gets a reference to this class. */
154+ DataFlow:: Node getARef ( ) { result = this .getARef ( DataFlow:: TypeTracker:: end ( ) ) }
143155 }
144156
145157 /**
@@ -229,6 +241,64 @@ private module Tornado {
229241 }
230242 }
231243 }
244+
245+ /**
246+ * Provides models for the `tornado.web.Application` class
247+ *
248+ * See https://www.tornadoweb.org/en/stable/web.html#tornado.web.Application.
249+ */
250+ module Application {
251+ /** Gets a reference to the `tornado.web.Application` class. */
252+ private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
253+ t .start ( ) and
254+ result = web_attr ( "Application" )
255+ or
256+ exists ( DataFlow:: TypeTracker t2 | result = classRef ( t2 ) .track ( t2 , t ) )
257+ }
258+
259+ /** Gets a reference to the `tornado.web.Application` class. */
260+ DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
261+
262+ /**
263+ * A source of instances of `tornado.web.Application`, extend this class to model new instances.
264+ *
265+ * This can include instantiations of the class, return values from function
266+ * calls, or a special parameter that will be set when functions are called by an external
267+ * library.
268+ *
269+ * Use the predicate `Application::instance()` to get references to instances of `tornado.web.Application`.
270+ */
271+ abstract class InstanceSource extends DataFlow:: Node { }
272+
273+ /** A direct instantiation of `tornado.web.Application`. */
274+ class ClassInstantiation extends InstanceSource , DataFlow:: CfgNode {
275+ override CallNode node ;
276+
277+ ClassInstantiation ( ) { node .getFunction ( ) = classRef ( ) .asCfgNode ( ) }
278+ }
279+
280+ /** Gets a reference to an instance of `tornado.web.Application`. */
281+ private DataFlow:: Node instance ( DataFlow:: TypeTracker t ) {
282+ t .start ( ) and
283+ result instanceof InstanceSource
284+ or
285+ exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
286+ }
287+
288+ /** Gets a reference to an instance of `tornado.web.Application`. */
289+ DataFlow:: Node instance ( ) { result = instance ( DataFlow:: TypeTracker:: end ( ) ) }
290+
291+ /** Gets a reference to the `add_handlers` method. */
292+ private DataFlow:: Node add_handlers ( DataFlow:: TypeTracker t ) {
293+ t .startInAttr ( "add_handlers" ) and
294+ result = instance ( )
295+ or
296+ exists ( DataFlow:: TypeTracker t2 | result = add_handlers ( t2 ) .track ( t2 , t ) )
297+ }
298+
299+ /** Gets a reference to the `add_handlers` method. */
300+ DataFlow:: Node add_handlers ( ) { result = add_handlers ( DataFlow:: TypeTracker:: end ( ) ) }
301+ }
232302 }
233303
234304 // -------------------------------------------------------------------------
@@ -366,4 +436,84 @@ private module Tornado {
366436 }
367437 }
368438 }
439+
440+ // ---------------------------------------------------------------------------
441+ // routing
442+ // ---------------------------------------------------------------------------
443+ /** A sequence that defines a number of route rules */
444+ SequenceNode routeSetupRuleList ( ) {
445+ exists ( CallNode call | call = any ( tornado:: web:: Application:: ClassInstantiation c ) .asCfgNode ( ) |
446+ result in [ call .getArg ( 0 ) , call .getArgByName ( "handlers" ) ]
447+ )
448+ or
449+ exists ( CallNode call |
450+ call .getFunction ( ) = tornado:: web:: Application:: add_handlers ( ) .asCfgNode ( )
451+ |
452+ result in [ call .getArg ( 1 ) , call .getArgByName ( "host_handlers" ) ]
453+ )
454+ or
455+ result = routeSetupRuleList ( ) .getElement ( _) .( TupleNode ) .getElement ( 1 )
456+ }
457+
458+ /** A tornado route setup. */
459+ abstract class TornadoRouteSetup extends HTTP:: Server:: RouteSetup:: Range { }
460+
461+ /**
462+ * A regex that is used to set up a route.
463+ *
464+ * Needs this subclass to be considered a RegexString.
465+ */
466+ private class TornadoRouteRegex extends RegexString {
467+ TornadoRouteSetup setup ;
468+
469+ TornadoRouteRegex ( ) {
470+ this instanceof StrConst and
471+ DataFlow:: localFlow ( DataFlow:: exprNode ( this ) , setup .getUrlPatternArg ( ) )
472+ }
473+
474+ TornadoRouteSetup getRouteSetup ( ) { result = setup }
475+ }
476+
477+ /** A route setup using a tuple. */
478+ private class TornadoTupleRouteSetup extends TornadoRouteSetup , DataFlow:: CfgNode {
479+ override TupleNode node ;
480+
481+ TornadoTupleRouteSetup ( ) {
482+ node = routeSetupRuleList ( ) .getElement ( _) and
483+ count ( node .getElement ( _) ) = 2 and
484+ not node .getElement ( 1 ) instanceof SequenceNode
485+ }
486+
487+ override DataFlow:: Node getUrlPatternArg ( ) { result .asCfgNode ( ) = node .getElement ( 0 ) }
488+
489+ override Function getARequestHandler ( ) {
490+ exists ( tornado:: web:: RequestHandler:: RequestHandlerClass cls |
491+ cls .getARef ( ) .asCfgNode ( ) = node .getElement ( 1 ) and
492+ // TODO: Proper MRO
493+ result = cls .getAMethod ( ) and
494+ result .getName ( ) = HTTP:: httpVerbLower ( )
495+ )
496+ }
497+
498+ override Parameter getARoutedParameter ( ) {
499+ // If we don't know the URL pattern, we simply mark all parameters as a routed
500+ // parameter. This should give us more RemoteFlowSources but could also lead to
501+ // more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
502+ exists ( Function requestHandler | requestHandler = this .getARequestHandler ( ) |
503+ not exists ( this .getUrlPattern ( ) ) and
504+ result in [ requestHandler .getArg ( _) , requestHandler .getArgByName ( _) ] and
505+ not result = requestHandler .getArg ( 0 )
506+ )
507+ or
508+ exists ( Function requestHandler , TornadoRouteRegex regex |
509+ requestHandler = this .getARequestHandler ( ) and
510+ regex .getRouteSetup ( ) = this
511+ |
512+ // first group will have group number 1
513+ result = requestHandler .getArg ( regex .getGroupNumber ( _, _) )
514+ or
515+ result = requestHandler .getArgByName ( regex .getGroupName ( _, _) )
516+ )
517+ }
518+ }
369519}
0 commit comments