From 17d88b7742ea2bfb684c2c2badcf16a078e4fd21 Mon Sep 17 00:00:00 2001 From: Julian Faude Date: Thu, 20 Jul 2023 18:08:42 +0200 Subject: [PATCH] Fix qvalue parsing --- CHANGES.rst | 1 + src/werkzeug/_internal.py | 9 +++++++++ src/werkzeug/http.py | 4 ++-- tests/test_http.py | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f3784c2305..4eec070f46 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Version 2.3.7 Unreleased - Use ``flit_core`` instead of ``setuptools`` as build backend. +- Fix ``parse_options_header`` rejects q values "0" and "1". :issue:`2751` Version 2.3.6 diff --git a/src/werkzeug/_internal.py b/src/werkzeug/_internal.py index 4981f811f6..e0db0d6696 100644 --- a/src/werkzeug/_internal.py +++ b/src/werkzeug/_internal.py @@ -314,6 +314,7 @@ def _decode_idna(domain: str) -> str: _plain_int_re = re.compile(r"-?\d+", re.ASCII) _plain_float_re = re.compile(r"-?\d+\.\d+", re.ASCII) +_plain_qvalue_re = re.compile(r"(1(\.0{0,3})?)|(0(\.\d{0,3})?)", re.ASCII) def _plain_int(value: str) -> int: @@ -339,3 +340,11 @@ def _plain_float(value: str) -> float: raise ValueError return float(value) + + +def _qvalue(value: str) -> float: + """Parse a RFC9110 quality value""" + if _plain_qvalue_re.fullmatch(value) is None: + raise ValueError + + return float(value) diff --git a/src/werkzeug/http.py b/src/werkzeug/http.py index 72dc3f3eb3..f35002b21c 100644 --- a/src/werkzeug/http.py +++ b/src/werkzeug/http.py @@ -18,8 +18,8 @@ from urllib.request import parse_http_list as _parse_list_header from ._internal import _dt_as_utc -from ._internal import _plain_float from ._internal import _plain_int +from ._internal import _qvalue if t.TYPE_CHECKING: from _typeshed.wsgi import WSGIEnvironment @@ -658,7 +658,7 @@ def parse_accept_header( if "q" in options: try: # pop q, remaining options are reconstructed - q = _plain_float(options.pop("q")) + q = _qvalue(options.pop("q")) except ValueError: # ignore an invalid q continue diff --git a/tests/test_http.py b/tests/test_http.py index 2c6c0b29ac..bbd51ba335 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -776,6 +776,12 @@ def test_accept_invalid_float(value): assert list(a.values()) == ["en"] +def test_accept_valid_int_one_zero(): + assert http.parse_accept_header("en;q=1") == http.parse_accept_header("en;q=1.0") + assert http.parse_accept_header("en;q=0") == http.parse_accept_header("en;q=0.0") + assert http.parse_accept_header("en;q=5") == http.parse_accept_header("en;q=5.0") + + @pytest.mark.parametrize("value", ["🯱🯲🯳", "+1-", "1-1_23"]) def test_range_invalid_int(value): assert http.parse_range_header(value) is None