@@ -20,6 +20,19 @@ private import semmle.python.frameworks.Yarl
2020 * See https://docs.aiohttp.org/en/stable/web.html
2121 */
2222module 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 --
2336 /** Gets a reference to a `aiohttp.web.Application` instance. */
2437 API:: Node applicationInstance ( ) {
2538 // Not sure whether you're allowed to add routes _after_ starting the app, for
@@ -36,7 +49,6 @@ module AiohttpWebModel {
3649 result = applicationInstance ( ) .getMember ( "router" )
3750 }
3851
39- // -- route modeling --
4052 /** A route setup in `aiohttp.web` */
4153 abstract class AiohttpRouteSetup extends HTTP:: Server:: RouteSetup:: Range {
4254 override Parameter getARoutedParameter ( ) { none ( ) }
@@ -54,6 +66,50 @@ module AiohttpWebModel {
5466 }
5567 }
5668
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+
57113 /**
58114 * A route-setup from `add_route` or any of `add_get`, `add_post`, etc. on an
59115 * `aiohttp.web.UrlDispatcher`.
@@ -142,6 +198,91 @@ module AiohttpWebModel {
142198 override Function getARequestHandler ( ) { result .getADecorator ( ) = this .asExpr ( ) }
143199 }
144200
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+
145286 // ---------------------------------------------------------------------------
146287 // aiohttp.web.Request taint modeling
147288 // ---------------------------------------------------------------------------
@@ -183,7 +324,7 @@ module AiohttpWebModel {
183324 DataFlow:: ParameterNode {
184325 AiohttpRequestHandlerRequestParam ( ) {
185326 exists ( Function requestHandler |
186- requestHandler = any ( AiohttpRouteSetup setup ) .getARequestHandler ( ) and
327+ requestHandler = any ( AiohttpCoroutineRouteSetup setup ) .getARequestHandler ( ) and
187328 // We select the _last_ parameter for the request since that is what they do in
188329 // `aiohttp-jinja2`.
189330 // https://github.com/aio-libs/aiohttp-jinja2/blob/7fb4daf2c3003921d34031d38c2311ee0e02c18b/aiohttp_jinja2/__init__.py#L235
@@ -200,6 +341,11 @@ module AiohttpWebModel {
200341 this .getParameter ( ) =
201342 max ( Parameter param , int i | param = requestHandler .getArg ( i ) | param order by i )
202343 )
344+ or
345+ exists ( AiohttpViewClass vc |
346+ // TODO
347+ none ( )
348+ )
203349 }
204350
205351 override string getSourceType ( ) { result = "aiohttp.web.Request" }
0 commit comments