@@ -2074,7 +2074,11 @@ private module Django {
20742074 // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
20752075 // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def
20762076 result = this .getAMethod ( ) and
2077- result .getName ( ) = HTTP:: httpVerbLower ( )
2077+ (
2078+ result .getName ( ) = HTTP:: httpVerbLower ( )
2079+ or
2080+ result .getName ( ) = "get_redirect_url"
2081+ )
20782082 }
20792083
20802084 /**
@@ -2124,6 +2128,8 @@ private module Django {
21242128 /**
21252129 * A function that is a django route handler, meaning it handles incoming requests
21262130 * with the django framework.
2131+ *
2132+ * Most functions take a django HttpRequest as a parameter (but not all).
21272133 */
21282134 private class DjangoRouteHandler extends Function {
21292135 DjangoRouteHandler ( ) {
@@ -2132,6 +2138,12 @@ private module Django {
21322138 any ( DjangoViewClass vc ) .getARequestHandler ( ) = this
21332139 }
21342140
2141+ /**
2142+ * Gets the index of the parameter where the first routed parameter can be passed --
2143+ * that is, the one just after any possible `self` or HttpRequest parameters.
2144+ */
2145+ int getFirstPossibleRoutedParamIndex ( ) { result = 1 + this .getRequestParamIndex ( ) }
2146+
21352147 /** Gets the index of the request parameter. */
21362148 int getRequestParamIndex ( ) {
21372149 not this .isMethod ( ) and
@@ -2145,6 +2157,26 @@ private module Django {
21452157 Parameter getRequestParam ( ) { result = this .getArg ( this .getRequestParamIndex ( ) ) }
21462158 }
21472159
2160+ /**
2161+ * A method named `get_redirect_url` on a django view class.
2162+ *
2163+ * See https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#django.views.generic.base.RedirectView.get_redirect_url
2164+ *
2165+ * Note: this function only does something on a subclass of `RedirectView`, but since
2166+ * classes can be considered django view classes without us knowing their super-classes,
2167+ * we need to consider _any_ django view class. I don't expect any problems to come from this.
2168+ */
2169+ private class GetRedirectUrlFunction extends DjangoRouteHandler {
2170+ GetRedirectUrlFunction ( ) {
2171+ this .getName ( ) = "get_redirect_url" and
2172+ any ( DjangoViewClass vc ) .getARequestHandler ( ) = this
2173+ }
2174+
2175+ override int getFirstPossibleRoutedParamIndex ( ) { result = 1 }
2176+
2177+ override int getRequestParamIndex ( ) { none ( ) }
2178+ }
2179+
21482180 /** A data-flow node that sets up a route on a server, using the django framework. */
21492181 abstract private class DjangoRouteSetup extends HTTP:: Server:: RouteSetup:: Range , DataFlow:: CfgNode {
21502182 /** Gets the data-flow node that is used as the argument for the view handler. */
@@ -2173,7 +2205,7 @@ private module Django {
21732205 // parameter. This should give us more RemoteFlowSources but could also lead to
21742206 // more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
21752207 result in [ this .getArg ( _) , this .getArgByName ( _) ] and
2176- not result = any ( int i | i <= this .getRequestParamIndex ( ) | this .getArg ( i ) )
2208+ not result = any ( int i | i < this .getFirstPossibleRoutedParamIndex ( ) | this .getArg ( i ) )
21772209 }
21782210 }
21792211
@@ -2211,7 +2243,8 @@ private module Django {
22112243 exists ( DjangoRouteHandler routeHandler | routeHandler = this .getARequestHandler ( ) |
22122244 not exists ( this .getUrlPattern ( ) ) and
22132245 result in [ routeHandler .getArg ( _) , routeHandler .getArgByName ( _) ] and
2214- not result = any ( int i | i <= routeHandler .getRequestParamIndex ( ) | routeHandler .getArg ( i ) )
2246+ not result =
2247+ any ( int i | i < routeHandler .getFirstPossibleRoutedParamIndex ( ) | routeHandler .getArg ( i ) )
22152248 )
22162249 or
22172250 exists ( string name |
@@ -2233,7 +2266,8 @@ private module Django {
22332266 exists ( DjangoRouteHandler routeHandler | routeHandler = this .getARequestHandler ( ) |
22342267 not exists ( this .getUrlPattern ( ) ) and
22352268 result in [ routeHandler .getArg ( _) , routeHandler .getArgByName ( _) ] and
2236- not result = any ( int i | i <= routeHandler .getRequestParamIndex ( ) | routeHandler .getArg ( i ) )
2269+ not result =
2270+ any ( int i | i < routeHandler .getFirstPossibleRoutedParamIndex ( ) | routeHandler .getArg ( i ) )
22372271 )
22382272 or
22392273 exists ( DjangoRouteHandler routeHandler , DjangoRouteRegex regex |
@@ -2245,7 +2279,9 @@ private module Django {
22452279 not exists ( regex .getGroupName ( _, _) ) and
22462280 // first group will have group number 1
22472281 result =
2248- routeHandler .getArg ( routeHandler .getRequestParamIndex ( ) + regex .getGroupNumber ( _, _) )
2282+ routeHandler
2283+ .getArg ( routeHandler .getFirstPossibleRoutedParamIndex ( ) - 1 +
2284+ regex .getGroupNumber ( _, _) )
22492285 or
22502286 result = routeHandler .getArgByName ( regex .getGroupName ( _, _) )
22512287 )
@@ -2441,4 +2477,31 @@ private module Django {
24412477
24422478 override string getMimetypeDefault ( ) { none ( ) }
24432479 }
2480+
2481+ // ---------------------------------------------------------------------------
2482+ // RedirectView handling
2483+ // ---------------------------------------------------------------------------
2484+ /**
2485+ * A return from a method named `get_redirect_url` on a django view class.
2486+ *
2487+ * Note that in reality, this only does something on a subclass of `RedirectView` --
2488+ * but until API graphs makes this easy to model, I took a shortcut in modeling
2489+ * preciseness.
2490+ *
2491+ * See https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#redirectview
2492+ */
2493+ private class DjangoRedirectViewGetRedirectUrlReturn extends HTTP:: Server:: HttpRedirectResponse:: Range ,
2494+ DataFlow:: CfgNode {
2495+ DjangoRedirectViewGetRedirectUrlReturn ( ) {
2496+ node = any ( GetRedirectUrlFunction f ) .getAReturnValueFlowNode ( )
2497+ }
2498+
2499+ override DataFlow:: Node getRedirectLocation ( ) { result = this }
2500+
2501+ override DataFlow:: Node getBody ( ) { none ( ) }
2502+
2503+ override DataFlow:: Node getMimetypeOrContentTypeArg ( ) { none ( ) }
2504+
2505+ override string getMimetypeDefault ( ) { none ( ) }
2506+ }
24442507}
0 commit comments