diff --git a/CHANGELOG.md b/CHANGELOG.md index 07be8e1c..07a9564b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to this library are documented in this file. ### New Features -- Add `allow_list` option to `iemapp()` helper to stop lists. +- Add `allowed_as_list` option to `iemapp()` helper to stop lists. - Add support for plotting by FEMA Regions. ### Bug Fixes diff --git a/src/pyiem/webutil.py b/src/pyiem/webutil.py index 9338a4ab..5d3f56ad 100644 --- a/src/pyiem/webutil.py +++ b/src/pyiem/webutil.py @@ -402,7 +402,8 @@ def iemapp(**kwargs): the response, defaults to 3600. - content_type (str or callable): The content type to use for the response. - - allow_list (bool): Allow lists in the form, default True. + - allowed_as_list (list): CGI parameters that are permitted to be + lists. What all this does: 1) Attempts to catch database connection errors and handle nicely @@ -453,9 +454,12 @@ def _handle_exp(errormsg, routine=False, code=500): try: # mixed convers this to a regular dict form = parse_formvars(environ).mixed() - if not kwargs.get("allow_list", True): + allowed = kwargs.get("allowed_as_list", []) + if allowed: for key in form: - if isinstance(form[key], list): + if not isinstance(form[key], list): + continue + if key not in allowed: raise BadWebRequest( f"Key {key} is a list, but not allowed" ) diff --git a/tests/test_webutil.py b/tests/test_webutil.py index a28a8f17..d0eedb20 100644 --- a/tests/test_webutil.py +++ b/tests/test_webutil.py @@ -27,17 +27,24 @@ ) -def test_allow_list(): +def test_allowed_as_list(): """Test that we don't allow a list in the parsed form.""" - @iemapp(allow_list=False) + @iemapp(allowed_as_list=["q"]) def application(environ, _start_response): """Test.""" return f"{random.random()}" env = { "wsgi.input": mock.MagicMock(), - "QUERY_STRING": "f=1&f=2", + "QUERY_STRING": "q=1&q=2&f=1", + } + sr = mock.MagicMock() + res = application(env, sr) + assert res[0].find(b"Oopsy") == -1 + env = { + "wsgi.input": mock.MagicMock(), + "QUERY_STRING": "q=1&f=1&f=2", } sr = mock.MagicMock() res = application(env, sr) @@ -317,7 +324,9 @@ def application(environ, _start_response): def test_iemapp_iemdb_cursor(): """Test with a cursor set.""" - @iemapp(iemdb="mesosite", iemdb_cursorname="magic") + @iemapp( + iemdb="mesosite", iemdb_cursorname="magic", allowed_as_list=["year"] + ) def application(environ, _start_response): """Test.""" environ["iemdb.mesosite.cursor"].execute("SELECT 1 as test")