From 58246f0fd4913a00a7bda7cfdc233dc53eb787a5 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Mon, 8 Jun 2026 09:45:55 -0700 Subject: [PATCH] fix(jinja): apply consistent value handling to url_param across input sources url_param() returned the request.args value through an early return, skipping the dialect-specific quoting and cache-key handling that the form_data path applies. Funnel both input sources through the same tail so the returned value is handled consistently regardless of where the parameter originated. Co-Authored-By: Claude Opus 4.8 --- superset/jinja_context.py | 13 +++++++++---- tests/unit_tests/jinja_context_test.py | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/superset/jinja_context.py b/superset/jinja_context.py index 2a2e838708f..296769da0da 100644 --- a/superset/jinja_context.py +++ b/superset/jinja_context.py @@ -288,11 +288,16 @@ class ExtraCache: from superset.views.utils import get_form_data if has_request_context() and request.args.get(param): - return request.args.get(param, default) + result = request.args.get(param, default) + else: + form_data, _ = get_form_data() + url_params = form_data.get("url_params") or {} + result = url_params.get(param, default) - form_data, _ = get_form_data() - url_params = form_data.get("url_params") or {} - result = url_params.get(param, default) + # Apply the same handling to every input source. Values read from + # request.args must go through the dialect-specific quoting below just + # like values sourced from form_data, so the result is consistent + # regardless of where the parameter originated. if result and escape_result and self.dialect: # use the dialect specific quoting logic to escape string result = String().literal_processor(dialect=self.dialect)(value=result)[ diff --git a/tests/unit_tests/jinja_context_test.py b/tests/unit_tests/jinja_context_test.py index cfdbc2090d0..47c97991c84 100644 --- a/tests/unit_tests/jinja_context_test.py +++ b/tests/unit_tests/jinja_context_test.py @@ -438,6 +438,26 @@ def test_url_param_unescaped_default_form_data() -> None: assert cache.url_param("bar", "O'Malley", escape_result=False) == "O'Malley" +def test_url_param_escaped_query() -> None: + """ + Test that a ``url_param`` value read from the request query string is + handled the same way as one sourced from ``form_data`` -- i.e. it goes + through the dialect-specific quoting instead of being returned raw. + """ + with current_app.test_request_context(query_string={"foo": "O'Brien"}): + cache = ExtraCache(dialect=dialect()) + assert cache.url_param("foo") == "O''Brien" + + +def test_url_param_unescaped_query() -> None: + """ + Test that ``escape_result=False`` returns the raw query-string value. + """ + with current_app.test_request_context(query_string={"foo": "O'Brien"}): + cache = ExtraCache(dialect=dialect()) + assert cache.url_param("foo", escape_result=False) == "O'Brien" + + def test_safe_proxy_primitive() -> None: """ Test the ``safe_proxy`` helper with a function returning a ``str``.