@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
99private import semmle.python.dataflow.new.TaintTracking
1010private import semmle.python.Concepts
1111private import semmle.python.frameworks.Werkzeug
12+ private import semmle.python.ApiGraphs
1213
1314/**
1415 * Provides models for the `flask` PyPI package.
@@ -19,62 +20,20 @@ private module FlaskModel {
1920 // flask
2021 // ---------------------------------------------------------------------------
2122 /** Gets a reference to the `flask` module. */
22- private DataFlow:: Node flask ( DataFlow:: TypeTracker t ) {
23- t .start ( ) and
24- result = DataFlow:: importNode ( "flask" )
25- or
26- exists ( DataFlow:: TypeTracker t2 | result = flask ( t2 ) .track ( t2 , t ) )
27- }
28-
29- /** Gets a reference to the `flask` module. */
30- DataFlow:: Node flask ( ) { result = flask ( DataFlow:: TypeTracker:: end ( ) ) }
23+ API:: Node flask ( ) { result = API:: moduleImport ( "flask" ) }
3124
3225 /**
3326 * Gets a reference to the attribute `attr_name` of the `flask` module.
34- * WARNING: Only holds for a few predefined attributes.
3527 */
36- private DataFlow:: Node flask_attr ( DataFlow:: TypeTracker t , string attr_name ) {
37- attr_name in [ "request" , "make_response" , "Response" , "views" ] and
38- (
39- t .start ( ) and
40- result = DataFlow:: importNode ( "flask" + "." + attr_name )
41- or
42- t .startInAttr ( attr_name ) and
43- result = flask ( )
44- )
45- or
46- // Due to bad performance when using normal setup with `flask_attr(t2, attr_name).track(t2, t)`
47- // we have inlined that code and forced a join
48- exists ( DataFlow:: TypeTracker t2 |
49- exists ( DataFlow:: StepSummary summary |
50- flask_attr_first_join ( t2 , attr_name , result , summary ) and
51- t = t2 .append ( summary )
52- )
53- )
54- }
55-
56- pragma [ nomagic]
57- private predicate flask_attr_first_join (
58- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res , DataFlow:: StepSummary summary
59- ) {
60- DataFlow:: StepSummary:: step ( flask_attr ( t2 , attr_name ) , res , summary )
61- }
62-
63- /**
64- * Gets a reference to the attribute `attr_name` of the `flask` module.
65- * WARNING: Only holds for a few predefined attributes.
66- */
67- private DataFlow:: Node flask_attr ( string attr_name ) {
68- result = flask_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
69- }
28+ private API:: Node flask_attr ( string attr_name ) { result = flask ( ) .getMember ( attr_name ) }
7029
7130 /** Provides models for the `flask` module. */
7231 module flask {
7332 /** Gets a reference to the `flask.request` object. */
74- DataFlow :: Node request ( ) { result = flask_attr ( "request" ) }
33+ API :: Node request ( ) { result = flask_attr ( "request" ) }
7534
7635 /** Gets a reference to the `flask.make_response` function. */
77- DataFlow:: Node make_response ( ) { result = flask_attr ( "make_response" ) }
36+ DataFlow:: Node make_response ( ) { result = flask_attr ( "make_response" ) . getAUse ( ) }
7837
7938 /**
8039 * Provides models for the `flask.Flask` class
@@ -83,157 +42,49 @@ private module FlaskModel {
8342 */
8443 module Flask {
8544 /** Gets a reference to the `flask.Flask` class. */
86- private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
87- t .start ( ) and
88- result = DataFlow:: importNode ( "flask.Flask" )
89- or
90- t .startInAttr ( "Flask" ) and
91- result = flask ( )
92- or
93- exists ( DataFlow:: TypeTracker t2 | result = classRef ( t2 ) .track ( t2 , t ) )
94- }
95-
96- /** Gets a reference to the `flask.Flask` class. */
97- DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
98-
99- /**
100- * A source of instances of `flask.Flask`, extend this class to model new instances.
101- *
102- * This can include instantiations of the class, return values from function
103- * calls, or a special parameter that will be set when functions are called by an external
104- * library.
105- *
106- * Use the predicate `Flask::instance()` to get references to instances of `flask.Flask`.
107- */
108- abstract class InstanceSource extends DataFlow:: Node { }
109-
110- /** A direct instantiation of `flask.Flask`. */
111- private class ClassInstantiation extends InstanceSource , DataFlow:: CfgNode {
112- override CallNode node ;
113-
114- ClassInstantiation ( ) { node .getFunction ( ) = classRef ( ) .asCfgNode ( ) }
115- }
45+ API:: Node classRef ( ) { result = flask ( ) .getMember ( "Flask" ) }
11646
11747 /** Gets a reference to an instance of `flask.Flask` (a flask application). */
118- private DataFlow:: Node instance ( DataFlow:: TypeTracker t ) {
119- t .start ( ) and
120- result instanceof InstanceSource
121- or
122- exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
123- }
124-
125- /** Gets a reference to an instance of `flask.Flask` (a flask application). */
126- DataFlow:: Node instance ( ) { result = instance ( DataFlow:: TypeTracker:: end ( ) ) }
127-
128- /**
129- * Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
130- * WARNING: Only holds for a few predefined attributes.
131- */
132- private DataFlow:: Node instance_attr ( DataFlow:: TypeTracker t , string attr_name ) {
133- attr_name in [ "route" , "add_url_rule" , "make_response" ] and
134- t .startInAttr ( attr_name ) and
135- result = flask:: Flask:: instance ( )
136- or
137- // Due to bad performance when using normal setup with `instance_attr(t2, attr_name).track(t2, t)`
138- // we have inlined that code and forced a join
139- exists ( DataFlow:: TypeTracker t2 |
140- exists ( DataFlow:: StepSummary summary |
141- instance_attr_first_join ( t2 , attr_name , result , summary ) and
142- t = t2 .append ( summary )
143- )
144- )
145- }
146-
147- pragma [ nomagic]
148- private predicate instance_attr_first_join (
149- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res ,
150- DataFlow:: StepSummary summary
151- ) {
152- DataFlow:: StepSummary:: step ( instance_attr ( t2 , attr_name ) , res , summary )
153- }
48+ API:: Node instance ( ) { result = classRef ( ) .getReturn ( ) }
15449
15550 /**
15651 * Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
157- * WARNING: Only holds for a few predefined attributes.
15852 */
159- private DataFlow:: Node instance_attr ( string attr_name ) {
160- result = instance_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
161- }
53+ private API:: Node instance_attr ( string attr_name ) { result = instance ( ) .getMember ( attr_name ) }
16254
16355 /** Gets a reference to the `route` method on an instance of `flask.Flask`. */
164- DataFlow :: Node route ( ) { result = instance_attr ( "route" ) }
56+ API :: Node route ( ) { result = instance_attr ( "route" ) }
16557
16658 /** Gets a reference to the `add_url_rule` method on an instance of `flask.Flask`. */
167- DataFlow :: Node add_url_rule ( ) { result = instance_attr ( "add_url_rule" ) }
59+ API :: Node add_url_rule ( ) { result = instance_attr ( "add_url_rule" ) }
16860
16961 /** Gets a reference to the `make_response` method on an instance of `flask.Flask`. */
17062 // HACK: We can't call this predicate `make_response` since shadowing is
17163 // completely disallowed in QL. I added an underscore to move things forward for
17264 // now :(
173- DataFlow:: Node make_response_ ( ) { result = instance_attr ( "make_response" ) }
174-
175- /** Gets a reference to the `response_class` attribute on the `flask.Flask` class or an instance. */
176- private DataFlow:: Node response_class ( DataFlow:: TypeTracker t ) {
177- t .startInAttr ( "response_class" ) and
178- result in [ classRef ( ) , instance ( ) ]
179- or
180- exists ( DataFlow:: TypeTracker t2 | result = response_class ( t2 ) .track ( t2 , t ) )
181- }
65+ API:: Node make_response_ ( ) { result = instance_attr ( "make_response" ) }
18266
18367 /**
18468 * Gets a reference to the `response_class` attribute on the `flask.Flask` class or an instance.
18569 *
18670 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.response_class
18771 */
188- DataFlow :: Node response_class ( ) { result = response_class ( DataFlow :: TypeTracker :: end ( ) ) }
72+ API :: Node response_class ( ) { result = [ classRef ( ) , instance ( ) ] . getMember ( "response_class" ) }
18973 }
19074
19175 // -------------------------------------------------------------------------
19276 // flask.views
19377 // -------------------------------------------------------------------------
19478 /** Gets a reference to the `flask.views` module. */
195- DataFlow:: Node views ( ) { result = flask_attr ( "views" ) }
79+ DataFlow:: Node views ( ) { result = flask_attr ( "views" ) . getAUse ( ) }
19680
19781 /** Provides models for the `flask.views` module */
19882 module views {
19983 /**
20084 * Gets a reference to the attribute `attr_name` of the `flask.views` module.
201- * WARNING: Only holds for a few predefined attributes.
202- */
203- private DataFlow:: Node views_attr ( DataFlow:: TypeTracker t , string attr_name ) {
204- attr_name in [ "View" , "MethodView" ] and
205- (
206- t .start ( ) and
207- result = DataFlow:: importNode ( "flask.views" + "." + attr_name )
208- or
209- t .startInAttr ( attr_name ) and
210- result = views ( )
211- )
212- or
213- // Due to bad performance when using normal setup with `views_attr(t2, attr_name).track(t2, t)`
214- // we have inlined that code and forced a join
215- exists ( DataFlow:: TypeTracker t2 |
216- exists ( DataFlow:: StepSummary summary |
217- views_attr_first_join ( t2 , attr_name , result , summary ) and
218- t = t2 .append ( summary )
219- )
220- )
221- }
222-
223- pragma [ nomagic]
224- private predicate views_attr_first_join (
225- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res ,
226- DataFlow:: StepSummary summary
227- ) {
228- DataFlow:: StepSummary:: step ( views_attr ( t2 , attr_name ) , res , summary )
229- }
230-
231- /**
232- * Gets a reference to the attribute `attr_name` of the `flask.views` module.
233- * WARNING: Only holds for a few predefined attributes.
23485 */
23586 private DataFlow:: Node views_attr ( string attr_name ) {
236- result = views_attr ( DataFlow :: TypeTracker :: end ( ) , attr_name )
87+ result = flask ( ) . getMember ( "views" ) . getMember ( attr_name ) . getAUse ( )
23788 }
23889
23990 /**
@@ -287,15 +138,7 @@ private module FlaskModel {
287138 */
288139 module Response {
289140 /** Gets a reference to the `flask.Response` class. */
290- private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
291- t .start ( ) and
292- result in [ flask_attr ( "Response" ) , flask:: Flask:: response_class ( ) ]
293- or
294- exists ( DataFlow:: TypeTracker t2 | result = classRef ( t2 ) .track ( t2 , t ) )
295- }
296-
297- /** Gets a reference to the `flask.Response` class. */
298- DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
141+ API:: Node classRef ( ) { result = [ flask_attr ( "Response" ) , flask:: Flask:: response_class ( ) ] }
299142
300143 /**
301144 * A source of instances of `flask.Response`, extend this class to model new instances.
@@ -312,7 +155,7 @@ private module FlaskModel {
312155 private class ClassInstantiation extends InstanceSource , DataFlow:: CfgNode {
313156 override CallNode node ;
314157
315- ClassInstantiation ( ) { node .getFunction ( ) = classRef ( ) .asCfgNode ( ) }
158+ ClassInstantiation ( ) { node .getFunction ( ) = classRef ( ) .getAUse ( ) . asCfgNode ( ) }
316159
317160 override DataFlow:: Node getBody ( ) { result .asCfgNode ( ) = node .getArg ( 0 ) }
318161
@@ -338,15 +181,7 @@ private module FlaskModel {
338181 }
339182
340183 /** Gets a reference to an instance of `flask.Response`. */
341- private DataFlow:: Node instance ( DataFlow:: TypeTracker t ) {
342- t .start ( ) and
343- result instanceof InstanceSource
344- or
345- exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
346- }
347-
348- /** Gets a reference to an instance of `flask.Response`. */
349- DataFlow:: Node instance ( ) { result = instance ( DataFlow:: TypeTracker:: end ( ) ) }
184+ API:: Node instance ( ) { result = classRef ( ) .getReturn ( ) }
350185 }
351186
352187 // ---------------------------------------------------------------------------
@@ -445,7 +280,7 @@ private module FlaskModel {
445280 private class FlaskAppRouteCall extends FlaskRouteSetup , DataFlow:: CfgNode {
446281 override CallNode node ;
447282
448- FlaskAppRouteCall ( ) { node .getFunction ( ) = flask:: Flask:: route ( ) .asCfgNode ( ) }
283+ FlaskAppRouteCall ( ) { node .getFunction ( ) = flask:: Flask:: route ( ) .getAUse ( ) . asCfgNode ( ) }
449284
450285 override DataFlow:: Node getUrlPatternArg ( ) {
451286 result .asCfgNode ( ) in [ node .getArg ( 0 ) , node .getArgByName ( "rule" ) ]
@@ -462,7 +297,9 @@ private module FlaskModel {
462297 private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup , DataFlow:: CfgNode {
463298 override CallNode node ;
464299
465- FlaskAppAddUrlRuleCall ( ) { node .getFunction ( ) = flask:: Flask:: add_url_rule ( ) .asCfgNode ( ) }
300+ FlaskAppAddUrlRuleCall ( ) {
301+ node .getFunction ( ) = flask:: Flask:: add_url_rule ( ) .getAUse ( ) .asCfgNode ( )
302+ }
466303
467304 override DataFlow:: Node getUrlPatternArg ( ) {
468305 result .asCfgNode ( ) in [ node .getArg ( 0 ) , node .getArgByName ( "rule" ) ]
@@ -511,41 +348,16 @@ private module FlaskModel {
511348 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
512349 */
513350 private class RequestSource extends RemoteFlowSource:: Range {
514- RequestSource ( ) { this = flask:: request ( ) }
351+ RequestSource ( ) { this = flask:: request ( ) . getAUse ( ) }
515352
516353 override string getSourceType ( ) { result = "flask.request" }
517354 }
518355
519356 private module FlaskRequestTracking {
520- /** Gets a reference to the `get_data` attribute of a Flask request. */
521- private DataFlow:: Node get_data ( DataFlow:: TypeTracker t ) {
522- t .startInAttr ( "get_data" ) and
523- result = flask:: request ( )
524- or
525- exists ( DataFlow:: TypeTracker t2 | result = get_data ( t2 ) .track ( t2 , t ) )
526- }
527-
528- /** Gets a reference to the `get_data` attribute of a Flask request. */
529- DataFlow:: Node get_data ( ) { result = get_data ( DataFlow:: TypeTracker:: end ( ) ) }
530-
531- /** Gets a reference to the `get_json` attribute of a Flask request. */
532- private DataFlow:: Node get_json ( DataFlow:: TypeTracker t ) {
533- t .startInAttr ( "get_json" ) and
534- result = flask:: request ( )
535- or
536- exists ( DataFlow:: TypeTracker t2 | result = get_json ( t2 ) .track ( t2 , t ) )
537- }
538-
539- /** Gets a reference to the `get_json` attribute of a Flask request. */
540- DataFlow:: Node get_json ( ) { result = get_json ( DataFlow:: TypeTracker:: end ( ) ) }
541-
542357 /** Gets a reference to either of the `get_json` or `get_data` attributes of a Flask request. */
543358 DataFlow:: Node tainted_methods ( string attr_name ) {
544- result = get_data ( ) and
545- attr_name = "get_data"
546- or
547- result = get_json ( ) and
548- attr_name = "get_json"
359+ attr_name in [ "get_data" , "get_json" ] and
360+ result = flask:: request ( ) .getMember ( attr_name ) .getAUse ( )
549361 }
550362 }
551363
@@ -560,7 +372,8 @@ private module FlaskModel {
560372 RequestInputAccess ( ) {
561373 // attributes
562374 exists ( AttrNode attr |
563- this .asCfgNode ( ) = attr and attr .getObject ( attr_name ) = flask:: request ( ) .asCfgNode ( )
375+ this .asCfgNode ( ) = attr and
376+ attr .getObject ( attr_name ) = flask:: request ( ) .getAUse ( ) .asCfgNode ( )
564377 |
565378 attr_name in [
566379 // str
@@ -645,7 +458,7 @@ private module FlaskModel {
645458 FlaskMakeResponseCall ( ) {
646459 node .getFunction ( ) = flask:: make_response ( ) .asCfgNode ( )
647460 or
648- node .getFunction ( ) = flask:: Flask:: make_response_ ( ) .asCfgNode ( )
461+ node .getFunction ( ) = flask:: Flask:: make_response_ ( ) .getAUse ( ) . asCfgNode ( )
649462 }
650463
651464 override DataFlow:: Node getBody ( ) { result .asCfgNode ( ) = node .getArg ( 0 ) }
0 commit comments