@@ -74,14 +74,8 @@ private module FlaskModel {
7474 API:: Node instance ( ) { result = classRef ( ) .getReturn ( ) }
7575 }
7676
77- // ---------------------------------------------------------------------------
78- // flask
79- // ---------------------------------------------------------------------------
80- /** Provides models for the `flask` module. */
81- module flask {
82- /** Gets a reference to the `flask.request` object. */
83- API:: Node request ( ) { result = API:: moduleImport ( "flask" ) .getMember ( "request" ) }
84- }
77+ /** Gets a reference to the `flask.request` object. */
78+ API:: Node request ( ) { result = API:: moduleImport ( "flask" ) .getMember ( "request" ) }
8579
8680 /**
8781 * Provides models for the `flask.Response` class
@@ -305,100 +299,92 @@ private module FlaskModel {
305299 // ---------------------------------------------------------------------------
306300 // flask.Request taint modeling
307301 // ---------------------------------------------------------------------------
308- // TODO: Do we even need this class? :|
309302 /**
310303 * A source of remote flow from a flask request.
311304 *
312305 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
313306 */
314307 private class RequestSource extends RemoteFlowSource:: Range {
315- RequestSource ( ) { this = flask :: request ( ) .getAUse ( ) }
308+ RequestSource ( ) { this = request ( ) .getAUse ( ) }
316309
317310 override string getSourceType ( ) { result = "flask.request" }
318311 }
319312
320- private module FlaskRequestTracking {
321- /** Gets a reference to either of the `get_json` or `get_data` attributes of a Flask request. */
322- API:: Node tainted_methods ( string attr_name ) {
323- attr_name in [ "get_data" , "get_json" ] and
324- result = flask:: request ( ) .getMember ( attr_name )
325- }
326- }
327-
328313 /**
329- * A source of remote flow from attributes from a flask request.
314+ * Taint propagation for a flask request.
330315 *
331316 * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Request
332317 */
333- private class RequestInputAccess extends RemoteFlowSource:: Range {
334- string attr_name ;
335-
336- RequestInputAccess ( ) {
337- // attributes
338- this = flask:: request ( ) .getMember ( attr_name ) .getAnImmediateUse ( ) and
339- attr_name in [
340- // str
341- "path" , "full_path" , "base_url" , "url" , "access_control_request_method" ,
342- "content_encoding" , "content_md5" , "content_type" , "data" , "method" , "mimetype" , "origin" ,
343- "query_string" , "referrer" , "remote_addr" , "remote_user" , "user_agent" ,
344- // dict
345- "environ" , "cookies" , "mimetype_params" , "view_args" ,
346- // json
347- "json" ,
348- // List[str]
349- "access_route" ,
350- // file-like
351- "stream" , "input_stream" ,
352- // MultiDict[str, str]
353- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
354- "args" , "values" , "form" ,
355- // MultiDict[str, FileStorage]
356- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
357- // TODO: FileStorage needs extra taint steps
358- "files" ,
359- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
360- "access_control_request_headers" , "pragma" ,
361- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
362- // TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
363- "accept_charsets" , "accept_encodings" , "accept_languages" , "accept_mimetypes" ,
364- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
365- // TODO: dict subclass with extra attributes like `username` and `password`
366- "authorization" ,
367- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
368- // TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
369- "cache_control" ,
370- // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
371- // TODO: dict-like with wsgiref.headers.Header compatibility methods
372- "headers"
373- ]
318+ private class FlaskRequestAdditionalTaintStep extends TaintTracking:: AdditionalTaintStep {
319+ override predicate step ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
320+ // Methods
321+ exists ( string method_name | method_name in [ "get_data" , "get_json" ] |
322+ // Method access
323+ nodeFrom = request ( ) .getAUse ( ) and
324+ nodeTo = request ( ) .getMember ( method_name ) .getAnImmediateUse ( )
325+ or
326+ // Method call
327+ nodeFrom = request ( ) .getMember ( method_name ) .getAUse ( ) and
328+ nodeTo .( DataFlow:: CallCfgNode ) .getFunction ( ) = nodeFrom
329+ )
374330 or
375- // methods (needs special handling to track bound-methods -- see `FlaskRequestMethodCallsAdditionalTaintStep` below)
376- this = FlaskRequestTracking:: tainted_methods ( attr_name ) .getAUse ( )
331+ // Attributes
332+ nodeFrom = request ( ) .getAUse ( ) and
333+ exists ( DataFlow:: AttrRead read | nodeTo = read and read .getObject ( ) = nodeFrom |
334+ read .getAttributeName ( ) in [
335+ // str
336+ "path" , "full_path" , "base_url" , "url" , "access_control_request_method" ,
337+ "content_encoding" , "content_md5" , "content_type" , "data" , "method" , "mimetype" ,
338+ "origin" , "query_string" , "referrer" , "remote_addr" , "remote_user" , "user_agent" ,
339+ // dict
340+ "environ" , "cookies" , "mimetype_params" , "view_args" ,
341+ // json
342+ "json" ,
343+ // List[str]
344+ "access_route" ,
345+ // file-like
346+ "stream" , "input_stream" ,
347+ // MultiDict[str, str]
348+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.MultiDict
349+ "args" , "values" , "form" ,
350+ // MultiDict[str, FileStorage]
351+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage
352+ // TODO: FileStorage needs extra taint steps
353+ "files" ,
354+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.HeaderSet
355+ "access_control_request_headers" , "pragma" ,
356+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Accept
357+ // TODO: Kinda badly modeled for now -- has type List[Tuple[value, quality]], and some extra methods
358+ "accept_charsets" , "accept_encodings" , "accept_languages" , "accept_mimetypes" ,
359+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Authorization
360+ // TODO: dict subclass with extra attributes like `username` and `password`
361+ "authorization" ,
362+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.RequestCacheControl
363+ // TODO: has attributes like `no_cache`, and `to_header` method (actually, many of these models do)
364+ "cache_control" ,
365+ // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers
366+ // TODO: dict-like with wsgiref.headers.Header compatibility methods
367+ "headers"
368+ ]
369+ )
377370 }
378-
379- override string getSourceType ( ) { result = "flask.request input" }
380371 }
381372
382- private class FlaskRequestMethodCallsAdditionalTaintStep extends TaintTracking:: AdditionalTaintStep {
383- override predicate step ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
384- // NOTE: `request -> request.tainted_method` part is handled as part of RequestInputAccess
385- // tainted_method -> tainted_method()
386- nodeFrom = FlaskRequestTracking:: tainted_methods ( _) .getAUse ( ) and
387- nodeTo .( DataFlow:: CallCfgNode ) .getFunction ( ) = nodeFrom
388- }
389- }
373+ private class RequestAttrMultiDict extends Werkzeug:: werkzeug:: datastructures:: MultiDict:: InstanceSource {
374+ string attr_name ;
390375
391- private class RequestInputMultiDict extends RequestInputAccess ,
392- Werkzeug:: werkzeug:: datastructures:: MultiDict:: InstanceSource {
393- RequestInputMultiDict ( ) { attr_name in [ "args" , "values" , "form" , "files" ] }
376+ RequestAttrMultiDict ( ) {
377+ attr_name in [ "args" , "values" , "form" , "files" ] and
378+ this = request ( ) .getMember ( attr_name ) .getAnImmediateUse ( )
379+ }
394380 }
395381
396- private class RequestInputFiles extends RequestInputMultiDict {
397- // TODO: Somehow specify that elements of `RequestInputFiles ` are
382+ private class RequestAttrFiles extends RequestAttrMultiDict {
383+ // TODO: Somehow specify that elements of `RequestAttrFiles ` are
398384 // Werkzeug::werkzeug::datastructures::FileStorage and should have those additional taint steps
399385 // AND that the 0-indexed argument to its' save method is a sink for path-injection.
400386 // https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.FileStorage.save
401- RequestInputFiles ( ) { attr_name = "files" }
387+ RequestAttrFiles ( ) { attr_name = "files" }
402388 }
403389
404390 // ---------------------------------------------------------------------------
0 commit comments