@@ -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
@@ -104,9 +105,6 @@ private module Django {
104105 // -------------------------------------------------------------------------
105106 // django.db.models
106107 // -------------------------------------------------------------------------
107- // NOTE: The modelling of django models is currently fairly incomplete.
108- // It does not fully take `Model`s, `Manager`s, `and QuerySet`s into account.
109- // It simply identifies some common dangerous cases.
110108 /** Gets a reference to the `django.db.models` module. */
111109 private DataFlow:: Node models ( DataFlow:: TypeTracker t ) {
112110 t .start ( ) and
@@ -123,71 +121,51 @@ private module Django {
123121
124122 /** Provides models for the `django.db.models` module. */
125123 module models {
126- /** 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+ */
127129 module Model {
128- /** Gets a reference to the `django.db.models.Model` class. */
129- private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
130- t .start ( ) and
131- result = DataFlow:: importNode ( "django.db.models.Model" )
132- or
133- t .startInAttr ( "Model" ) and
134- result = models ( )
135- or
136- // subclass
137- result .asExpr ( ) .( ClassExpr ) .getABase ( ) = classRef ( t .continue ( ) ) .asExpr ( )
138- or
139- 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 * ( )
140138 }
141-
142- /** Gets a reference to the `django.db.models.Model` class. */
143- DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
144- }
145-
146- /** Gets a reference to the `objects` object of a django model. */
147- private DataFlow:: Node objects ( DataFlow:: TypeTracker t ) {
148- t .startInAttr ( "objects" ) and
149- result = Model:: classRef ( )
150- or
151- exists ( DataFlow:: TypeTracker t2 | result = objects ( t2 ) .track ( t2 , t ) )
152139 }
153140
154- /** Gets a reference to the `objects` object of a model. */
155- DataFlow:: Node objects ( ) { result = objects ( DataFlow:: TypeTracker:: end ( ) ) }
156-
157141 /**
158- * Gets a reference to the attribute `attr_name` of an `objects` object.
159- * 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` .
160144 */
161- private DataFlow:: Node objects_attr ( DataFlow:: TypeTracker t , string attr_name ) {
162- attr_name in [ "annotate" , "extra" , "raw" ] and
163- t .startInAttr ( attr_name ) and
164- result = objects ( )
165- or
166- // Due to bad performance when using normal setup with `objects_attr(t2, attr_name).track(t2, t)`
167- // we have inlined that code and forced a join
168- exists ( DataFlow:: TypeTracker t2 |
169- exists ( DataFlow:: StepSummary summary |
170- objects_attr_first_join ( t2 , attr_name , result , summary ) and
171- t = t2 .append ( summary )
172- )
173- )
174- }
145+ API:: Node manager ( ) { result = Model:: subclassRef ( ) .getMember ( "objects" ) }
175146
176- pragma [ nomagic]
177- private predicate objects_attr_first_join (
178- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res ,
179- DataFlow:: StepSummary summary
180- ) {
181- 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 )
182161 }
183162
184163 /**
185- * Gets a reference to the attribute `attr_name` of an `objects` object.
186- * 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/
187167 */
188- DataFlow:: Node objects_attr ( string attr_name ) {
189- result = objects_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
190- }
168+ API:: Node querySet ( ) { result = querySetReturningMethod ( _) .getReturn ( ) }
191169
192170 /** Gets a reference to the `django.db.models.expressions` module. */
193171 private DataFlow:: Node expressions ( DataFlow:: TypeTracker t ) {
@@ -253,14 +231,13 @@ private module Django {
253231 *
254232 * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
255233 */
256- private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CfgNode {
257- override CallNode node ;
234+ private class ObjectsAnnotate extends SqlExecution:: Range , DataFlow:: CallCfgNode {
258235 ControlFlowNode sql ;
259236
260237 ObjectsAnnotate ( ) {
261- node . getFunction ( ) = django:: db:: models:: objects_attr ( "annotate" ) .asCfgNode ( ) and
262- django:: db:: models:: expressions:: RawSQL:: instance ( sql ) . asCfgNode ( ) in [
263- 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 ( _)
264241 ]
265242 }
266243
@@ -274,27 +251,24 @@ private module Django {
274251 * - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
275252 * - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
276253 */
277- private class ObjectsRaw extends SqlExecution:: Range , DataFlow:: CfgNode {
278- override CallNode node ;
279-
280- 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 ( ) }
281256
282- override DataFlow:: Node getSql ( ) { result . asCfgNode ( ) = node .getArg ( 0 ) }
257+ override DataFlow:: Node getSql ( ) { result = this .getArg ( 0 ) }
283258 }
284259
285260 /**
286261 * A call to the `extra` function on a model.
287262 *
288263 * See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
289264 */
290- private class ObjectsExtra extends SqlExecution:: Range , DataFlow:: CfgNode {
291- override CallNode node ;
292-
293- 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 ( ) }
294267
295268 override DataFlow:: Node getSql ( ) {
296- result .asCfgNode ( ) =
297- [ 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+ ]
298272 }
299273 }
300274
0 commit comments