From e9e11e1b337c56a35dcbf2e8f5bffd0f9168c3b3 Mon Sep 17 00:00:00 2001 From: pgjones Date: Fri, 1 Jul 2022 10:02:25 +0100 Subject: [PATCH] Lowercase the parsed options names I cannot find a RFC that indicates that the option names are case sensitive whereas RFC6266 states that the `filename` option name is case insensitive. Therefore the best user experience is to lowercase the names. This will cause breaking changes if case sensitivity is relied upon, and hence users will need to use the lowercase name if so. --- CHANGES.rst | 4 +++- src/werkzeug/http.py | 5 ++++- tests/test_http.py | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 749f6563f..c5247876d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,7 +9,9 @@ Version 2.2.0 - Fix compatibility with Python 3.11 by ensuring that ``end_lineno`` and ``end_col_offset`` are present on AST nodes. :issue:`2425` - Add a new faster matching router based on a state - machine. :pr:`2433`. + machine. :pr:`2433` +- Names within options headers are always converted to lowercase. This + matches :rfc:`6266` that the case is not relevant. :issue:`2442` Version 2.1.2 diff --git a/src/werkzeug/http.py b/src/werkzeug/http.py index 936990013..3d0fba905 100644 --- a/src/werkzeug/http.py +++ b/src/werkzeug/http.py @@ -390,6 +390,9 @@ def parse_options_header( :param value: The header value to parse. + .. versionchanged:: 2.2 + Option names are always converted to lowercase. + .. versionchanged:: 2.1 The ``multiple`` parameter is deprecated and will be removed in Werkzeug 2.2. @@ -440,7 +443,7 @@ def parse_options_header( if not encoding: encoding = continued_encoding continued_encoding = encoding - option = unquote_header_value(option) + option = unquote_header_value(option).lower() if option_value is not None: option_value = unquote_header_value(option_value, option == "filename") diff --git a/tests/test_http.py b/tests/test_http.py index 5936bfa59..623114cb5 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -358,6 +358,10 @@ def test_parse_options_header_broken_values(self): assert http.parse_options_header(" , a ") == ("", {}) assert http.parse_options_header(" ; a ") == ("", {}) + def test_parse_options_header_case_insensitive(self): + _, options = http.parse_options_header(r'something; fileName="File.ext"') + assert options["filename"] == "File.ext" + def test_dump_options_header(self): assert http.dump_options_header("foo", {"bar": 42}) == "foo; bar=42" assert http.dump_options_header("foo", {"bar": 42, "fizz": None}) in (