From eaec29083417bc25998e316f641af9aac604df1b Mon Sep 17 00:00:00 2001 From: Daniel Gaspar Date: Fri, 17 Jun 2022 11:53:06 +0100 Subject: [PATCH 1/5] fix(MVC): discard excluded filters from query --- flask_appbuilder/urltools.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/flask_appbuilder/urltools.py b/flask_appbuilder/urltools.py index 32bd3b161d..59adebf22f 100644 --- a/flask_appbuilder/urltools.py +++ b/flask_appbuilder/urltools.py @@ -1,7 +1,10 @@ +import logging import re from flask import request +log = logging.getLogger(__name__) + class Stack(object): """ @@ -96,7 +99,16 @@ def get_filter_args(filters): request_args = set(request.args) for arg in request_args: re_match = re.findall(r"_flt_(\d)_(.*)", arg) + try: + filter_index = int(re_match[0][0]) + except ValueError: + log.warning("Invalid filter index") + continue + filter_column = re_match[0][1] + if filter_column not in filters.get_search_filters().keys(): + log.warning("Filter column not allowed") + continue if re_match: filters.add_filter_index( - re_match[0][1], int(re_match[0][0]), request.args.getlist(arg) + filter_column, filter_index, request.args.getlist(arg) ) From db6b7c9af27e58e0f82ee4e1388387bc3a97d387 Mon Sep 17 00:00:00 2001 From: Daniel Gaspar Date: Tue, 21 Jun 2022 09:31:11 +0100 Subject: [PATCH 2/5] fix get_filter_args --- flask_appbuilder/urltools.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask_appbuilder/urltools.py b/flask_appbuilder/urltools.py index 59adebf22f..264c86148d 100644 --- a/flask_appbuilder/urltools.py +++ b/flask_appbuilder/urltools.py @@ -99,6 +99,8 @@ def get_filter_args(filters): request_args = set(request.args) for arg in request_args: re_match = re.findall(r"_flt_(\d)_(.*)", arg) + if not re_match: + continue try: filter_index = int(re_match[0][0]) except ValueError: From d383a7c63763aed38eaf1b7025cdfa364161d321 Mon Sep 17 00:00:00 2001 From: Daniel Gaspar Date: Tue, 21 Jun 2022 12:13:40 +0100 Subject: [PATCH 3/5] add tests --- flask_appbuilder/tests/test_urltools.py | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 flask_appbuilder/tests/test_urltools.py diff --git a/flask_appbuilder/tests/test_urltools.py b/flask_appbuilder/tests/test_urltools.py new file mode 100644 index 0000000000..b455db9a55 --- /dev/null +++ b/flask_appbuilder/tests/test_urltools.py @@ -0,0 +1,55 @@ +import os + +from flask import Flask +from flask_appbuilder import AppBuilder, SQLA +from flask_appbuilder.models.sqla.interface import SQLAInterface +from flask_appbuilder.tests.sqla.models import Model1 +from flask_appbuilder.urltools import get_filter_args + +from .base import FABTestCase + + +class FlaskTestCase(FABTestCase): + def setUp(self): + self.app = Flask(__name__) + self.basedir = os.path.abspath(os.path.dirname(__file__)) + self.app.config.from_object("flask_appbuilder.tests.config_api") + + self.db = SQLA(self.app) + self.appbuilder = AppBuilder(self.app, self.db.session) + + def test_get_filter_args_allow_one(self): + datamodel = SQLAInterface(Model1) + with self.appbuilder.get_app.test_request_context( + "/users/list?_flt_1_field_string=a" + ): + filters = datamodel.get_filters(["field_string", "field_integer"]) + get_filter_args(filters) + assert filters.values == [["a"]] + + def test_get_filter_args_allow_multiple(self): + datamodel = SQLAInterface(Model1) + with self.appbuilder.get_app.test_request_context( + "/users/list?_flt_1_field_string=a&_flt_1_field_integer=2" + ): + filters = datamodel.get_filters(["field_string", "field_integer"]) + get_filter_args(filters) + assert filters.values == [["a"], ["2"]] + + def test_get_filter_args_disallow(self): + datamodel = SQLAInterface(Model1) + with self.appbuilder.get_app.test_request_context( + "/users/list?_flt_1_field_float=1.0" + ): + filters = datamodel.get_filters(["field_string", "field_integer"]) + get_filter_args(filters) + assert filters.values == [] + + def test_get_filter_args_invalid_index(self): + datamodel = SQLAInterface(Model1) + with self.appbuilder.get_app.test_request_context( + "/users/list?_flt_a_field_string=a" + ): + filters = datamodel.get_filters(["field_string", "field_integer"]) + get_filter_args(filters) + assert filters.values == [] From 618c42e355b8fe6863cfdb4e2b657e671d4381d0 Mon Sep 17 00:00:00 2001 From: Daniel Gaspar Date: Tue, 21 Jun 2022 12:24:36 +0100 Subject: [PATCH 4/5] fix flaky test --- flask_appbuilder/tests/test_urltools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_appbuilder/tests/test_urltools.py b/flask_appbuilder/tests/test_urltools.py index b455db9a55..3dc6033ffd 100644 --- a/flask_appbuilder/tests/test_urltools.py +++ b/flask_appbuilder/tests/test_urltools.py @@ -34,7 +34,7 @@ def test_get_filter_args_allow_multiple(self): ): filters = datamodel.get_filters(["field_string", "field_integer"]) get_filter_args(filters) - assert filters.values == [["a"], ["2"]] + assert filters.values in ([["a"], ["2"]], [["2"], ["a"]]) def test_get_filter_args_disallow(self): datamodel = SQLAInterface(Model1) From ca8a6e23c31ce61f0cbe6b4f269a4e2021a21101 Mon Sep 17 00:00:00 2001 From: Daniel Gaspar Date: Tue, 21 Jun 2022 13:05:08 +0100 Subject: [PATCH 5/5] remove unnecessary try except --- flask_appbuilder/urltools.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flask_appbuilder/urltools.py b/flask_appbuilder/urltools.py index 264c86148d..f028a17f85 100644 --- a/flask_appbuilder/urltools.py +++ b/flask_appbuilder/urltools.py @@ -101,11 +101,7 @@ def get_filter_args(filters): re_match = re.findall(r"_flt_(\d)_(.*)", arg) if not re_match: continue - try: - filter_index = int(re_match[0][0]) - except ValueError: - log.warning("Invalid filter index") - continue + filter_index = int(re_match[0][0]) filter_column = re_match[0][1] if filter_column not in filters.get_search_filters().keys(): log.warning("Filter column not allowed")