From 1b3ba609f1e6678c895ba4d07ed41237341e673ccea126cc3d19fc748958c250 Mon Sep 17 00:00:00 2001 From: nkrapp Date: Mon, 1 Sep 2025 14:47:03 +0200 Subject: [PATCH] Add CVE-2025-57804.patch to fix CVE-2025-57804 (bsc#1248737) --- CVE-2025-57804.patch | 91 ++++++++++++++++++++++++++++++++++++++++++++ python-h2.changes | 5 +++ python-h2.spec | 6 ++- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 CVE-2025-57804.patch diff --git a/CVE-2025-57804.patch b/CVE-2025-57804.patch new file mode 100644 index 0000000..eb6fe6e --- /dev/null +++ b/CVE-2025-57804.patch @@ -0,0 +1,91 @@ +From 883ed37be42592b2f0aa0caddab6ca5e3d668fa3 Mon Sep 17 00:00:00 2001 +From: Thomas Kriechbaumer +Date: Mon, 18 Aug 2025 22:46:12 +0200 +Subject: [PATCH] reject header names and values containing unpermitted + characters `\r`, `\n`, or `\0x00` + +--- + CHANGELOG.rst | 2 +- + src/h2/utilities.py | 25 +++++++++++++++++++++++++ + tests/test_invalid_headers.py | 8 +++++++- + 3 files changed, 33 insertions(+), 2 deletions(-) + +Index: h2-4.2.0/src/h2/utilities.py +=================================================================== +--- h2-4.2.0.orig/src/h2/utilities.py ++++ h2-4.2.0/src/h2/utilities.py +@@ -201,6 +201,9 @@ def validate_headers(headers: Iterable[H + # For example, we avoid tuple unpacking in loops because it represents a + # fixed cost that we don't want to spend, instead indexing into the header + # tuples. ++ headers = _reject_illegal_characters( ++ headers, hdr_validation_flags, ++ ) + headers = _reject_empty_header_names( + headers, hdr_validation_flags, + ) +@@ -225,6 +228,36 @@ def validate_headers(headers: Iterable[H + return _check_path_header(headers, hdr_validation_flags) + + ++def _reject_illegal_characters(headers, hdr_validation_flags): ++ """ ++ Raises a ProtocolError if any header names or values contain illegal characters. ++ See RFC 9113, section 8.2.1. ++ """ ++ for header in headers: ++ # > A field name MUST NOT contain characters in the ranges 0x00-0x20, 0x41-0x5a, ++ # > or 0x7f-0xff (all ranges inclusive). ++ for c in header[0]: ++ if c <= 0x20 or 0x41 <= c <= 0x5a or 0x7f <= c: ++ msg = f"Illegal character '{chr(c)}' in header name: {header[0]!r}" ++ raise ProtocolError(msg) ++ ++ # > With the exception of pseudo-header fields (Section 8.3), which have a name ++ # > that starts with a single colon, field names MUST NOT include a colon (ASCII ++ # > COLON, 0x3a). ++ if header[0].find(b":", 1) != -1: ++ msg = f"Illegal character ':' in header name: {header[0]!r}" ++ raise ProtocolError(msg) ++ ++ # > A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed ++ # > (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position. ++ for c in header[1]: ++ if c == 0 or c == 0x0a or c == 0x0d: ++ msg = f"Illegal character '{chr(c)}' in header value: {header[1]!r}" ++ raise ProtocolError(msg) ++ ++ # Surrounding whitespace is enforced in `_reject_surrounding_whitespace`. ++ yield header ++ + + def _reject_empty_header_names(headers: Iterable[Header], + hdr_validation_flags: HeaderValidationFlags) -> Generator[Header, None, None]: +Index: h2-4.2.0/tests/test_invalid_headers.py +=================================================================== +--- h2-4.2.0.orig/tests/test_invalid_headers.py ++++ h2-4.2.0/tests/test_invalid_headers.py +@@ -48,6 +48,14 @@ class TestInvalidFrameSequences: + [*base_request_headers, ("name ", "name with trailing space")], + [*base_request_headers, ("name", " value with leading space")], + [*base_request_headers, ("name", "value with trailing space ")], ++ [*base_request_headers, ("illegal:characters", "value")], ++ [*base_request_headers, ("illegal-\r-characters", "value")], ++ [*base_request_headers, ("illegal-\n-characters", "value")], ++ [*base_request_headers, ("illegal-\x00-characters", "value")], ++ [*base_request_headers, ("illegal-\x01-characters", "value")], ++ [*base_request_headers, ("illegal-characters", "some \r value")], ++ [*base_request_headers, ("illegal-characters", "some \n value")], ++ [*base_request_headers, ("illegal-characters", "some \x00 value")], + [header for header in base_request_headers + if header[0] != ":authority"], + [(":protocol", "websocket"), *base_request_headers], +@@ -665,7 +673,7 @@ class TestFilter: + + def test_inbound_header_name_length_full_frame_decode(self, frame_factory) -> None: + f = frame_factory.build_headers_frame([]) +- f.data = b"\x00\x00\x05\x00\x00\x00\x00\x04" ++ f.data = b"\x00\x00\x01\x04" + data = f.serialize() + + c = h2.connection.H2Connection(config=h2.config.H2Configuration(client_side=False)) diff --git a/python-h2.changes b/python-h2.changes index 3f8f4f1..de13a5b 100644 --- a/python-h2.changes +++ b/python-h2.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Sep 1 12:46:44 UTC 2025 - Nico Krapp + +- Add CVE-2025-57804.patch to fix CVE-2025-57804 (bsc#1248737) + ------------------------------------------------------------------- Tue Feb 11 09:03:44 UTC 2025 - John Paul Adrian Glaubitz diff --git a/python-h2.spec b/python-h2.spec index f8d2d1a..63606c1 100644 --- a/python-h2.spec +++ b/python-h2.spec @@ -16,8 +16,6 @@ # -%{?!python_module:%define python_module() python3-%{**}} -%define skip_python2 1 %{?sle15_python_module_pythons} Name: python-h2 Version: 4.2.0 @@ -26,6 +24,10 @@ Summary: HTTP/2 State-Machine based protocol implementation License: MIT URL: https://github.com/python-hyper/hyper-h2 Source0: https://files.pythonhosted.org/packages/source/h/h2/h2-%{version}.tar.gz +# PATCH-FIX-UPSTREAM CVE-2025-57804.patch (bsc#1248737) +# taken from https://github.com/python-hyper/h2/commit/883ed37be42592b2f0aa0caddab6ca5e3d668fa3 +# and https://github.com/python-hyper/h2/commit/035e9899f95e3709af098f578bfc3cd302298e3a +Patch0: CVE-2025-57804.patch BuildRequires: %{python_module hpack >= 2.3} BuildRequires: %{python_module hyperframe >= 6.0} BuildRequires: %{python_module hypothesis >= 5.49}