File tree Expand file tree Collapse file tree 6 files changed +87
-2
lines changed
src/semmle/python/web/flask
test/library-tests/web/flask Expand file tree Collapse file tree 6 files changed +87
-2
lines changed Original file line number Diff line number Diff line change @@ -54,3 +54,35 @@ class FlaskRequestJson extends HttpRequestTaintSource {
5454
5555 override string toString ( ) { result = "flask.request.json" }
5656}
57+
58+ /**
59+ * A parameter to a flask request handler, that can capture a part of the URL (as specified in
60+ * the url-pattern of a route).
61+ *
62+ * For example, the `name` parameter in:
63+ * ```
64+ * @app.route('/hello/<name>')
65+ * def hello(name):
66+ * ```
67+ */
68+ class FlaskRoutedParameter extends HttpRequestTaintSource {
69+ FlaskRoutedParameter ( ) {
70+ exists ( string name , Function func , StrConst url_pattern |
71+ this .( ControlFlowNode ) .getNode ( ) = func .getArgByName ( name ) and
72+ flask_routing ( url_pattern .getAFlowNode ( ) , func ) and
73+ exists ( string match |
74+ match = url_pattern .getS ( ) .regexpFind ( werkzeug_rule_re ( ) , _, _) and
75+ name = match .regexpCapture ( werkzeug_rule_re ( ) , 4 )
76+ )
77+ )
78+ }
79+
80+ override predicate isSourceOf ( TaintKind kind ) { kind instanceof ExternalStringKind }
81+ }
82+
83+ private string werkzeug_rule_re ( ) {
84+ // since flask uses werkzeug internally, we are using its routing rules from
85+ // https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
86+ result =
87+ "(?<static>[^<]*)<(?:(?<converter>[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?<args>.*?)\\))?\\:)?(?<variable>[a-zA-Z_][a-zA-Z0-9_]*)>"
88+ }
Original file line number Diff line number Diff line change 66| test.py:41:26:41:53 | flask.response.argument | externally controlled string |
77| test.py:46:12:46:62 | flask.routed.response | externally controlled string |
88| test.py:46:26:46:61 | flask.response.argument | externally controlled string |
9+ | test.py:50:12:50:48 | flask.routed.response | externally controlled string |
10+ | test.py:50:26:50:47 | flask.response.argument | externally controlled string |
11+ | test.py:54:12:54:53 | flask.routed.response | externally controlled string |
12+ | test.py:54:26:54:52 | flask.response.argument | externally controlled string |
13+ | test.py:60:12:60:62 | flask.routed.response | externally controlled string |
14+ | test.py:60:26:60:61 | flask.response.argument | externally controlled string |
15+ | test.py:64:12:64:58 | flask.routed.response | externally controlled string |
16+ | test.py:64:26:64:57 | flask.response.argument | externally controlled string |
Original file line number Diff line number Diff line change 33| test.py:35:16:35:27 | Attribute | {externally controlled string} |
44| test.py:40:18:40:29 | Attribute | {externally controlled string} |
55| test.py:45:18:45:29 | Attribute | {externally controlled string} |
6+ | test.py:49:11:49:14 | name | externally controlled string |
7+ | test.py:53:9:53:15 | subpath | externally controlled string |
8+ | test.py:59:24:59:26 | bar | externally controlled string |
9+ | test.py:63:13:63:21 | lang_code | externally controlled string |
Original file line number Diff line number Diff line change 1- | / | Function hello |
1+ | / | Function hello_world |
2+ | /complex/<string(length=2):lang_code> | Function complex |
23| /dangerous | Function dangerous |
34| /dangerous-with-cfg-split | Function dangerous2 |
5+ | /foo/<path:subpath> | Function foo |
6+ | /hello/<name> | Function hello |
7+ | /multiple/bar/<bar> | Function multiple |
48| /safe | Function safe |
59| /the/ | Function get |
610| /unsafe | Function unsafe |
Original file line number Diff line number Diff line change 1515| test.py:45 | Attribute() | externally controlled string |
1616| test.py:46 | first_name | externally controlled string |
1717| test.py:46 | make_response() | flask.Response |
18+ | test.py:49 | name | externally controlled string |
19+ | test.py:50 | BinaryExpr | externally controlled string |
20+ | test.py:50 | make_response() | flask.Response |
21+ | test.py:50 | name | externally controlled string |
22+ | test.py:53 | subpath | externally controlled string |
23+ | test.py:54 | BinaryExpr | externally controlled string |
24+ | test.py:54 | make_response() | flask.Response |
25+ | test.py:54 | subpath | externally controlled string |
26+ | test.py:59 | bar | externally controlled string |
27+ | test.py:60 | Attribute() | externally controlled string |
28+ | test.py:60 | bar | externally controlled string |
29+ | test.py:60 | make_response() | flask.Response |
30+ | test.py:63 | lang_code | externally controlled string |
31+ | test.py:64 | Attribute() | externally controlled string |
32+ | test.py:64 | lang_code | externally controlled string |
33+ | test.py:64 | make_response() | flask.Response |
Original file line number Diff line number Diff line change 44app = Flask (__name__ )
55
66@app .route ("/" )
7- def hello ():
7+ def hello_world ():
88 return "Hello World!"
99
1010from flask .views import MethodView
@@ -44,3 +44,24 @@ def unsafe():
4444def safe ():
4545 first_name = request .args .get ('name' , '' )
4646 return make_response ("Your name is " + escape (first_name ))
47+
48+ @app .route ('/hello/<name>' )
49+ def hello (name ):
50+ return make_response ("Your name is " + name )
51+
52+ @app .route ('/foo/<path:subpath>' )
53+ def foo (subpath ):
54+ return make_response ("The subpath is " + subpath )
55+
56+ @app .route ('/multiple/' ) # TODO: not recognized as route
57+ @app .route ('/multiple/foo/<foo>' ) # TODO: not recognized as route
58+ @app .route ('/multiple/bar/<bar>' )
59+ def multiple (foo = None , bar = None ):
60+ return make_response ("foo={!r} bar={!r}" .format (foo , bar ))
61+
62+ @app .route ('/complex/<string(length=2):lang_code>' )
63+ def complex (lang_code ):
64+ return make_response ("lang_code {}" .format (lang_code ))
65+
66+ if __name__ == "__main__" :
67+ app .run (debug = True )
You can’t perform that action at this time.
0 commit comments