@@ -10,13 +10,11 @@ private import experimental.dataflow.TaintTracking
1010private import experimental.semmle.python.Concepts
1111private import experimental.semmle.python.frameworks.Werkzeug
1212
13- // for old improved impl see
14- // https://github.com/github/codeql/blob/9f95212e103c68d0c1dfa4b6f30fb5d53954ccef/python/ql/src/semmle/python/web/flask/Request.qll
1513/**
1614 * Provides models for the `flask` PyPI package.
1715 * See https://flask.palletsprojects.com/en/1.1.x/.
1816 */
19- private module Flask {
17+ private module FlaskModel {
2018 /** Gets a reference to the `flask` module. */
2119 private DataFlow:: Node flask ( DataFlow:: TypeTracker t ) {
2220 t .start ( ) and
@@ -44,69 +42,101 @@ private module Flask {
4442 /** Gets a reference to the `flask.request` object. */
4543 DataFlow:: Node request ( ) { result = request ( DataFlow:: TypeTracker:: end ( ) ) }
4644
47- /** Gets a reference to the `flask.Flask` class. */
48- private DataFlow:: Node classFlask ( DataFlow:: TypeTracker t ) {
49- t .start ( ) and
50- result = DataFlow:: importNode ( "flask.Flask" )
51- or
52- t .startInAttr ( "Flask" ) and
53- result = flask ( )
54- or
55- exists ( DataFlow:: TypeTracker t2 | result = classFlask ( t2 ) .track ( t2 , t ) )
56- }
57-
58- /** Gets a reference to the `flask.Flask` class. */
59- DataFlow:: Node classFlask ( ) { result = classFlask ( DataFlow:: TypeTracker:: end ( ) ) }
60-
61- /** Gets a reference to an instance of `flask.Flask` (a Flask application). */
62- private DataFlow:: Node app ( DataFlow:: TypeTracker t ) {
63- t .start ( ) and
64- result .asCfgNode ( ) .( CallNode ) .getFunction ( ) = flask:: classFlask ( ) .asCfgNode ( )
65- or
66- exists ( DataFlow:: TypeTracker t2 | result = app ( t2 ) .track ( t2 , t ) )
45+ /**
46+ * Provides models for the `flask.Flask` class
47+ *
48+ * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
49+ */
50+ module Flask {
51+ /** Gets a reference to the `flask.Flask` class. */
52+ private DataFlow:: Node classRef ( DataFlow:: TypeTracker t ) {
53+ t .start ( ) and
54+ result = DataFlow:: importNode ( "flask.Flask" )
55+ or
56+ t .startInAttr ( "Flask" ) and
57+ result = flask ( )
58+ or
59+ exists ( DataFlow:: TypeTracker t2 | result = classRef ( t2 ) .track ( t2 , t ) )
60+ }
61+
62+ /** Gets a reference to the `flask.Flask` class. */
63+ DataFlow:: Node classRef ( ) { result = classRef ( DataFlow:: TypeTracker:: end ( ) ) }
64+
65+ /**
66+ * A source of an instance of `flask.Flask`.
67+ *
68+ * This can include instantiation of the class, return value from function
69+ * calls, or a special parameter that will be set when functions are call by external
70+ * library.
71+ *
72+ * Use `Flask::instance()` predicate to get references to instances of `flask.Flask`.
73+ */
74+ abstract class InstanceSource extends DataFlow:: Node { }
75+
76+ /** A direct instantiation of `flask.Flask`. */
77+ private class ClassInstantiation extends InstanceSource , DataFlow:: CfgNode {
78+ override CallNode node ;
79+
80+ ClassInstantiation ( ) { node .getFunction ( ) = classRef ( ) .asCfgNode ( ) }
81+ }
82+
83+ /** Gets a reference to an instance of `flask.Flask` (a flask application). */
84+ private DataFlow:: Node instance ( DataFlow:: TypeTracker t ) {
85+ t .start ( ) and
86+ result instanceof InstanceSource
87+ or
88+ exists ( DataFlow:: TypeTracker t2 | result = instance ( t2 ) .track ( t2 , t ) )
89+ }
90+
91+ /** Gets a reference to an instance of `flask.Flask` (a flask application). */
92+ DataFlow:: Node instance ( ) { result = instance ( DataFlow:: TypeTracker:: end ( ) ) }
93+
94+ /**
95+ * Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
96+ * WARNING: Only holds for a few predefined attributes.
97+ */
98+ private DataFlow:: Node instance_attr ( DataFlow:: TypeTracker t , string attr_name ) {
99+ attr_name in [ "route" , "add_url_rule" ] and
100+ t .startInAttr ( attr_name ) and
101+ result = flask:: Flask:: instance ( )
102+ or
103+ // Due to bad performance when using normal setup with `instance_attr(t2, attr_name).track(t2, t)`
104+ // we have inlined that code and forced a join
105+ exists ( DataFlow:: TypeTracker t2 |
106+ exists ( DataFlow:: StepSummary summary |
107+ instance_attr_first_join ( t2 , attr_name , result , summary ) and
108+ t = t2 .append ( summary )
109+ )
110+ )
111+ }
112+
113+ pragma [ nomagic]
114+ private predicate instance_attr_first_join (
115+ DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res ,
116+ DataFlow:: StepSummary summary
117+ ) {
118+ DataFlow:: StepSummary:: step ( instance_attr ( t2 , attr_name ) , res , summary )
119+ }
120+
121+ /**
122+ * Gets a reference to the attribute `attr_name` of an instance of `flask.Flask` (a flask application).
123+ * WARNING: Only holds for a few predefined attributes.
124+ */
125+ private DataFlow:: Node instance_attr ( string attr_name ) {
126+ result = instance_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
127+ }
128+
129+ /** Gets a reference to the `route` method on an instance of `flask.Flask`. */
130+ DataFlow:: Node route ( ) { result = instance_attr ( "route" ) }
131+
132+ /** Gets a reference to the `add_url_rule` method on an instance of `flask.Flask`. */
133+ DataFlow:: Node add_url_rule ( ) { result = instance_attr ( "add_url_rule" ) }
67134 }
68-
69- /** Gets a reference to an instance of `flask.Flask` (a flask application). */
70- DataFlow:: Node app ( ) { result = app ( DataFlow:: TypeTracker:: end ( ) ) }
71135 }
72136
73137 // ---------------------------------------------------------------------------
74138 // routing modeling
75139 // ---------------------------------------------------------------------------
76- /**
77- * Gets a reference to the attribute `attr_name` of a flask application.
78- * WARNING: Only holds for a few predefined attributes.
79- */
80- private DataFlow:: Node app_attr ( DataFlow:: TypeTracker t , string attr_name ) {
81- attr_name in [ "route" , "add_url_rule" ] and
82- t .startInAttr ( attr_name ) and
83- result = flask:: app ( )
84- or
85- // Due to bad performance when using normal setup with `app_attr(t2, attr_name).track(t2, t)`
86- // we have inlined that code and forced a join
87- exists ( DataFlow:: TypeTracker t2 |
88- exists ( DataFlow:: StepSummary summary |
89- app_attr_first_join ( t2 , attr_name , result , summary ) and
90- t = t2 .append ( summary )
91- )
92- )
93- }
94-
95- pragma [ nomagic]
96- private predicate app_attr_first_join (
97- DataFlow:: TypeTracker t2 , string attr_name , DataFlow:: Node res , DataFlow:: StepSummary summary
98- ) {
99- DataFlow:: StepSummary:: step ( app_attr ( t2 , attr_name ) , res , summary )
100- }
101-
102- /**
103- * Gets a reference to the attribute `attr_name` of a flask application.
104- * WARNING: Only holds for a few predefined attributes.
105- */
106- private DataFlow:: Node app_attr ( string attr_name ) {
107- result = app_attr ( DataFlow:: TypeTracker:: end ( ) , attr_name )
108- }
109-
110140 private string werkzeug_rule_re ( ) {
111141 // since flask uses werkzeug internally, we are using its routing rules from
112142 // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
@@ -134,14 +164,14 @@ private module Flask {
134164 }
135165
136166 /**
137- * A call to ` flask.Flask.route `.
167+ * A call to the `route` method on an instance of ` flask.Flask`.
138168 *
139169 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route
140170 */
141171 private class FlaskAppRouteCall extends FlaskRouteSetup , DataFlow:: CfgNode {
142172 override CallNode node ;
143173
144- FlaskAppRouteCall ( ) { node .getFunction ( ) = app_attr ( " route" ) .asCfgNode ( ) }
174+ FlaskAppRouteCall ( ) { node .getFunction ( ) = flask :: Flask :: route ( ) .asCfgNode ( ) }
145175
146176 override DataFlow:: Node getUrlPatternArg ( ) {
147177 result .asCfgNode ( ) in [ node .getArg ( 0 ) , node .getArgByName ( "rule" ) ]
@@ -151,14 +181,14 @@ private module Flask {
151181 }
152182
153183 /**
154- * A call to ` flask.Flask.add_url_rule `.
184+ * A call to the `add_url_rule` method on an instance of ` flask.Flask`.
155185 *
156186 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule
157187 */
158- private class FlaskAppAddUrlRule extends FlaskRouteSetup , DataFlow:: CfgNode {
188+ private class FlaskAppAddUrlRuleCall extends FlaskRouteSetup , DataFlow:: CfgNode {
159189 override CallNode node ;
160190
161- FlaskAppAddUrlRule ( ) { node .getFunction ( ) = app_attr ( " add_url_rule" ) .asCfgNode ( ) }
191+ FlaskAppAddUrlRuleCall ( ) { node .getFunction ( ) = flask :: Flask :: add_url_rule ( ) .asCfgNode ( ) }
162192
163193 override DataFlow:: Node getUrlPatternArg ( ) {
164194 result .asCfgNode ( ) in [ node .getArg ( 0 ) , node .getArgByName ( "rule" ) ]
@@ -287,15 +317,15 @@ private module Flask {
287317 }
288318
289319 private class RequestInputMultiDict extends RequestInputAccess ,
290- Werkzeug:: Datastructures :: MultiDict {
320+ Werkzeug:: werkzeug :: datastructures :: MultiDict:: InstanceSource {
291321 RequestInputMultiDict ( ) { attr_name in [ "args" , "values" , "form" , "files" ] }
292322 }
293323
294324 private class RequestInputFiles extends RequestInputMultiDict {
295325 RequestInputFiles ( ) { attr_name = "files" }
296326 }
297327 // TODO: Somehow specify that elements of `RequestInputFiles` are
298- // Werkzeug::Datastructures ::FileStorage and should have those additional taint steps
328+ // Werkzeug::werkzeug::datastructures ::FileStorage and should have those additional taint steps
299329 // AND that the 0-indexed argument to its' save method is a sink for path-injection.
300330 // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
301331}
0 commit comments