mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #4415 from RasmusWL/python-flask-routed-parameter
Python: Add support for routed parameters in flask
This commit is contained in:
@@ -2,6 +2,7 @@ private import python
|
||||
private import experimental.dataflow.DataFlow
|
||||
// Need to import since frameworks can extend `RemoteFlowSource::Range`
|
||||
private import experimental.semmle.python.Frameworks
|
||||
private import experimental.semmle.python.Concepts
|
||||
|
||||
/**
|
||||
* A data flow source of remote user input.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import python
|
||||
private import experimental.dataflow.DataFlow
|
||||
private import experimental.semmle.python.Frameworks
|
||||
private import experimental.dataflow.RemoteFlowSources
|
||||
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
@@ -38,3 +39,62 @@ module SystemCommandExecution {
|
||||
abstract DataFlow::Node getCommand();
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for modeling HTTP-related APIs. */
|
||||
module HTTP {
|
||||
/** Provides classes for modeling HTTP servers. */
|
||||
module Server {
|
||||
/**
|
||||
* An data-flow node that sets up a route on a server.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `RouteSetup::Range` instead.
|
||||
*/
|
||||
class RouteSetup extends DataFlow::Node {
|
||||
RouteSetup::Range range;
|
||||
|
||||
RouteSetup() { this = range }
|
||||
|
||||
/** Gets the URL pattern for this route, if it can be statically determined. */
|
||||
string getUrlPattern() { result = range.getUrlPattern() }
|
||||
|
||||
/** Gets a function that will handle incoming requests for this route, if any. */
|
||||
Function getARouteHandler() { result = range.getARouteHandler() }
|
||||
|
||||
/**
|
||||
* Gets a parameter that will receive parts of the url when handling incoming
|
||||
* requests for this route, if any. These automatically become a `RemoteFlowSource`.
|
||||
*/
|
||||
Parameter getARoutedParameter() { result = range.getARoutedParameter() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new HTTP routing APIs. */
|
||||
module RouteSetup {
|
||||
/**
|
||||
* An data-flow node that sets up a route on a server.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `RouteSetup` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the URL pattern for this route, if it can be statically determined. */
|
||||
abstract string getUrlPattern();
|
||||
|
||||
/** Gets a function that will handle incoming requests for this route, if any. */
|
||||
abstract Function getARouteHandler();
|
||||
|
||||
/**
|
||||
* Gets a parameter that will receive parts of the url when handling incoming
|
||||
* requests for this route, if any. These automatically become a `RemoteFlowSource`.
|
||||
*/
|
||||
abstract Parameter getARoutedParameter();
|
||||
}
|
||||
}
|
||||
|
||||
private class RoutedParameter extends RemoteFlowSource::Range, DataFlow::ParameterNode {
|
||||
RoutedParameter() { this.getParameter() = any(RouteSetup setup).getARoutedParameter() }
|
||||
|
||||
override string getSourceType() { result = "RoutedParameter" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `flask` package.
|
||||
* Provides classes modeling security-relevant aspects of the `flask` PyPI package.
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/.
|
||||
*/
|
||||
|
||||
private import python
|
||||
@@ -11,6 +12,10 @@ private import experimental.semmle.python.frameworks.Werkzeug
|
||||
|
||||
// for old improved impl see
|
||||
// https://github.com/github/codeql/blob/9f95212e103c68d0c1dfa4b6f30fb5d53954ccef/python/ql/src/semmle/python/web/flask/Request.qll
|
||||
/**
|
||||
* Provides models for the `flask` PyPI package.
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/.
|
||||
*/
|
||||
private module Flask {
|
||||
/** Gets a reference to the `flask` module. */
|
||||
DataFlow::Node flask(DataFlow::TypeTracker t) {
|
||||
@@ -23,6 +28,7 @@ private module Flask {
|
||||
/** Gets a reference to the `flask` module. */
|
||||
DataFlow::Node flask() { result = flask(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Provides models for the `flask` module. */
|
||||
module flask {
|
||||
/** Gets a reference to the `flask.request` object. */
|
||||
DataFlow::Node request(DataFlow::TypeTracker t) {
|
||||
@@ -32,13 +38,154 @@ private module Flask {
|
||||
t.startInAttr("request") and
|
||||
result = flask()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = flask::request(t2).track(t2, t))
|
||||
exists(DataFlow::TypeTracker t2 | result = request(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `flask.request` object. */
|
||||
DataFlow::Node request() { result = flask::request(DataFlow::TypeTracker::end()) }
|
||||
DataFlow::Node request() { result = request(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to the `flask.Flask` class. */
|
||||
private DataFlow::Node classFlask(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = DataFlow::importNode("flask.Flask")
|
||||
or
|
||||
t.startInAttr("Flask") and
|
||||
result = flask()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = classFlask(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `flask.Flask` class. */
|
||||
DataFlow::Node classFlask() { result = classFlask(DataFlow::TypeTracker::end()) }
|
||||
|
||||
/** Gets a reference to an instance of `flask.Flask` (a Flask application). */
|
||||
private DataFlow::Node app(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asCfgNode().(CallNode).getFunction() = flask::classFlask().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = app(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
|
||||
DataFlow::Node app() { result = app(DataFlow::TypeTracker::end()) }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// routing modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of a flask application.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node app_attr(DataFlow::TypeTracker t, string attr_name) {
|
||||
attr_name in ["route", "add_url_rule"] and
|
||||
t.startInAttr(attr_name) and
|
||||
result = flask::app()
|
||||
or
|
||||
// Due to bad performance when using normal setup with `app_attr(t2, attr_name).track(t2, t)`
|
||||
// we have inlined that code and forced a join
|
||||
exists(DataFlow::TypeTracker t2 |
|
||||
exists(DataFlow::StepSummary summary |
|
||||
app_attr_first_join(t2, attr_name, result, summary) and
|
||||
t = t2.append(summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate app_attr_first_join(
|
||||
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
|
||||
) {
|
||||
DataFlow::StepSummary::step(app_attr(t2, attr_name), res, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the attribute `attr_name` of a flask application.
|
||||
* WARNING: Only holds for a few predefined attributes.
|
||||
*/
|
||||
private DataFlow::Node app_attr(string attr_name) {
|
||||
result = app_attr(DataFlow::TypeTracker::end(), attr_name)
|
||||
}
|
||||
|
||||
private string werkzeug_rule_re() {
|
||||
// since flask uses werkzeug internally, we are using its routing rules from
|
||||
// https://github.com/pallets/werkzeug/blob/4dc8d6ab840d4b78cbd5789cef91b01e3bde01d5/src/werkzeug/routing.py#L138-L151
|
||||
result =
|
||||
"(?<static>[^<]*)<(?:(?<converter>[a-zA-Z_][a-zA-Z0-9_]*)(?:\\((?<args>.*?)\\))?\\:)?(?<variable>[a-zA-Z_][a-zA-Z0-9_]*)>"
|
||||
}
|
||||
|
||||
/** A route setup made by flask (sharing handling of URL patterns). */
|
||||
abstract private class FlaskRouteSetup extends HTTP::Server::RouteSetup::Range {
|
||||
override Parameter getARoutedParameter() {
|
||||
// If we don't know the URL pattern, we simply mark all parameters as a routed
|
||||
// parameter. This should give us more RemoteFlowSources but could also lead to
|
||||
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
|
||||
not exists(this.getUrlPattern()) and
|
||||
result = this.getARouteHandler().getArgByName(_)
|
||||
or
|
||||
exists(string name |
|
||||
result = this.getARouteHandler().getArgByName(name) and
|
||||
exists(string match |
|
||||
match = this.getUrlPattern().regexpFind(werkzeug_rule_re(), _, _) and
|
||||
name = match.regexpCapture(werkzeug_rule_re(), 4)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the argument used to pass in the URL pattern. */
|
||||
abstract DataFlow::Node getUrlPatternArg();
|
||||
|
||||
override string getUrlPattern() {
|
||||
exists(StrConst str |
|
||||
DataFlow::localFlow(DataFlow::exprNode(str), this.getUrlPatternArg()) and
|
||||
result = str.getText()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `flask.Flask.route`.
|
||||
*
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.route
|
||||
*/
|
||||
private class FlaskAppRouteCall extends FlaskRouteSetup, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
FlaskAppRouteCall() { node.getFunction() = app_attr("route").asCfgNode() }
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
|
||||
}
|
||||
|
||||
override Function getARouteHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `flask.Flask.add_url_rule`.
|
||||
*
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.add_url_rule
|
||||
*/
|
||||
private class FlaskAppAddUrlRule extends FlaskRouteSetup, DataFlow::CfgNode {
|
||||
override CallNode node;
|
||||
|
||||
FlaskAppAddUrlRule() { node.getFunction() = app_attr("add_url_rule").asCfgNode() }
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() {
|
||||
result.asCfgNode() in [node.getArg(0), node.getArgByName("rule")]
|
||||
}
|
||||
|
||||
override Function getARouteHandler() {
|
||||
exists(DataFlow::Node view_func_arg, DataFlow::Node func_src |
|
||||
view_func_arg.asCfgNode() in [node.getArg(2), node.getArgByName("view_func")] and
|
||||
DataFlow::localFlow(func_src, view_func_arg) and
|
||||
func_src.asExpr().(CallableExpr) = result.getDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// flask.Request taint modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
// TODO: Do we even need this class? :|
|
||||
/**
|
||||
* A source of remote flow from a flask request.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import python
|
||||
import experimental.meta.ConceptsTest
|
||||
@@ -1,98 +1,98 @@
|
||||
| test.py:6 | fail | test_taint | name |
|
||||
| test.py:6 | fail | test_taint | number |
|
||||
| test.py:7 | ok | test_taint | foo |
|
||||
| test.py:14 | ok | test_taint | request.environ |
|
||||
| test.py:15 | ok | test_taint | request.environ.get(..) |
|
||||
| test.py:17 | ok | test_taint | request.path |
|
||||
| test.py:18 | ok | test_taint | request.full_path |
|
||||
| test.py:19 | ok | test_taint | request.base_url |
|
||||
| test.py:20 | ok | test_taint | request.url |
|
||||
| test.py:23 | fail | test_taint | request.accept_charsets.best |
|
||||
| test.py:24 | fail | test_taint | request.accept_charsets.best_match(..) |
|
||||
| test.py:25 | ok | test_taint | request.accept_charsets[0] |
|
||||
| test.py:26 | ok | test_taint | request.accept_encodings |
|
||||
| test.py:27 | ok | test_taint | request.accept_languages |
|
||||
| test.py:28 | ok | test_taint | request.accept_mimetypes |
|
||||
| test.py:31 | ok | test_taint | request.access_control_request_headers |
|
||||
| test.py:33 | ok | test_taint | request.access_control_request_method |
|
||||
| test.py:35 | ok | test_taint | request.access_route |
|
||||
| test.py:36 | ok | test_taint | request.access_route[0] |
|
||||
| test.py:39 | ok | test_taint | request.args |
|
||||
| test.py:40 | ok | test_taint | request.args['key'] |
|
||||
| test.py:41 | ok | test_taint | request.args.getlist(..) |
|
||||
| test.py:44 | ok | test_taint | request.authorization |
|
||||
| test.py:45 | ok | test_taint | request.authorization['username'] |
|
||||
| test.py:46 | fail | test_taint | request.authorization.username |
|
||||
| test.py:49 | ok | test_taint | request.cache_control |
|
||||
| test.py:51 | fail | test_taint | request.cache_control.max_age |
|
||||
| test.py:52 | fail | test_taint | request.cache_control.max_stale |
|
||||
| test.py:53 | fail | test_taint | request.cache_control.min_fresh |
|
||||
| test.py:55 | ok | test_taint | request.content_encoding |
|
||||
| test.py:57 | ok | test_taint | request.content_md5 |
|
||||
| test.py:59 | ok | test_taint | request.content_type |
|
||||
| test.py:62 | ok | test_taint | request.cookies |
|
||||
| test.py:63 | ok | test_taint | request.cookies['key'] |
|
||||
| test.py:65 | ok | test_taint | request.data |
|
||||
| test.py:68 | ok | test_taint | request.files |
|
||||
| test.py:69 | ok | test_taint | request.files['key'] |
|
||||
| test.py:70 | fail | test_taint | request.files['key'].filename |
|
||||
| test.py:71 | fail | test_taint | request.files['key'].stream |
|
||||
| test.py:72 | ok | test_taint | request.files.getlist(..) |
|
||||
| test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename |
|
||||
| test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream |
|
||||
| test.py:77 | ok | test_taint | request.form |
|
||||
| test.py:78 | ok | test_taint | request.form['key'] |
|
||||
| test.py:79 | ok | test_taint | request.form.getlist(..) |
|
||||
| test.py:81 | ok | test_taint | request.get_data() |
|
||||
| test.py:83 | ok | test_taint | request.get_json() |
|
||||
| test.py:84 | ok | test_taint | request.get_json()['foo'] |
|
||||
| test.py:85 | ok | test_taint | request.get_json()['foo']['bar'] |
|
||||
| test.py:89 | ok | test_taint | request.headers |
|
||||
| test.py:90 | ok | test_taint | request.headers['key'] |
|
||||
| test.py:91 | fail | test_taint | request.headers.get_all(..) |
|
||||
| test.py:92 | fail | test_taint | request.headers.getlist(..) |
|
||||
| test.py:93 | ok | test_taint | list(..) |
|
||||
| test.py:94 | fail | test_taint | request.headers.to_wsgi_list() |
|
||||
| test.py:96 | ok | test_taint | request.json |
|
||||
| test.py:97 | ok | test_taint | request.json['foo'] |
|
||||
| test.py:98 | ok | test_taint | request.json['foo']['bar'] |
|
||||
| test.py:100 | ok | test_taint | request.method |
|
||||
| test.py:102 | ok | test_taint | request.mimetype |
|
||||
| test.py:104 | ok | test_taint | request.mimetype_params |
|
||||
| test.py:106 | ok | test_taint | request.origin |
|
||||
| test.py:109 | ok | test_taint | request.pragma |
|
||||
| test.py:111 | ok | test_taint | request.query_string |
|
||||
| test.py:113 | ok | test_taint | request.referrer |
|
||||
| test.py:115 | ok | test_taint | request.remote_addr |
|
||||
| test.py:117 | ok | test_taint | request.remote_user |
|
||||
| test.py:120 | ok | test_taint | request.stream |
|
||||
| test.py:121 | ok | test_taint | request.input_stream |
|
||||
| test.py:123 | ok | test_taint | request.url |
|
||||
| test.py:125 | ok | test_taint | request.user_agent |
|
||||
| test.py:128 | ok | test_taint | request.values |
|
||||
| test.py:129 | ok | test_taint | request.values['key'] |
|
||||
| test.py:130 | ok | test_taint | request.values.getlist(..) |
|
||||
| test.py:133 | ok | test_taint | request.view_args |
|
||||
| test.py:134 | ok | test_taint | request.view_args['key'] |
|
||||
| test.py:138 | ok | test_taint | request.script_root |
|
||||
| test.py:139 | ok | test_taint | request.url_root |
|
||||
| test.py:143 | ok | test_taint | request.charset |
|
||||
| test.py:144 | ok | test_taint | request.url_charset |
|
||||
| test.py:148 | ok | test_taint | request.date |
|
||||
| test.py:151 | ok | test_taint | request.endpoint |
|
||||
| test.py:156 | ok | test_taint | request.host |
|
||||
| test.py:157 | ok | test_taint | request.host_url |
|
||||
| test.py:159 | ok | test_taint | request.scheme |
|
||||
| test.py:161 | ok | test_taint | request.script_root |
|
||||
| test.py:169 | ok | test_taint | request.args |
|
||||
| test.py:170 | ok | test_taint | a |
|
||||
| test.py:171 | ok | test_taint | b |
|
||||
| test.py:173 | ok | test_taint | request.args['key'] |
|
||||
| test.py:174 | ok | test_taint | a['key'] |
|
||||
| test.py:175 | ok | test_taint | b['key'] |
|
||||
| test.py:177 | ok | test_taint | request.args.getlist(..) |
|
||||
| test.py:178 | ok | test_taint | a.getlist(..) |
|
||||
| test.py:179 | ok | test_taint | b.getlist(..) |
|
||||
| test.py:180 | ok | test_taint | gl(..) |
|
||||
| test.py:187 | ok | test_taint | req.path |
|
||||
| test.py:188 | ok | test_taint | gd() |
|
||||
| taint_test.py:6 | ok | test_taint | name |
|
||||
| taint_test.py:6 | ok | test_taint | number |
|
||||
| taint_test.py:7 | ok | test_taint | foo |
|
||||
| taint_test.py:14 | ok | test_taint | request.environ |
|
||||
| taint_test.py:15 | ok | test_taint | request.environ.get(..) |
|
||||
| taint_test.py:17 | ok | test_taint | request.path |
|
||||
| taint_test.py:18 | ok | test_taint | request.full_path |
|
||||
| taint_test.py:19 | ok | test_taint | request.base_url |
|
||||
| taint_test.py:20 | ok | test_taint | request.url |
|
||||
| taint_test.py:23 | fail | test_taint | request.accept_charsets.best |
|
||||
| taint_test.py:24 | fail | test_taint | request.accept_charsets.best_match(..) |
|
||||
| taint_test.py:25 | ok | test_taint | request.accept_charsets[0] |
|
||||
| taint_test.py:26 | ok | test_taint | request.accept_encodings |
|
||||
| taint_test.py:27 | ok | test_taint | request.accept_languages |
|
||||
| taint_test.py:28 | ok | test_taint | request.accept_mimetypes |
|
||||
| taint_test.py:31 | ok | test_taint | request.access_control_request_headers |
|
||||
| taint_test.py:33 | ok | test_taint | request.access_control_request_method |
|
||||
| taint_test.py:35 | ok | test_taint | request.access_route |
|
||||
| taint_test.py:36 | ok | test_taint | request.access_route[0] |
|
||||
| taint_test.py:39 | ok | test_taint | request.args |
|
||||
| taint_test.py:40 | ok | test_taint | request.args['key'] |
|
||||
| taint_test.py:41 | ok | test_taint | request.args.getlist(..) |
|
||||
| taint_test.py:44 | ok | test_taint | request.authorization |
|
||||
| taint_test.py:45 | ok | test_taint | request.authorization['username'] |
|
||||
| taint_test.py:46 | fail | test_taint | request.authorization.username |
|
||||
| taint_test.py:49 | ok | test_taint | request.cache_control |
|
||||
| taint_test.py:51 | fail | test_taint | request.cache_control.max_age |
|
||||
| taint_test.py:52 | fail | test_taint | request.cache_control.max_stale |
|
||||
| taint_test.py:53 | fail | test_taint | request.cache_control.min_fresh |
|
||||
| taint_test.py:55 | ok | test_taint | request.content_encoding |
|
||||
| taint_test.py:57 | ok | test_taint | request.content_md5 |
|
||||
| taint_test.py:59 | ok | test_taint | request.content_type |
|
||||
| taint_test.py:62 | ok | test_taint | request.cookies |
|
||||
| taint_test.py:63 | ok | test_taint | request.cookies['key'] |
|
||||
| taint_test.py:65 | ok | test_taint | request.data |
|
||||
| taint_test.py:68 | ok | test_taint | request.files |
|
||||
| taint_test.py:69 | ok | test_taint | request.files['key'] |
|
||||
| taint_test.py:70 | fail | test_taint | request.files['key'].filename |
|
||||
| taint_test.py:71 | fail | test_taint | request.files['key'].stream |
|
||||
| taint_test.py:72 | ok | test_taint | request.files.getlist(..) |
|
||||
| taint_test.py:73 | fail | test_taint | request.files.getlist(..)[0].filename |
|
||||
| taint_test.py:74 | fail | test_taint | request.files.getlist(..)[0].stream |
|
||||
| taint_test.py:77 | ok | test_taint | request.form |
|
||||
| taint_test.py:78 | ok | test_taint | request.form['key'] |
|
||||
| taint_test.py:79 | ok | test_taint | request.form.getlist(..) |
|
||||
| taint_test.py:81 | ok | test_taint | request.get_data() |
|
||||
| taint_test.py:83 | ok | test_taint | request.get_json() |
|
||||
| taint_test.py:84 | ok | test_taint | request.get_json()['foo'] |
|
||||
| taint_test.py:85 | ok | test_taint | request.get_json()['foo']['bar'] |
|
||||
| taint_test.py:89 | ok | test_taint | request.headers |
|
||||
| taint_test.py:90 | ok | test_taint | request.headers['key'] |
|
||||
| taint_test.py:91 | fail | test_taint | request.headers.get_all(..) |
|
||||
| taint_test.py:92 | fail | test_taint | request.headers.getlist(..) |
|
||||
| taint_test.py:93 | ok | test_taint | list(..) |
|
||||
| taint_test.py:94 | fail | test_taint | request.headers.to_wsgi_list() |
|
||||
| taint_test.py:96 | ok | test_taint | request.json |
|
||||
| taint_test.py:97 | ok | test_taint | request.json['foo'] |
|
||||
| taint_test.py:98 | ok | test_taint | request.json['foo']['bar'] |
|
||||
| taint_test.py:100 | ok | test_taint | request.method |
|
||||
| taint_test.py:102 | ok | test_taint | request.mimetype |
|
||||
| taint_test.py:104 | ok | test_taint | request.mimetype_params |
|
||||
| taint_test.py:106 | ok | test_taint | request.origin |
|
||||
| taint_test.py:109 | ok | test_taint | request.pragma |
|
||||
| taint_test.py:111 | ok | test_taint | request.query_string |
|
||||
| taint_test.py:113 | ok | test_taint | request.referrer |
|
||||
| taint_test.py:115 | ok | test_taint | request.remote_addr |
|
||||
| taint_test.py:117 | ok | test_taint | request.remote_user |
|
||||
| taint_test.py:120 | ok | test_taint | request.stream |
|
||||
| taint_test.py:121 | ok | test_taint | request.input_stream |
|
||||
| taint_test.py:123 | ok | test_taint | request.url |
|
||||
| taint_test.py:125 | ok | test_taint | request.user_agent |
|
||||
| taint_test.py:128 | ok | test_taint | request.values |
|
||||
| taint_test.py:129 | ok | test_taint | request.values['key'] |
|
||||
| taint_test.py:130 | ok | test_taint | request.values.getlist(..) |
|
||||
| taint_test.py:133 | ok | test_taint | request.view_args |
|
||||
| taint_test.py:134 | ok | test_taint | request.view_args['key'] |
|
||||
| taint_test.py:138 | ok | test_taint | request.script_root |
|
||||
| taint_test.py:139 | ok | test_taint | request.url_root |
|
||||
| taint_test.py:143 | ok | test_taint | request.charset |
|
||||
| taint_test.py:144 | ok | test_taint | request.url_charset |
|
||||
| taint_test.py:148 | ok | test_taint | request.date |
|
||||
| taint_test.py:151 | ok | test_taint | request.endpoint |
|
||||
| taint_test.py:156 | ok | test_taint | request.host |
|
||||
| taint_test.py:157 | ok | test_taint | request.host_url |
|
||||
| taint_test.py:159 | ok | test_taint | request.scheme |
|
||||
| taint_test.py:161 | ok | test_taint | request.script_root |
|
||||
| taint_test.py:169 | ok | test_taint | request.args |
|
||||
| taint_test.py:170 | ok | test_taint | a |
|
||||
| taint_test.py:171 | ok | test_taint | b |
|
||||
| taint_test.py:173 | ok | test_taint | request.args['key'] |
|
||||
| taint_test.py:174 | ok | test_taint | a['key'] |
|
||||
| taint_test.py:175 | ok | test_taint | b['key'] |
|
||||
| taint_test.py:177 | ok | test_taint | request.args.getlist(..) |
|
||||
| taint_test.py:178 | ok | test_taint | a.getlist(..) |
|
||||
| taint_test.py:179 | ok | test_taint | b.getlist(..) |
|
||||
| taint_test.py:180 | ok | test_taint | gl(..) |
|
||||
| taint_test.py:187 | ok | test_taint | req.path |
|
||||
| taint_test.py:188 | ok | test_taint | gd() |
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import flask
|
||||
|
||||
from flask import Flask, request, make_response
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/") # $routeSetup="/"
|
||||
def hello_world(): # $routeHandler
|
||||
return "Hello World!"
|
||||
|
||||
from flask.views import MethodView
|
||||
|
||||
class MyView(MethodView):
|
||||
|
||||
def get(self, user_id): # $f-:routeHandler
|
||||
if user_id is None:
|
||||
# return a list of users
|
||||
pass
|
||||
else:
|
||||
# expose a single user
|
||||
pass
|
||||
|
||||
the_view = MyView.as_view('my_view')
|
||||
|
||||
app.add_url_rule('/the/', defaults={'user_id': None}, # $routeSetup="/the/"
|
||||
view_func=the_view, methods=['GET',])
|
||||
|
||||
@app.route("/dangerous") # $routeSetup="/dangerous"
|
||||
def dangerous(): # $routeHandler
|
||||
return request.args.get('payload')
|
||||
|
||||
@app.route("/dangerous-with-cfg-split") # $routeSetup="/dangerous-with-cfg-split"
|
||||
def dangerous2(): # $routeHandler
|
||||
x = request.form['param0']
|
||||
if request.method == "POST":
|
||||
return request.form['param1']
|
||||
return None
|
||||
|
||||
@app.route("/unsafe") # $routeSetup="/unsafe"
|
||||
def unsafe(): # $routeHandler
|
||||
first_name = request.args.get('name', '')
|
||||
return make_response("Your name is " + first_name)
|
||||
|
||||
@app.route("/safe") # $routeSetup="/safe"
|
||||
def safe(): # $routeHandler
|
||||
first_name = request.args.get('name', '')
|
||||
return make_response("Your name is " + escape(first_name))
|
||||
|
||||
@app.route("/hello/<name>") # $routeSetup="/hello/<name>"
|
||||
def hello(name): # $routeHandler $routedParameter=name
|
||||
return make_response("Your name is " + name)
|
||||
|
||||
@app.route("/foo/<path:subpath>") # $routeSetup="/foo/<path:subpath>"
|
||||
def foo(subpath): # $routeHandler $routedParameter=subpath
|
||||
return make_response("The subpath is " + subpath)
|
||||
|
||||
@app.route("/multiple/") # $routeSetup="/multiple/"
|
||||
@app.route("/multiple/foo/<foo>") # $routeSetup="/multiple/foo/<foo>"
|
||||
@app.route("/multiple/bar/<bar>") # $routeSetup="/multiple/bar/<bar>"
|
||||
def multiple(foo=None, bar=None): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
return make_response("foo={!r} bar={!r}".format(foo, bar))
|
||||
|
||||
@app.route("/complex/<string(length=2):lang_code>") # $routeSetup="/complex/<string(length=2):lang_code>"
|
||||
def complex(lang_code): # $routeHandler $routedParameter=lang_code
|
||||
return make_response("lang_code {}".format(lang_code))
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
@@ -0,0 +1,31 @@
|
||||
import flask
|
||||
|
||||
from flask import Flask, make_response
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
SOME_ROUTE = "/some/route"
|
||||
@app.route(SOME_ROUTE) # $routeSetup="/some/route"
|
||||
def some_route(): # $routeHandler
|
||||
return make_response("some_route")
|
||||
|
||||
|
||||
def index(): # $routeHandler
|
||||
return make_response("index")
|
||||
app.add_url_rule('/index', 'index', index) # $routeSetup="/index"
|
||||
|
||||
|
||||
# We don't support this yet, and I think that's OK
|
||||
def later_set(): # $f-:routeHandler
|
||||
return make_response("later_set")
|
||||
app.add_url_rule('/later-set', 'later_set', view_func=None) # $routeSetup="/later-set"
|
||||
app.view_functions['later_set'] = later_set
|
||||
|
||||
|
||||
@app.route(UNKNOWN_ROUTE) # $routeSetup
|
||||
def unkown_route(foo, bar): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
return make_response("unkown_route")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
@@ -1,8 +1,8 @@
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/test_taint/<name>/<int:number>')
|
||||
def test_taint(name = "World!", number="0", foo="foo"):
|
||||
@app.route("/test_taint/<name>/<int:number>") # $routeSetup="/test_taint/<name>/<int:number>"
|
||||
def test_taint(name = "World!", number="0", foo="foo"): # $routeHandler $routedParameter=name $routedParameter=number
|
||||
ensure_tainted(name, number)
|
||||
ensure_not_tainted(foo)
|
||||
|
||||
@@ -191,8 +191,8 @@ def test_taint(name = "World!", number="0", foo="foo"):
|
||||
|
||||
|
||||
|
||||
@app.route('/debug/<foo>/<bar>', methods=['GET'])
|
||||
def debug(foo, bar):
|
||||
@app.route("/debug/<foo>/<bar>", methods=['GET']) # $routeSetup="/debug/<foo>/<bar>"
|
||||
def debug(foo, bar): # $routeHandler $routedParameter=foo $routedParameter=bar
|
||||
print("request.view_args", request.view_args)
|
||||
|
||||
print("request.headers {!r}".format(request.headers))
|
||||
@@ -202,8 +202,8 @@ def debug(foo, bar):
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/stream', methods=['POST'])
|
||||
def stream():
|
||||
@app.route("/stream", methods=['POST']) # $routeSetup="/stream"
|
||||
def stream(): # $routeHandler
|
||||
print(request.path)
|
||||
s = request.stream
|
||||
print(s)
|
||||
@@ -212,8 +212,8 @@ def stream():
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/input_stream', methods=['POST'])
|
||||
def input_stream():
|
||||
@app.route("/input_stream", methods=['POST']) # $routeSetup="/input_stream"
|
||||
def input_stream(): # $routeHandler
|
||||
print(request.path)
|
||||
s = request.input_stream
|
||||
print(s)
|
||||
@@ -223,15 +223,15 @@ def input_stream():
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/form', methods=['POST'])
|
||||
def form():
|
||||
@app.route("/form", methods=['POST']) # $routeSetup="/form"
|
||||
def form(): # $routeHandler
|
||||
print(request.path)
|
||||
print("request.form", request.form)
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/cache_control', methods=['POST'])
|
||||
def cache_control():
|
||||
@app.route("/cache_control", methods=['POST']) # $routeSetup="/cache_control"
|
||||
def cache_control(): # $routeHandler
|
||||
print(request.path)
|
||||
print("request.cache_control.max_age", request.cache_control.max_age, type(request.cache_control.max_age))
|
||||
print("request.cache_control.max_stale", request.cache_control.max_stale, type(request.cache_control.max_stale))
|
||||
@@ -239,16 +239,16 @@ def cache_control():
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/file_upload', methods=['POST'])
|
||||
def file_upload():
|
||||
@app.route("/file_upload", methods=['POST']) # $routeSetup="/file_upload"
|
||||
def file_upload(): # $routeHandler
|
||||
print(request.path)
|
||||
for k,v in request.files.items():
|
||||
print(k, v, v.name, v.filename, v.stream)
|
||||
|
||||
return 'ok'
|
||||
|
||||
@app.route('/args', methods=['GET'])
|
||||
def args():
|
||||
@app.route("/args", methods=['GET']) # $routeSetup="/args"
|
||||
def args(): # $routeHandler
|
||||
print(request.path)
|
||||
print("request.args", request.args)
|
||||
|
||||
@@ -22,8 +22,8 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
override string getARelevantTag() { result = "getCommand" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(SystemCommandExecution sce, DataFlow::Node command |
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
command = sce.getCommand() and
|
||||
location = command.getLocation() and
|
||||
element = command.toString() and
|
||||
@@ -32,3 +32,39 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class HttpServerRouteSetupTest extends InlineExpectationsTest {
|
||||
HttpServerRouteSetupTest() { this = "HttpServerRouteSetupTest" }
|
||||
|
||||
override string getARelevantTag() { result in ["routeSetup", "routeHandler", "routedParameter"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(HTTP::Server::RouteSetup setup |
|
||||
location = setup.getLocation() and
|
||||
element = setup.toString() and
|
||||
(
|
||||
value = "\"" + setup.getUrlPattern() + "\""
|
||||
or
|
||||
not exists(setup.getUrlPattern()) and
|
||||
value = ""
|
||||
) and
|
||||
tag = "routeSetup"
|
||||
)
|
||||
or
|
||||
exists(HTTP::Server::RouteSetup setup, Function func |
|
||||
func = setup.getARouteHandler() and
|
||||
location = func.getLocation() and
|
||||
element = func.toString() and
|
||||
value = "" and
|
||||
tag = "routeHandler"
|
||||
)
|
||||
or
|
||||
exists(HTTP::Server::RouteSetup setup, Parameter param |
|
||||
param = setup.getARoutedParameter() and
|
||||
location = param.getLocation() and
|
||||
element = param.toString() and
|
||||
value = param.asName().getId() and
|
||||
tag = "routedParameter"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user