diff --git a/datasette/filters.py b/datasette/filters.py index 0499d086..5352c691 100644 --- a/datasette/filters.py +++ b/datasette/filters.py @@ -66,11 +66,12 @@ def search_filters(request, database, table, datasette): requested_fts_pk and requested_fts_pk != fts_pk ): raise BadRequest("Invalid _fts_table or _fts_pk") - search_args = { - key: request.args[key] - for key in request.args - if key.startswith("_search") and key != "_searchmode" - } + search_args = {} + for key in request.args: + if key == "_search" or key.startswith("_search_"): + search_args[key] = request.args[key] + elif key != "_searchmode" and key.startswith("_search"): + raise BadRequest(f"Invalid _search parameter: {key}") search = "" search_mode_raw = table_metadata.get("searchmode") == "raw" # Or set search mode from the querystring diff --git a/tests/test_filters.py b/tests/test_filters.py index 9f201fdf..6b739e12 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -165,3 +165,33 @@ async def test_search_filters_from_request(ds_client): assert filter_args.params == {"search": "bobcat"} assert filter_args.human_descriptions == ['search matches "bobcat"'] assert filter_args.extra_context == {"supports_search": True, "search": "bobcat"} + + +@pytest.mark.asyncio +@pytest.mark.parametrize("key", ("_searchfoo", "_search2")) +async def test_search_invalid_parameter(ds_client, key): + # ?_searchfoo= used to crash with an IndexError from + # key.split("_search_", 1)[1] - it should return a 400 like every other + # invalid search parameter + response = await ds_client.get(f"/fixtures/searchable.json?{key}=bobcat") + assert response.status_code == 400 + assert response.json() == { + "ok": False, + "error": f"Invalid _search parameter: {key}", + "errors": [f"Invalid _search parameter: {key}"], + "status": 400, + } + + +@pytest.mark.asyncio +async def test_search_valid_parameters_still_work(ds_client): + # The three documented search parameters keep working + response = await ds_client.get( + "/fixtures/searchable.json?_shape=arrays&_search=weasel&_searchmode=raw" + ) + assert response.status_code == 200 + assert response.json()["rows"] == [[2, "terry dog", "sara weasel", "puma"]] + response = await ds_client.get( + "/fixtures/searchable.json?_shape=arrays&_search_text2=dog" + ) + assert response.status_code == 200