@@ -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.ApiGraphs
1112private import semmle.python.frameworks.PEP249
1213private import semmle.python.regex
1314
@@ -1975,6 +1976,205 @@ private module Django {
19751976 }
19761977 }
19771978
1979+ /** Provides models for django forms (defined in the `django.forms` module) */
1980+ module Forms {
1981+ /**
1982+ * Provides models for the `django.forms.forms.BaseForm` class and subclasses. This
1983+ * is usually used by the `django.forms.forms.Form` class, which is also available
1984+ * under the more commonly used alias `django.forms.Form`.
1985+ *
1986+ * See https://docs.djangoproject.com/en/3.1/ref/forms/api/
1987+ */
1988+ module Form {
1989+ /** Gets a reference to the `django.forms.forms.BaseForm` class or any subclass. */
1990+ API:: Node subclassRef ( ) {
1991+ // canonical definition
1992+ result =
1993+ API:: moduleImport ( "django" )
1994+ .getMember ( "forms" )
1995+ .getMember ( "forms" )
1996+ .getMember ( [ "BaseForm" , "Form" ] )
1997+ .getASubclass * ( )
1998+ or
1999+ result =
2000+ API:: moduleImport ( "django" )
2001+ .getMember ( "forms" )
2002+ .getMember ( "models" )
2003+ .getMember ( [ "BaseModelForm" , "ModelForm" ] )
2004+ .getASubclass * ( )
2005+ or
2006+ // aliases from `django.forms`
2007+ result =
2008+ API:: moduleImport ( "django" )
2009+ .getMember ( "forms" )
2010+ .getMember ( [ "BaseForm" , "Form" , "BaseModelForm" , "ModelForm" ] )
2011+ .getASubclass * ( )
2012+ or
2013+ // other Form subclasses defined in Django
2014+ result =
2015+ API:: moduleImport ( "django" )
2016+ .getMember ( "contrib" )
2017+ .getMember ( "admin" )
2018+ .getMember ( "forms" )
2019+ .getMember ( [ "AdminAuthenticationForm" , "AdminPasswordChangeForm" ] )
2020+ .getASubclass * ( )
2021+ or
2022+ result =
2023+ API:: moduleImport ( "django" )
2024+ .getMember ( "contrib" )
2025+ .getMember ( "admin" )
2026+ .getMember ( "helpers" )
2027+ .getMember ( "ActionForm" )
2028+ .getASubclass * ( )
2029+ or
2030+ result =
2031+ API:: moduleImport ( "django" )
2032+ .getMember ( "contrib" )
2033+ .getMember ( "admin" )
2034+ .getMember ( "views" )
2035+ .getMember ( "main" )
2036+ .getMember ( "ChangeListSearchForm" )
2037+ .getASubclass * ( )
2038+ or
2039+ result =
2040+ API:: moduleImport ( "django" )
2041+ .getMember ( "contrib" )
2042+ .getMember ( "auth" )
2043+ .getMember ( "forms" )
2044+ .getMember ( [
2045+ "PasswordResetForm" , "UserChangeForm" , "SetPasswordForm" ,
2046+ "AdminPasswordChangeForm" , "PasswordChangeForm" , "AuthenticationForm" ,
2047+ "UserCreationForm"
2048+ ] )
2049+ .getASubclass * ( )
2050+ or
2051+ result =
2052+ API:: moduleImport ( "django" )
2053+ .getMember ( "contrib" )
2054+ .getMember ( "flatpages" )
2055+ .getMember ( "forms" )
2056+ .getMember ( "FlatpageForm" )
2057+ .getASubclass * ( )
2058+ or
2059+ result =
2060+ API:: moduleImport ( "django" )
2061+ .getMember ( "forms" )
2062+ .getMember ( "formsets" )
2063+ .getMember ( "ManagementForm" )
2064+ .getASubclass * ( )
2065+ or
2066+ result =
2067+ API:: moduleImport ( "django" )
2068+ .getMember ( "forms" )
2069+ .getMember ( "models" )
2070+ .getMember ( [ "ModelForm" , "BaseModelForm" ] )
2071+ .getASubclass * ( )
2072+ }
2073+ }
2074+
2075+ /**
2076+ * Provides models for the `django.forms.fields.Field` class and subclasses. This is
2077+ * also available under the more commonly used alias `django.forms.Field`.
2078+ *
2079+ * See https://docs.djangoproject.com/en/3.1/ref/forms/fields/
2080+ */
2081+ module Field {
2082+ /** Gets a reference to the `django.forms.fields.Field` class or any subclass. */
2083+ API:: Node subclassRef ( ) {
2084+ exists ( string modName , string clsName |
2085+ // canonical definition
2086+ result =
2087+ API:: moduleImport ( "django" )
2088+ .getMember ( "forms" )
2089+ .getMember ( modName )
2090+ .getMember ( clsName )
2091+ .getASubclass * ( )
2092+ or
2093+ // alias from `django.forms`
2094+ result = API:: moduleImport ( "django" ) .getMember ( "forms" ) .getMember ( clsName ) .getASubclass * ( )
2095+ |
2096+ modName = "fields" and
2097+ clsName in [
2098+ "Field" ,
2099+ // Known subclasses
2100+ "BooleanField" , "IntegerField" , "CharField" , "SlugField" , "DateTimeField" ,
2101+ "EmailField" , "DateField" , "TimeField" , "DurationField" , "DecimalField" , "FloatField" ,
2102+ "GenericIPAddressField" , "UUIDField" , "JSONField" , "FilePathField" ,
2103+ "NullBooleanField" , "URLField" , "TypedChoiceField" , "FileField" , "ImageField" ,
2104+ "RegexField" , "ChoiceField" , "MultipleChoiceField" , "ComboField" , "MultiValueField" ,
2105+ "SplitDateTimeField" , "TypedMultipleChoiceField" , "BaseTemporalField"
2106+ ]
2107+ or
2108+ // Known subclasses from `django.forms.models`
2109+ modName = "models" and
2110+ clsName in [ "ModelChoiceField" , "ModelMultipleChoiceField" , "InlineForeignKeyField" ]
2111+ )
2112+ or
2113+ // other Field subclasses defined in Django
2114+ result =
2115+ API:: moduleImport ( "django" )
2116+ .getMember ( "contrib" )
2117+ .getMember ( "auth" )
2118+ .getMember ( "forms" )
2119+ .getMember ( [ "ReadOnlyPasswordHashField" , "UsernameField" ] )
2120+ .getASubclass * ( )
2121+ or
2122+ result =
2123+ API:: moduleImport ( "django" )
2124+ .getMember ( "contrib" )
2125+ .getMember ( "gis" )
2126+ .getMember ( "forms" )
2127+ .getMember ( "fields" )
2128+ .getMember ( [
2129+ "GeometryCollectionField" , "GeometryField" , "LineStringField" ,
2130+ "MultiLineStringField" , "MultiPointField" , "MultiPolygonField" , "PointField" ,
2131+ "PolygonField"
2132+ ] )
2133+ .getASubclass * ( )
2134+ or
2135+ result =
2136+ API:: moduleImport ( "django" )
2137+ .getMember ( "contrib" )
2138+ .getMember ( "postgres" )
2139+ .getMember ( "forms" )
2140+ .getMember ( "array" )
2141+ .getMember ( [ "SimpleArrayField" , "SplitArrayField" ] )
2142+ .getASubclass * ( )
2143+ or
2144+ result =
2145+ API:: moduleImport ( "django" )
2146+ .getMember ( "contrib" )
2147+ .getMember ( "postgres" )
2148+ .getMember ( "forms" )
2149+ .getMember ( "hstore" )
2150+ .getMember ( "HStoreField" )
2151+ .getASubclass * ( )
2152+ or
2153+ result =
2154+ API:: moduleImport ( "django" )
2155+ .getMember ( "contrib" )
2156+ .getMember ( "postgres" )
2157+ .getMember ( "forms" )
2158+ .getMember ( "ranges" )
2159+ .getMember ( [
2160+ "BaseRangeField" , "DateRangeField" , "DateTimeRangeField" , "DecimalRangeField" ,
2161+ "IntegerRangeField"
2162+ ] )
2163+ .getASubclass * ( )
2164+ or
2165+ result =
2166+ API:: moduleImport ( "django" )
2167+ .getMember ( "forms" )
2168+ .getMember ( "models" )
2169+ .getMember ( [ "InlineForeignKeyField" , "ModelChoiceField" , "ModelMultipleChoiceField" ] )
2170+ .getASubclass * ( )
2171+ }
2172+ }
2173+ }
2174+
2175+ // ---------------------------------------------------------------------------
2176+ // Helpers
2177+ // ---------------------------------------------------------------------------
19782178 /**
19792179 * Gets the last decorator call for the function `func`, if `func` has decorators.
19802180 */
@@ -1983,6 +2183,96 @@ private module Django {
19832183 not exists ( Call other_decorator | other_decorator .getArg ( 0 ) = result )
19842184 }
19852185
2186+ /** Adds the `getASelfRef` member predicate when modeling a class. */
2187+ abstract private class SelfRefMixin extends Class {
2188+ /**
2189+ * Gets a reference to instances of this class, originating from a self parameter of
2190+ * a method defined on this class.
2191+ *
2192+ * Note: TODO: This doesn't take MRO into account
2193+ * Note: TODO: This doesn't take staticmethod/classmethod into account
2194+ */
2195+ private DataFlow:: Node getASelfRef ( DataFlow:: TypeTracker t ) {
2196+ t .start ( ) and
2197+ result .( DataFlow:: ParameterNode ) .getParameter ( ) = this .getAMethod ( ) .getArg ( 0 )
2198+ or
2199+ exists ( DataFlow:: TypeTracker t2 | result = this .getASelfRef ( t2 ) .track ( t2 , t ) )
2200+ }
2201+
2202+ /**
2203+ * Gets a reference to instances of this class, originating from a self parameter of
2204+ * a method defined on this class.
2205+ *
2206+ * Note: TODO: This doesn't take MRO into account
2207+ * Note: TODO: This doesn't take staticmethod/classmethod into account
2208+ */
2209+ DataFlow:: Node getASelfRef ( ) { result = this .getASelfRef ( DataFlow:: TypeTracker:: end ( ) ) }
2210+ }
2211+
2212+ // ---------------------------------------------------------------------------
2213+ // Form and form field modeling
2214+ // ---------------------------------------------------------------------------
2215+ /**
2216+ * A class that is a subclass of the `django.forms.Form` class,
2217+ * thereby handling user input.
2218+ */
2219+ class DjangoFormClass extends Class , SelfRefMixin {
2220+ DjangoFormClass ( ) { this .getABase ( ) = Django:: Forms:: Form:: subclassRef ( ) .getAUse ( ) .asExpr ( ) }
2221+ }
2222+
2223+ /**
2224+ * A source of cleaned_data (either the return value from `super().clean()`, or a reference to `self.cleaned_data`)
2225+ *
2226+ * See https://docs.djangoproject.com/en/3.1/ref/forms/validation/#form-and-field-validation
2227+ */
2228+ private class DjangoFormCleanedData extends RemoteFlowSource:: Range , DataFlow:: Node {
2229+ DjangoFormCleanedData ( ) {
2230+ exists ( DjangoFormClass cls , Function meth |
2231+ cls .getAMethod ( ) = meth and
2232+ (
2233+ this = API:: builtin ( "super" ) .getReturn ( ) .getMember ( "clean" ) .getACall ( ) and
2234+ this .getScope ( ) = meth
2235+ or
2236+ this .( DataFlow:: AttrRead ) .getAttributeName ( ) = "cleaned_data" and
2237+ this .( DataFlow:: AttrRead ) .getObject ( ) = cls .getASelfRef ( )
2238+ )
2239+ )
2240+ }
2241+
2242+ override string getSourceType ( ) {
2243+ result = "django.forms.Field subclass, value parameter in method"
2244+ }
2245+ }
2246+
2247+ /**
2248+ * A class that is a subclass of the `django.forms.Field` class,
2249+ * thereby handling user input.
2250+ */
2251+ class DjangoFormFieldClass extends Class {
2252+ DjangoFormFieldClass ( ) {
2253+ this .getABase ( ) = Django:: Forms:: Field:: subclassRef ( ) .getAUse ( ) .asExpr ( )
2254+ }
2255+ }
2256+
2257+ /**
2258+ * A parameter in a method on a `DjangoFormFieldClass` that receives the user-supplied value for this field.
2259+ *
2260+ * See https://docs.djangoproject.com/en/3.1/ref/forms/validation/#form-and-field-validation
2261+ */
2262+ private class DjangoFormFieldValueParam extends RemoteFlowSource:: Range , DataFlow:: ParameterNode {
2263+ DjangoFormFieldValueParam ( ) {
2264+ exists ( DjangoFormFieldClass cls , Function meth |
2265+ cls .getAMethod ( ) = meth and
2266+ meth .getName ( ) in [ "to_python" , "validate" , "run_validators" , "clean" ] and
2267+ this .getParameter ( ) = meth .getArg ( 1 )
2268+ )
2269+ }
2270+
2271+ override string getSourceType ( ) {
2272+ result = "django.forms.Field subclass, value parameter in method"
2273+ }
2274+ }
2275+
19862276 // ---------------------------------------------------------------------------
19872277 // routing modeling
19882278 // ---------------------------------------------------------------------------
@@ -2068,7 +2358,7 @@ private module Django {
20682358 }
20692359
20702360 /** A class that we consider a django View class. */
2071- abstract class DjangoViewClass extends DjangoViewClassHelper {
2361+ abstract class DjangoViewClass extends DjangoViewClassHelper , SelfRefMixin {
20722362 /** Gets a function that could handle incoming requests, if any. */
20732363 Function getARequestHandler ( ) {
20742364 // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with
@@ -2080,29 +2370,6 @@ private module Django {
20802370 result .getName ( ) = "get_redirect_url"
20812371 )
20822372 }
2083-
2084- /**
2085- * Gets a reference to instances of this class, originating from a self parameter of
2086- * a method defined on this class.
2087- *
2088- * Note: TODO: This doesn't take MRO into account
2089- * Note: TODO: This doesn't take staticmethod/classmethod into account
2090- */
2091- private DataFlow:: Node getASelfRef ( DataFlow:: TypeTracker t ) {
2092- t .start ( ) and
2093- result .( DataFlow:: ParameterNode ) .getParameter ( ) = this .getAMethod ( ) .getArg ( 0 )
2094- or
2095- exists ( DataFlow:: TypeTracker t2 | result = this .getASelfRef ( t2 ) .track ( t2 , t ) )
2096- }
2097-
2098- /**
2099- * Gets a reference to instances of this class, originating from a self parameter of
2100- * a method defined on this class.
2101- *
2102- * Note: TODO: This doesn't take MRO into account
2103- * Note: TODO: This doesn't take staticmethod/classmethod into account
2104- */
2105- DataFlow:: Node getASelfRef ( ) { result = this .getASelfRef ( DataFlow:: TypeTracker:: end ( ) ) }
21062373 }
21072374
21082375 /**
0 commit comments