@@ -20,6 +20,19 @@ private import semmle.python.frameworks.Yarl
20
20
* See https://docs.aiohttp.org/en/stable/web.html
21
21
*/
22
22
module AiohttpWebModel {
23
+ /**
24
+ * Provides models for the `aiohttp.web.View` class and subclasses.
25
+ *
26
+ * See https://docs.aiohttp.org/en/stable/web_reference.html#view.
27
+ */
28
+ module View {
29
+ /** Gets a reference to the `flask.views.View` class or any subclass. */
30
+ API:: Node subclassRef ( ) {
31
+ result = API:: moduleImport ( "aiohttp" ) .getMember ( "web" ) .getMember ( "View" ) .getASubclass * ( )
32
+ }
33
+ }
34
+
35
+ // -- route modeling --
23
36
/** Gets a reference to a `aiohttp.web.Application` instance. */
24
37
API:: Node applicationInstance ( ) {
25
38
// Not sure whether you're allowed to add routes _after_ starting the app, for
@@ -36,7 +49,6 @@ module AiohttpWebModel {
36
49
result = applicationInstance ( ) .getMember ( "router" )
37
50
}
38
51
39
- // -- route modeling --
40
52
/** A route setup in `aiohttp.web` */
41
53
abstract class AiohttpRouteSetup extends HTTP:: Server:: RouteSetup:: Range {
42
54
override Parameter getARoutedParameter ( ) { none ( ) }
@@ -54,6 +66,50 @@ module AiohttpWebModel {
54
66
}
55
67
}
56
68
69
+ /**
70
+ * Gets a reference to a class, that has been backtracked from the view-class handler
71
+ * argument `origin` (to a route-setup for view-classes).
72
+ */
73
+ private DataFlow:: LocalSourceNode viewClassBackTracker (
74
+ DataFlow:: TypeBackTracker t , DataFlow:: Node origin
75
+ ) {
76
+ t .start ( ) and
77
+ origin = any ( AiohttpViewRouteSetup rs ) .getViewClassArg ( ) and
78
+ result = origin .getALocalSource ( )
79
+ or
80
+ exists ( DataFlow:: TypeBackTracker t2 |
81
+ result = viewClassBackTracker ( t2 , origin ) .backtrack ( t2 , t )
82
+ )
83
+ }
84
+
85
+ /**
86
+ * Gets a reference to a class, that has been backtracked from the view-class handler
87
+ * argument `origin` (to a route-setup for view-classes).
88
+ */
89
+ DataFlow:: LocalSourceNode viewClassBackTracker ( DataFlow:: Node origin ) {
90
+ result = viewClassBackTracker ( DataFlow:: TypeBackTracker:: end ( ) , origin )
91
+ }
92
+
93
+ Class getBackTrackedViewClass ( DataFlow:: Node origin ) {
94
+ result .getParent ( ) = viewClassBackTracker ( origin ) .asExpr ( )
95
+ }
96
+
97
+ /** An aiohttp route setup that uses view-classes as request handlers. */
98
+ abstract class AiohttpViewRouteSetup extends AiohttpRouteSetup {
99
+ /** Gets the argument specifying the view-class handler. */
100
+ abstract DataFlow:: Node getViewClassArg ( ) ;
101
+
102
+ /** Gets the view-class that is referenced in the view-class handler argument. */
103
+ Class getViewClass ( ) { result = getBackTrackedViewClass ( this .getViewClassArg ( ) ) }
104
+
105
+ override Function getARequestHandler ( ) {
106
+ exists ( AiohttpViewClass cls |
107
+ cls = this .getViewClass ( ) and
108
+ result = cls .getARequestHandler ( )
109
+ )
110
+ }
111
+ }
112
+
57
113
/**
58
114
* A route-setup from `add_route` or any of `add_get`, `add_post`, etc. on an
59
115
* `aiohttp.web.UrlDispatcher`.
@@ -142,6 +198,91 @@ module AiohttpWebModel {
142
198
override Function getARequestHandler ( ) { result .getADecorator ( ) = this .asExpr ( ) }
143
199
}
144
200
201
+ /**
202
+ * A view-class route-setup from either:
203
+ * - `add_view` method on a `aiohttp.web.UrlDispatcher`
204
+ * - `view` function from `aiohttp.web`
205
+ */
206
+ class AiohttpViewRouteSetupFromFunction extends AiohttpViewRouteSetup , DataFlow:: CallCfgNode {
207
+ AiohttpViewRouteSetupFromFunction ( ) {
208
+ this = urlDispathcerInstance ( ) .getMember ( "add_view" ) .getACall ( )
209
+ or
210
+ this = API:: moduleImport ( "aiohttp" ) .getMember ( "web" ) .getMember ( "view" ) .getACall ( )
211
+ or
212
+ this =
213
+ API:: moduleImport ( "aiohttp" )
214
+ .getMember ( "web" )
215
+ .getMember ( "RouteTableDef" )
216
+ .getReturn ( )
217
+ .getMember ( "view" )
218
+ .getACall ( )
219
+ }
220
+
221
+ override DataFlow:: Node getUrlPatternArg ( ) {
222
+ result in [ this .getArg ( 0 ) , this .getArgByName ( "path" ) ]
223
+ }
224
+
225
+ override DataFlow:: Node getViewClassArg ( ) {
226
+ result in [ this .getArg ( 1 ) , this .getArgByName ( "handler" ) ]
227
+ }
228
+ }
229
+
230
+ /**
231
+ * A view-class route-setup from the `view` decorator from a `aiohttp.web.RouteTableDef`.
232
+ */
233
+ class AiohttpViewRouteSetupFromDecorator extends AiohttpViewRouteSetup , DataFlow:: CallCfgNode {
234
+ AiohttpViewRouteSetupFromDecorator ( ) {
235
+ this =
236
+ API:: moduleImport ( "aiohttp" )
237
+ .getMember ( "web" )
238
+ .getMember ( "RouteTableDef" )
239
+ .getReturn ( )
240
+ .getMember ( "view" )
241
+ .getACall ( )
242
+ }
243
+
244
+ override DataFlow:: Node getUrlPatternArg ( ) {
245
+ result in [ this .getArg ( 0 ) , this .getArgByName ( "path" ) ]
246
+ }
247
+
248
+ override DataFlow:: Node getViewClassArg ( ) { none ( ) }
249
+
250
+ override Class getViewClass ( ) { result .getADecorator ( ) = this .asExpr ( ) }
251
+ }
252
+
253
+ /** A class that we consider a aiohttp.web View class. */
254
+ abstract class AiohttpViewClass extends Class {
255
+ /** Gets a function that could handle incoming requests, if any. */
256
+ Function getARequestHandler ( ) {
257
+ // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
258
+ // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def
259
+ result = this .getAMethod ( ) and
260
+ result .getName ( ) = HTTP:: httpVerbLower ( )
261
+ }
262
+ }
263
+
264
+ /** A class that has a super-type which is a aiohttp.web View class. */
265
+ class AiohttpViewClassFromSuperClass extends AiohttpViewClass {
266
+ AiohttpViewClassFromSuperClass ( ) { this .getABase ( ) = View:: subclassRef ( ) .getAUse ( ) .asExpr ( ) }
267
+ }
268
+
269
+ /** A class that is used in a route-setup, therefore being considered a aiohttp.web View class. */
270
+ class AiohttpViewClassFromRouteSetup extends AiohttpViewClass {
271
+ AiohttpViewClassFromRouteSetup ( ) { this = any ( AiohttpViewRouteSetup rs ) .getViewClass ( ) }
272
+ }
273
+
274
+ /** A request handler defined in an `aiohttp.web` view class, that has no known route. */
275
+ private class AiohttpViewClassRequestHandlerWithoutKnownRoute extends HTTP:: Server:: RequestHandler:: Range {
276
+ AiohttpViewClassRequestHandlerWithoutKnownRoute ( ) {
277
+ exists ( AiohttpViewClass vc | vc .getARequestHandler ( ) = this ) and
278
+ not exists ( AiohttpRouteSetup setup | setup .getARequestHandler ( ) = this )
279
+ }
280
+
281
+ override Parameter getARoutedParameter ( ) { none ( ) }
282
+
283
+ override string getFramework ( ) { result = "aiohttp.web" }
284
+ }
285
+
145
286
// ---------------------------------------------------------------------------
146
287
// aiohttp.web.Request taint modeling
147
288
// ---------------------------------------------------------------------------
@@ -183,7 +324,7 @@ module AiohttpWebModel {
183
324
DataFlow:: ParameterNode {
184
325
AiohttpRequestHandlerRequestParam ( ) {
185
326
exists ( Function requestHandler |
186
- requestHandler = any ( AiohttpRouteSetup setup ) .getARequestHandler ( ) and
327
+ requestHandler = any ( AiohttpCoroutineRouteSetup setup ) .getARequestHandler ( ) and
187
328
// We select the _last_ parameter for the request since that is what they do in
188
329
// `aiohttp-jinja2`.
189
330
// https://github.com/aio-libs/aiohttp-jinja2/blob/7fb4daf2c3003921d34031d38c2311ee0e02c18b/aiohttp_jinja2/__init__.py#L235
@@ -200,6 +341,11 @@ module AiohttpWebModel {
200
341
this .getParameter ( ) =
201
342
max ( Parameter param , int i | param = requestHandler .getArg ( i ) | param order by i )
202
343
)
344
+ or
345
+ exists ( AiohttpViewClass vc |
346
+ // TODO
347
+ none ( )
348
+ )
203
349
}
204
350
205
351
override string getSourceType ( ) { result = "aiohttp.web.Request" }
0 commit comments