@@ -105,9 +105,6 @@ private module Django {
105105 // -------------------------------------------------------------------------
106106 // django.db.models
107107 // -------------------------------------------------------------------------
108- // NOTE: The modelling of django models is currently fairly incomplete.
109- // It does not fully take `Model`s, `Manager`s, `and QuerySet`s into account.
110- // It simply identifies some common dangerous cases.
111108 /** Gets a reference to the `django.db.models` module. */
112109 private DataFlow:: Node models ( DataFlow:: TypeTracker t ) {
113110 t .start ( ) and
@@ -124,71 +121,51 @@ private module Django {
124121
125122 /** Provides models for the `django.db.models` module. */
126123 module models {
127- /** Provides models for the `django.db.models.Model` class. */
124+ /**
125+ * Provides models for the `django.db.models.Model` class and subclasses.
126+ *
127+ * See https://docs.djangoproject.com/en/3.1/topics/db/models/.
128+ */
128129 module Model {
129- /** Gets a reference to the `django.db.models.Model` class. */
130- private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
131- t .start ( ) and
132- result = DataFlow:: importNode ( "django.db.models.Model" )
133- or
134- t .startInAttr ( "Model" ) and
135- result = models ( )
136- or
137- // subclass
138- result .asExpr ( ) .( ClassExpr ) .getABase ( ) = classRef ( t .continue ( ) ) .asExpr ( )
139- or
140- exists ( DataFlow:: TypeTracker t2 | result = classRef ( t2 ) .track ( t2 , t ) )
130+ /** Gets a reference to the `flask.views.View` class or any subclass. */
131+ API:: Node subclassRef ( ) {
132+ result =
133+ API:: moduleImport ( "django" )
134+ .getMember ( "db" )
135+ .getMember ( "models" )
136+ .getMember ( "Model" )
137+ .getASubclass * ( )
141138 }
142-
143- /** Gets a reference to the `django.db.models.Model` class. */
144- DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
145- }
146-
147- /** Gets a reference to the `objects` object of a django model. */
148- private DataFlow:: Node objects ( DataFlow:: TypeTracker t ) {
149- t .startInAttr ( "objects" ) and
150- result = Model:: classRef ( )
151- or
152- exists ( DataFlow:: TypeTracker t2 | result = objects ( t2 ) .track ( t2 , t ) )
153139 }
154140
155- /** Gets a reference to the `objects` object of a model. */
156- DataFlow:: Node objects ( ) { result = objects ( DataFlow:: TypeTracker:: end ( ) ) }
157-
158141 /**
159- * Gets a reference to the attribute `attr_name` of an `objects` object.
160- * WARNING: Only holds for a few predefined attributes .
142+ * Gets a reference to the Manager (django.db.models.Manager) for a django Model,
143+ * accessed by `<ModelName>.objects` .
161144 */
162- private DataFlow:: Node objects_attr ( DataFlow:: TypeTracker t , string attr_name ) {
163- attr_name in [ "annotate" , "extra" , "raw" ] and
164- t .startInAttr ( attr_name ) and
165- result = objects ( )
166- or
167- // Due to bad performance when using normal setup with `objects_attr(t2, attr_name).track(t2, t)`
168- // we have inlined that code and forced a join
169- exists ( DataFlow:: TypeTracker t2 |
170- exists ( DataFlow:: StepSummary summary |
171- objects_attr_first_join ( t2 , attr_name , result , summary ) and
172- t = t2 .append ( summary )
173- )
174- )
175- }
145+ API:: Node manager ( ) { result = Model:: subclassRef ( ) .getMember ( "objects" ) }
176146
177- pragma [ nomagic]
178- private predicate objects_attr_first_join (
179- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res ,
180- DataFlow:: StepSummary summary
181- ) {
182- DataFlow:: StepSummary:: step ( objects_attr ( t2 , attr_name ) , res , summary )
147+ /**
148+ * Gets a method with `name` that returns a QuerySet.
149+ * This method can originate on a QuerySet or a Manager.
150+ *
151+ * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/
152+ */
153+ API:: Node querySetReturningMethod ( string name ) {
154+ name in [
155+ "none" , "all" , "filter" , "exclude" , "complex_filter" , "union" , "intersection" ,
156+ "difference" , "select_for_update" , "select_related" , "prefetch_related" , "order_by" ,
157+ "distinct" , "reverse" , "defer" , "only" , "using" , "annotate" , "extra" , "raw" ,
158+ "datetimes" , "dates" , "values" , "values_list"
159+ ] and
160+ result = [ manager ( ) , querySet ( ) ] .getMember ( name )
183161 }
184162
185163 /**
186- * Gets a reference to the attribute `attr_name` of an `objects` object.
187- * WARNING: Only holds for a few predefined attributes.
164+ * Gets a reference to a QuerySet (django.db.models.query.QuerySet).
165+ *
166+ * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/
188167 */
189- DataFlow:: Node objects_attr ( string attr_name ) {
190- result = objects_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
191- }
168+ API:: Node querySet ( ) { result = querySetReturningMethod ( _) .getReturn ( ) }
192169
193170 /** Gets a reference to the `django.db.models.expressions` module. */
194171 private DataFlow:: Node expressions ( DataFlow:: TypeTracker t ) {
@@ -254,14 +231,13 @@ private module Django {
254231 *
255232 * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
256233 */
257- private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CfgNode {
258- override CallNode node ;
234+ private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CallCfgNode {
259235 ControlFlowNode sql ;
260236
261237 ObjectsAnnotate ( ) {
262- node . getFunction ( ) = django:: db:: models:: objects_attr ( "annotate" ) .asCfgNode ( ) and
263- django:: db:: models:: expressions:: RawSQL:: instance ( sql ) . asCfgNode ( ) in [
264- node .getArg ( _) , node .getArgByName ( _)
238+ this = django:: db:: models:: querySetReturningMethod ( "annotate" ) .getACall ( ) and
239+ django:: db:: models:: expressions:: RawSQL:: instance ( sql ) in [
240+ this .getArg ( _) , this .getArgByName ( _)
265241 ]
266242 }
267243
@@ -275,27 +251,24 @@ private module Django {
275251 * - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
276252 * - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
277253 */
278- private class ObjectsRaw extends SqlExecution:: Range , DataFlow:: CfgNode {
279- override CallNode node ;
280-
281- ObjectsRaw ( ) { node .getFunction ( ) = django:: db:: models:: objects_attr ( "raw" ) .asCfgNode ( ) }
254+ private class ObjectsRaw extends SqlExecution:: Range , DataFlow:: CallCfgNode {
255+ ObjectsRaw ( ) { this = django:: db:: models:: querySetReturningMethod ( "raw" ) .getACall ( ) }
282256
283- override DataFlow:: Node getSql ( ) { result . asCfgNode ( ) = node .getArg ( 0 ) }
257+ override DataFlow:: Node getSql ( ) { result = this .getArg ( 0 ) }
284258 }
285259
286260 /**
287261 * A call to the `extra` function on a model.
288262 *
289263 * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
290264 */
291- private class ObjectsExtra extends SqlExecution:: Range , DataFlow:: CfgNode {
292- override CallNode node ;
293-
294- ObjectsExtra ( ) { node .getFunction ( ) = django:: db:: models:: objects_attr ( "extra" ) .asCfgNode ( ) }
265+ private class ObjectsExtra extends SqlExecution:: Range , DataFlow:: CallCfgNode {
266+ ObjectsExtra ( ) { this = django:: db:: models:: querySetReturningMethod ( "extra" ) .getACall ( ) }
295267
296268 override DataFlow:: Node getSql ( ) {
297- result .asCfgNode ( ) =
298- [ node .getArg ( [ 0 , 1 , 3 , 4 ] ) , node .getArgByName ( [ "select" , "where" , "tables" , "order_by" ] ) ]
269+ result in [
270+ this .getArg ( [ 0 , 1 , 3 , 4 ] ) , this .getArgByName ( [ "select" , "where" , "tables" , "order_by" ] )
271+ ]
299272 }
300273 }
301274
0 commit comments