@@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow
8
8
private import semmle.python.dataflow.new.RemoteFlowSources
9
9
private import semmle.python.dataflow.new.TaintTracking
10
10
private import semmle.python.Concepts
11
+ private import semmle.python.regex
11
12
12
13
/**
13
14
* Provides models for the `tornado` PyPI package.
@@ -82,7 +83,7 @@ private module Tornado {
82
83
* WARNING: Only holds for a few predefined attributes.
83
84
*/
84
85
private DataFlow:: Node web_attr ( DataFlow:: TypeTracker t , string attr_name ) {
85
- attr_name in [ "RequestHandler" ] and
86
+ attr_name in [ "RequestHandler" , "Application" ] and
86
87
(
87
88
t .start ( ) and
88
89
result = DataFlow:: importNode ( "tornado.web" + "." + attr_name )
@@ -138,8 +139,19 @@ private module Tornado {
138
139
DataFlow:: Node subclassRef ( ) { result = subclassRef ( DataFlow:: TypeTracker:: end ( ) ) }
139
140
140
141
/** A RequestHandler class (most likely in project code). */
141
- private class RequestHandlerClass extends Class {
142
+ class RequestHandlerClass extends Class {
142
143
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 ( ) ) }
143
155
}
144
156
145
157
/**
@@ -229,6 +241,64 @@ private module Tornado {
229
241
}
230
242
}
231
243
}
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
+ }
232
302
}
233
303
234
304
// -------------------------------------------------------------------------
@@ -366,4 +436,84 @@ private module Tornado {
366
436
}
367
437
}
368
438
}
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
+ }
369
519
}
0 commit comments