@@ -105,9 +105,6 @@ private module Django {
105
105
// -------------------------------------------------------------------------
106
106
// django.db.models
107
107
// -------------------------------------------------------------------------
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.
111
108
/** Gets a reference to the `django.db.models` module. */
112
109
private DataFlow:: Node models ( DataFlow:: TypeTracker t ) {
113
110
t .start ( ) and
@@ -124,71 +121,51 @@ private module Django {
124
121
125
122
/** Provides models for the `django.db.models` module. */
126
123
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
+ */
128
129
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 * ( )
141
138
}
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 ) )
153
139
}
154
140
155
- /** Gets a reference to the `objects` object of a model. */
156
- DataFlow:: Node objects ( ) { result = objects ( DataFlow:: TypeTracker:: end ( ) ) }
157
-
158
141
/**
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` .
161
144
*/
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" ) }
176
146
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 )
183
161
}
184
162
185
163
/**
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/
188
167
*/
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 ( ) }
192
169
193
170
/** Gets a reference to the `django.db.models.expressions` module. */
194
171
private DataFlow:: Node expressions ( DataFlow:: TypeTracker t ) {
@@ -254,14 +231,13 @@ private module Django {
254
231
*
255
232
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
256
233
*/
257
- private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CfgNode {
258
- override CallNode node ;
234
+ private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CallCfgNode {
259
235
ControlFlowNode sql ;
260
236
261
237
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 ( _)
265
241
]
266
242
}
267
243
@@ -275,27 +251,24 @@ private module Django {
275
251
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
276
252
* - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
277
253
*/
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 ( ) }
282
256
283
- override DataFlow:: Node getSql ( ) { result . asCfgNode ( ) = node .getArg ( 0 ) }
257
+ override DataFlow:: Node getSql ( ) { result = this .getArg ( 0 ) }
284
258
}
285
259
286
260
/**
287
261
* A call to the `extra` function on a model.
288
262
*
289
263
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
290
264
*/
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 ( ) }
295
267
296
268
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
+ ]
299
272
}
300
273
}
301
274
0 commit comments