14
0

14 Commits

Author SHA256 Message Date
a232380790 Add CVE-2025-62727.patch to fix CVE-2025-62727 (bsc#1252805) 2025-10-29 15:36:04 +01:00
9dcc206ed6 Add CVE-2025-54121.patch to fix CVE-2025-54121 (bsc#1246855) 2025-10-29 15:28:20 +01:00
f19e403868 Accepting request 1230669 from devel:languages:python
OBS-URL: https://build.opensuse.org/request/show/1230669
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=30
2024-12-13 21:33:08 +00:00
973c2f89e0 Accepting request 1230643 from home:bnavigator:branches:devel:languages:python
- Add starlette-pr2773-httpx0.28.patch gh#encode/starlette#2773
  for httpx 0.28

OBS-URL: https://build.opensuse.org/request/show/1230643
OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=60
2024-12-12 23:52:34 +00:00
ab72e566b1 Accepting request 1225373 from devel:languages:python
- update to 0.41.3:
  * Exclude the query parameters from the `scope[raw_path]` on
    the `TestClient` #2716.
  * Replace `dict` by `Mapping` on `HTTPException.headers` #2749.
  * Correct middleware argument passing and improve factory
    pattern #2752.
- update to 0.41.2:
  * Revert bump on `python-multipart`
- update to 0.41.1:
  * Bump minimum `python-multipart` version to 0.0.13

OBS-URL: https://build.opensuse.org/request/show/1225373
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=29
2024-11-21 14:13:26 +00:00
6efb479e30 - update to 0.41.3:
* Exclude the query parameters from the `scope[raw_path]` on
    the `TestClient` #2716.
  * Replace `dict` by `Mapping` on `HTTPException.headers` #2749.
  * Correct middleware argument passing and improve factory
    pattern #2752.
- update to 0.41.2:
  * Revert bump on `python-multipart`
- update to 0.41.1:
  * Bump minimum `python-multipart` version to 0.0.13

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=58
2024-11-20 17:28:57 +00:00
6435e99b98 Accepting request 1222062 from devel:languages:python
- disable PendingDeprecationWarning

OBS-URL: https://build.opensuse.org/request/show/1222062
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=28
2024-11-08 10:56:06 +00:00
218e589c26 - disable PendingDeprecationWarning
OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=56
2024-11-07 09:21:19 +00:00
5b45d99ec9 Accepting request 1208248 from devel:languages:python
- Update to 0.41.0:
  * Allow to raise HTTPException before websocket.accept().
  * Add max_part_size to MultiPartParser to limit the size of parts in
    multipart/form-data requests. (bsc#1231689, CVE-2024-47874)
  * Allow use of request.url_for when only "app" scope is available.
  * Avoid regex re-compilation in responses.py and schemas.py.
  * Improve performance of get_route_path by removing regular expression
    usage.
  * Consider FileResponse.chunk_size when handling multiple ranges.
  * Use token_hex for generating multipart boundary strings.
  * Add support for HTTP Range to FileResponse.
  * Close unclosed MemoryObjectReceiveStream in TestClient.

OBS-URL: https://build.opensuse.org/request/show/1208248
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=27
2024-10-20 08:12:53 +00:00
b44c5d4bb2 - Update to 0.41.0:
* Allow to raise HTTPException before websocket.accept().
  * Add max_part_size to MultiPartParser to limit the size of parts in
    multipart/form-data requests. (bsc#1231689, CVE-2024-47874)
  * Allow use of request.url_for when only "app" scope is available.
  * Avoid regex re-compilation in responses.py and schemas.py.
  * Improve performance of get_route_path by removing regular expression
    usage.
  * Consider FileResponse.chunk_size when handling multiple ranges.
  * Use token_hex for generating multipart boundary strings.
  * Add support for HTTP Range to FileResponse.
  * Close unclosed MemoryObjectReceiveStream in TestClient.

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=54
2024-10-16 04:18:55 +00:00
2a6ee2b320 Accepting request 1199466 from devel:languages:python
- update to 0.38.5:
  * Schedule `BackgroundTasks` from within `BaseHTTPMiddleware`
    #2688.
- update to 0.38.4:
  * Ensure accurate `root_path` removal in `get_route_path`
    function #2600
- update to 0.38.3:
  * Support for Python 3.13 #2662.
  * Don't poll for disconnects in `BaseHTTPMiddleware` via
    `StreamingResponse` #2620.

OBS-URL: https://build.opensuse.org/request/show/1199466
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=26
2024-09-09 12:43:25 +00:00
6c55d1424a - update to 0.38.5:
* Schedule `BackgroundTasks` from within `BaseHTTPMiddleware`
    #2688.
- update to 0.38.4:
  * Ensure accurate `root_path` removal in `get_route_path`
    function #2600
- update to 0.38.3:
  * Support for Python 3.13 #2662.
  * Don't poll for disconnects in `BaseHTTPMiddleware` via
    `StreamingResponse` #2620.

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=52
2024-09-08 15:06:36 +00:00
9d18310d7e Accepting request 1193696 from devel:languages:python
- Update to 0.38.2:
  * Fix routing.get_name() not to assume all routines have __name__ #2648
- 0.38.1:
  * Revert "Add support for ASGI pathsend extension" #2649.
- 0.38.0:
  * Allow use of memoryview in StreamingResponse and Response #2576
    and #2577.
  * Send 404 instead of 500 when filename requested is too long on
    StaticFiles #2583.
  * Fail fast on invalid Jinja2Template instantiation parameters #2568.
  * Check endpoint handler is async only once #2536.
  * Add proper synchronization to WebSocketTestSession #2597.

OBS-URL: https://build.opensuse.org/request/show/1193696
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/python-starlette?expand=0&rev=25
2024-08-15 07:57:21 +00:00
673187f1e3 - Update to 0.38.2:
* Fix routing.get_name() not to assume all routines have __name__ #2648
- 0.38.1:
  * Revert "Add support for ASGI pathsend extension" #2649.
- 0.38.0:
  * Allow use of memoryview in StreamingResponse and Response #2576
    and #2577.
  * Send 404 instead of 500 when filename requested is too long on
    StaticFiles #2583.
  * Fail fast on invalid Jinja2Template instantiation parameters #2568.
  * Check endpoint handler is async only once #2536.
  * Add proper synchronization to WebSocketTestSession #2597.

OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-starlette?expand=0&rev=50
2024-08-13 11:28:43 +00:00
7 changed files with 520 additions and 8 deletions

219
CVE-2025-54121.patch Normal file
View File

@@ -0,0 +1,219 @@
From 9f7ec2eb512fcc3fe90b43cb9dd9e1d08696bec1 Mon Sep 17 00:00:00 2001
From: Michael Honaker <37811263+HonakerM@users.noreply.github.com>
Date: Mon, 21 Jul 2025 02:24:02 +0900
Subject: [PATCH] Make UploadFile check for future rollover (#2962)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
---
starlette/datastructures.py | 22 ++++++++++---
tests/test_formparsers.py | 66 +++++++++++++++++++++++++++++++++++--
2 files changed, 82 insertions(+), 6 deletions(-)
Index: starlette-0.41.3/starlette/datastructures.py
===================================================================
--- starlette-0.41.3.orig/starlette/datastructures.py
+++ starlette-0.41.3/starlette/datastructures.py
@@ -424,6 +424,10 @@ class UploadFile:
self.size = size
self.headers = headers or Headers()
+ # Capture max size from SpooledTemporaryFile if one is provided. This slightly speeds up future checks.
+ # Note 0 means unlimited mirroring SpooledTemporaryFile's __init__
+ self._max_mem_size = getattr(self.file, "_max_size", 0)
+
@property
def content_type(self) -> str | None:
return self.headers.get("content-type", None)
@@ -434,14 +438,24 @@ class UploadFile:
rolled_to_disk = getattr(self.file, "_rolled", True)
return not rolled_to_disk
+ def _will_roll(self, size_to_add: int) -> bool:
+ # If we're not in_memory then we will always roll
+ if not self._in_memory:
+ return True
+
+ # Check for SpooledTemporaryFile._max_size
+ future_size = self.file.tell() + size_to_add
+ return bool(future_size > self._max_mem_size) if self._max_mem_size else False
+
async def write(self, data: bytes) -> None:
+ new_data_len = len(data)
if self.size is not None:
- self.size += len(data)
+ self.size += new_data_len
- if self._in_memory:
- self.file.write(data)
- else:
+ if self._will_roll(new_data_len):
await run_in_threadpool(self.file.write, data)
+ else:
+ self.file.write(data)
async def read(self, size: int = -1) -> bytes:
if self._in_memory:
Index: starlette-0.41.3/tests/test_formparsers.py
===================================================================
--- starlette-0.41.3.orig/tests/test_formparsers.py
+++ starlette-0.41.3/tests/test_formparsers.py
@@ -1,15 +1,21 @@
from __future__ import annotations
import os
+import threading
import typing
+from collections.abc import Generator
from contextlib import nullcontext as does_not_raise
+from io import BytesIO
from pathlib import Path
+from tempfile import SpooledTemporaryFile
+from typing import Any, ClassVar
+from unittest import mock
import pytest
from starlette.applications import Starlette
from starlette.datastructures import UploadFile
-from starlette.formparsers import MultiPartException, _user_safe_decode
+from starlette.formparsers import MultiPartException, MultiPartParser, _user_safe_decode
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Mount
@@ -104,6 +110,22 @@ async def app_read_body(scope: Scope, re
await response(scope, receive, send)
+async def app_monitor_thread(scope: Scope, receive: Receive, send: Send) -> None:
+ """Helper app to monitor what thread the app was called on.
+
+ This can later be used to validate thread/event loop operations.
+ """
+ request = Request(scope, receive)
+
+ # Make sure we parse the form
+ await request.form()
+ await request.close()
+
+ # Send back the current thread id
+ response = JSONResponse({"thread_ident": threading.current_thread().ident})
+ await response(scope, receive, send)
+
+
def make_app_max_parts(max_files: int = 1000, max_fields: int = 1000) -> ASGIApp:
async def app(scope: Scope, receive: Receive, send: Send) -> None:
request = Request(scope, receive)
@@ -303,6 +325,88 @@ def test_multipart_request_mixed_files_a
}
+class ThreadTrackingSpooledTemporaryFile(SpooledTemporaryFile[bytes]):
+ """Helper class to track which threads performed the rollover operation.
+
+ This is not threadsafe/multi-test safe.
+ """
+
+ rollover_threads: ClassVar[set[int | None]] = set()
+
+ def rollover(self) -> None:
+ ThreadTrackingSpooledTemporaryFile.rollover_threads.add(threading.current_thread().ident)
+ super().rollover()
+
+
+@pytest.fixture
+def mock_spooled_temporary_file() -> Generator[None]:
+ try:
+ with mock.patch("starlette.formparsers.SpooledTemporaryFile", ThreadTrackingSpooledTemporaryFile):
+ yield
+ finally:
+ ThreadTrackingSpooledTemporaryFile.rollover_threads.clear()
+
+
+def test_multipart_request_large_file_rollover_in_background_thread(
+ mock_spooled_temporary_file: None, test_client_factory: TestClientFactory
+) -> None:
+ """Test that Spooled file rollovers happen in background threads."""
+ data = BytesIO(b" " * (MultiPartParser.spool_max_size + 1))
+
+ client = test_client_factory(app_monitor_thread)
+ response = client.post("/", files=[("test_large", data)])
+ assert response.status_code == 200
+
+ # Parse the event thread id from the API response and ensure we have one
+ app_thread_ident = response.json().get("thread_ident")
+ assert app_thread_ident is not None
+
+ # Ensure the app thread was not the same as the rollover one and that a rollover thread exists
+ assert app_thread_ident not in ThreadTrackingSpooledTemporaryFile.rollover_threads
+ assert len(ThreadTrackingSpooledTemporaryFile.rollover_threads) == 1
+
+
+class ThreadTrackingSpooledTemporaryFile(SpooledTemporaryFile[bytes]):
+ """Helper class to track which threads performed the rollover operation.
+
+ This is not threadsafe/multi-test safe.
+ """
+
+ rollover_threads: ClassVar[set[int | None]] = set()
+
+ def rollover(self) -> None:
+ ThreadTrackingSpooledTemporaryFile.rollover_threads.add(threading.current_thread().ident)
+ super().rollover()
+
+
+@pytest.fixture
+def mock_spooled_temporary_file() -> Generator[None]:
+ try:
+ with mock.patch("starlette.formparsers.SpooledTemporaryFile", ThreadTrackingSpooledTemporaryFile):
+ yield
+ finally:
+ ThreadTrackingSpooledTemporaryFile.rollover_threads.clear()
+
+
+def test_multipart_request_large_file_rollover_in_background_thread(
+ mock_spooled_temporary_file: None, test_client_factory: TestClientFactory
+) -> None:
+ """Test that Spooled file rollovers happen in background threads."""
+ data = BytesIO(b" " * (MultiPartParser.spool_max_size + 1))
+
+ client = test_client_factory(app_monitor_thread)
+ response = client.post("/", files=[("test_large", data)])
+ assert response.status_code == 200
+
+ # Parse the event thread id from the API response and ensure we have one
+ app_thread_ident = response.json().get("thread_ident")
+ assert app_thread_ident is not None
+
+ # Ensure the app thread was not the same as the rollover one and that a rollover thread exists
+ assert app_thread_ident not in ThreadTrackingSpooledTemporaryFile.rollover_threads
+ assert len(ThreadTrackingSpooledTemporaryFile.rollover_threads) == 1
+
+
def test_multipart_request_with_charset_for_filename(tmpdir: Path, test_client_factory: TestClientFactory) -> None:
client = test_client_factory(app)
response = client.post(
Index: starlette-0.41.3/starlette/formparsers.py
===================================================================
--- starlette-0.41.3.orig/starlette/formparsers.py
+++ starlette-0.41.3/starlette/formparsers.py
@@ -122,7 +122,10 @@ class FormParser:
class MultiPartParser:
- max_file_size = 1024 * 1024 # 1MB
+ spool_max_size = 1024 * 1024 # 1MB
+ """The maximum size of the spooled temporary file used to store file data."""
+ max_part_size = 1024 * 1024 # 1MB
+ """The maximum size of a part in the multipart request."""
max_part_size = 1024 * 1024 # 1MB
def __init__(
@@ -201,7 +204,7 @@ class MultiPartParser:
if self._current_files > self.max_files:
raise MultiPartException(f"Too many files. Maximum number of files is {self.max_files}.")
filename = _user_safe_decode(options[b"filename"], self._charset)
- tempfile = SpooledTemporaryFile(max_size=self.max_file_size)
+ tempfile = SpooledTemporaryFile(max_size=self.spool_max_size)
self._files_to_close_on_error.append(tempfile)
self._current_part.file = UploadFile(
file=tempfile, # type: ignore[arg-type]

134
CVE-2025-62727.patch Normal file
View File

@@ -0,0 +1,134 @@
From 4ea6e22b489ec388d6004cfbca52dd5b147127c5 Mon Sep 17 00:00:00 2001
From: Marcelo Trylesinski <marcelotryle@gmail.com>
Date: Tue, 28 Oct 2025 18:14:01 +0100
Subject: [PATCH] Merge commit from fork
---
starlette/responses.py | 46 ++++++++++++++++++++++++++++-------------
tests/test_responses.py | 28 +++++++++++++++++++++++++
2 files changed, 60 insertions(+), 14 deletions(-)
Index: starlette-0.41.3/starlette/responses.py
===================================================================
--- starlette-0.41.3.orig/starlette/responses.py
+++ starlette-0.41.3/starlette/responses.py
@@ -3,7 +3,6 @@ from __future__ import annotations
import http.cookies
import json
import os
-import re
import stat
import typing
import warnings
@@ -272,9 +271,6 @@ class RangeNotSatisfiable(Exception):
self.max_size = max_size
-_RANGE_PATTERN = re.compile(r"(\d*)-(\d*)")
-
-
class FileResponse(Response):
chunk_size = 64 * 1024
@@ -435,8 +431,8 @@ class FileResponse(Response):
etag = f'"{md5_hexdigest(etag_base.encode(), usedforsecurity=False)}"'
return http_if_range == formatdate(stat_result.st_mtime, usegmt=True) or http_if_range == etag
- @staticmethod
- def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]:
+ @classmethod
+ def _parse_range_header(cls, http_range: str, file_size: int) -> list[tuple[int, int]]:
ranges: list[tuple[int, int]] = []
try:
units, range_ = http_range.split("=", 1)
@@ -448,14 +444,7 @@ class FileResponse(Response):
if units != "bytes":
raise MalformedRangeHeader("Only support bytes range")
- ranges = [
- (
- int(_[0]) if _[0] else file_size - int(_[1]),
- int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else file_size,
- )
- for _ in _RANGE_PATTERN.findall(range_)
- if _ != ("", "")
- ]
+ ranges = cls._parse_ranges(range_, file_size)
if len(ranges) == 0:
raise MalformedRangeHeader("Range header: range must be requested")
@@ -487,6 +476,35 @@ class FileResponse(Response):
return result
+ @classmethod
+ def _parse_ranges(cls, range_: str, file_size: int) -> list[tuple[int, int]]:
+ ranges: list[tuple[int, int]] = []
+
+ for part in range_.split(","):
+ part = part.strip()
+
+ # If the range is empty or a single dash, we ignore it.
+ if not part or part == "-":
+ continue
+
+ # If the range is not in the format "start-end", we ignore it.
+ if "-" not in part:
+ continue
+
+ start_str, end_str = part.split("-", 1)
+ start_str = start_str.strip()
+ end_str = end_str.strip()
+
+ try:
+ start = int(start_str) if start_str else file_size - int(end_str)
+ end = int(end_str) + 1 if start_str and end_str and int(end_str) < file_size else file_size
+ ranges.append((start, end))
+ except ValueError:
+ # If the range is not numeric, we ignore it.
+ continue
+
+ return ranges
+
def generate_multipart(
self,
ranges: typing.Sequence[tuple[int, int]],
Index: starlette-0.41.3/tests/test_responses.py
===================================================================
--- starlette-0.41.3.orig/tests/test_responses.py
+++ starlette-0.41.3/tests/test_responses.py
@@ -684,6 +684,34 @@ def test_file_response_insert_ranges(fil
]
+def test_file_response_range_without_dash(file_response_client: TestClient) -> None:
+ response = file_response_client.get("/", headers={"Range": "bytes=100, 0-50"})
+ assert response.status_code == 206
+ assert response.headers["content-range"] == f"bytes 0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_range_empty_start_and_end(file_response_client: TestClient) -> None:
+ response = file_response_client.get("/", headers={"Range": "bytes= - , 0-50"})
+ assert response.status_code == 206
+ assert response.headers["content-range"] == f"bytes 0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_range_ignore_non_numeric(file_response_client: TestClient) -> None:
+ response = file_response_client.get("/", headers={"Range": "bytes=abc-def, 0-50"})
+ assert response.status_code == 206
+ assert response.headers["content-range"] == f"bytes 0-50/{len(README.encode('utf8'))}"
+
+
+def test_file_response_suffix_range(file_response_client: TestClient) -> None:
+ # Test suffix range (last N bytes) - line 523 with empty start_str
+ response = file_response_client.get("/", headers={"Range": "bytes=-100"})
+ assert response.status_code == 206
+ file_size = len(README.encode("utf8"))
+ assert response.headers["content-range"] == f"bytes {file_size - 100}-{file_size - 1}/{file_size}"
+ assert response.headers["content-length"] == "100"
+ assert response.content == README.encode("utf8")[-100:]
+
+
@pytest.mark.anyio
async def test_file_response_multi_small_chunk_size(readme_file: Path) -> None:
class SmallChunkSizeFileResponse(FileResponse):

View File

@@ -1,3 +1,84 @@
-------------------------------------------------------------------
Wed Oct 29 14:29:58 UTC 2025 - Nico Krapp <nico.krapp@suse.com>
- Add CVE-2025-62727.patch to fix CVE-2025-62727 (bsc#1252805)
-------------------------------------------------------------------
Thu Jul 31 09:20:02 UTC 2025 - Nico Krapp <nico.krapp@suse.com>
- Add CVE-2025-54121.patch to fix CVE-2025-54121 (bsc#1246855)
-------------------------------------------------------------------
Thu Dec 12 16:09:18 UTC 2024 - Ben Greiner <code@bnavigator.de>
- Add starlette-pr2773-httpx0.28.patch gh#encode/starlette#2773
for httpx 0.28
-------------------------------------------------------------------
Wed Nov 20 17:28:31 UTC 2024 - Dirk Müller <dmueller@suse.com>
- update to 0.41.3:
* Exclude the query parameters from the `scope[raw_path]` on
the `TestClient` #2716.
* Replace `dict` by `Mapping` on `HTTPException.headers` #2749.
* Correct middleware argument passing and improve factory
pattern #2752.
- update to 0.41.2:
* Revert bump on `python-multipart`
- update to 0.41.1:
* Bump minimum `python-multipart` version to 0.0.13
-------------------------------------------------------------------
Thu Nov 7 09:20:47 UTC 2024 - Nico Krapp <nico.krapp@suse.com>
- disable PendingDeprecationWarning
-------------------------------------------------------------------
Wed Oct 16 04:18:23 UTC 2024 - Steve Kowalik <steven.kowalik@suse.com>
- Update to 0.41.0:
* Allow to raise HTTPException before websocket.accept().
* Add max_part_size to MultiPartParser to limit the size of parts in
multipart/form-data requests. (bsc#1231689, CVE-2024-47874)
* Allow use of request.url_for when only "app" scope is available.
* Avoid regex re-compilation in responses.py and schemas.py.
* Improve performance of get_route_path by removing regular expression
usage.
* Consider FileResponse.chunk_size when handling multiple ranges.
* Use token_hex for generating multipart boundary strings.
* Add support for HTTP Range to FileResponse.
* Close unclosed MemoryObjectReceiveStream in TestClient.
-------------------------------------------------------------------
Sun Sep 8 15:05:40 UTC 2024 - Dirk Müller <dmueller@suse.com>
- update to 0.38.5:
* Schedule `BackgroundTasks` from within `BaseHTTPMiddleware`
#2688.
- update to 0.38.4:
* Ensure accurate `root_path` removal in `get_route_path`
function #2600
- update to 0.38.3:
* Support for Python 3.13 #2662.
* Don't poll for disconnects in `BaseHTTPMiddleware` via
`StreamingResponse` #2620.
-------------------------------------------------------------------
Tue Aug 13 09:41:53 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
- Update to 0.38.2:
* Fix routing.get_name() not to assume all routines have __name__ #2648
- 0.38.1:
* Revert "Add support for ASGI pathsend extension" #2649.
- 0.38.0:
* Allow use of memoryview in StreamingResponse and Response #2576
and #2577.
* Send 404 instead of 500 when filename requested is too long on
StaticFiles #2583.
* Fail fast on invalid Jinja2Template instantiation parameters #2568.
* Check endpoint handler is async only once #2536.
* Add proper synchronization to WebSocketTestSession #2597.
-------------------------------------------------------------------
Sat Apr 20 08:10:00 UTC 2024 - Dirk Müller <dmueller@suse.com>

View File

@@ -27,12 +27,20 @@
%{?sle15_python_module_pythons}
Name: python-starlette%{psuffix}
Version: 0.37.2
Version: 0.41.3
Release: 0
Summary: Lightweight ASGI framework/toolkit
License: BSD-3-Clause
URL: https://github.com/encode/starlette
Source: https://github.com/encode/starlette/archive/refs/tags/%{version}.tar.gz#/starlette-%{version}.tar.gz
# PATCH-FIX-UPSTREAM starlette-pr2773-httpx0.28.patch gh#encode/starlette#2773
Patch0: https://github.com/encode/starlette/pull/2773.patch#/starlette-pr2773-httpx0.28.patch
# PATCH-FIX-UPSTREAM CVE-2025-54121.patch bsc#1246855
# taken from https://github.com/encode/starlette/commit/9f7ec2eb512fcc3fe90b43cb9dd9e1d08696bec1
# and https://github.com/encode/starlette/commit/4ae3213ca557a25aff91f1d43d530ca3962c729d
Patch1: CVE-2025-54121.patch
# PATCH-FIX-UPSTREAM CVE-2025-62727.patch https://github.com/Kludex/starlette/commit/4ea6e22b489ec388d6004cfbca52dd5b147127c5
Patch2: CVE-2025-62727.patch
BuildRequires: %{python_module base >= 3.8}
BuildRequires: %{python_module hatchling}
BuildRequires: %{python_module pip}
@@ -47,7 +55,7 @@ BuildRequires: %{python_module anyio >= 3.4.0}
# SECTION [full]
BuildRequires: %{python_module PyYAML}
BuildRequires: %{python_module Jinja2}
BuildRequires: %{python_module httpx >= 0.22}
BuildRequires: %{python_module httpx >= 0.28}
BuildRequires: %{python_module itsdangerous}
BuildRequires: %{python_module python-multipart >= 0.0.7}
# /SECTION
@@ -69,10 +77,12 @@ Starlette is a lightweight ASGI framework/toolkit, which is ideal for
building high performance asyncio services.
%prep
%autosetup -n starlette-%{version}
%autosetup -p1 -n starlette-%{version}
%build
%if ! %{with test}
%pyproject_wheel
%endif
%install
%if ! %{with test}
@@ -90,7 +100,7 @@ ignored_tests="test_set_cookie"
ignored_tests="$ignored_tests or test_expires_on_set_cookie"
# fails to raise a deprecation warning as of 2024/04/25
ignored_tests="$ignored_tests or test_lifespan_with_on_events"
%pytest --asyncio-mode=strict -k "not ($ignored_tests)"
%pytest -W ignore::PendingDeprecationWarning --asyncio-mode=strict -k "not ($ignored_tests)"
%endif
@@ -99,7 +109,7 @@ ignored_tests="$ignored_tests or test_lifespan_with_on_events"
%doc README.md
%license LICENSE.md
%{python_sitelib}/starlette
%{python_sitelib}/starlette-%{version}*-info
%{python_sitelib}/starlette-%{version}.dist-info
%endif
%changelog

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1bbb8b114ef94cedef5c2a320ff395dd2c6ab51e86ec605aec00976bd9314f4e
size 2846433

BIN
starlette-0.41.3.tar.gz LFS Normal file

Binary file not shown.

View File

@@ -0,0 +1,68 @@
From b781c571068f4afc0417c7dfb8df2eda0547af55 Mon Sep 17 00:00:00 2001
From: Marcelo Trylesinski <marcelotryle@gmail.com>
Date: Sat, 30 Nov 2024 10:32:50 +0100
Subject: [PATCH 1/2] Pin httpx in `full` extra
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index a532e4628..598d4333b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,7 +37,7 @@ full = [
"jinja2",
"python-multipart>=0.0.7",
"pyyaml",
- "httpx>=0.22.0",
+ "httpx>=0.27.0,<0.29.0",
]
[project.urls]
From 24de2bfc8aa99a084a9b4fcfab1e52d7a6747cd9 Mon Sep 17 00:00:00 2001
From: Marcelo Trylesinski <marcelotryle@gmail.com>
Date: Sat, 30 Nov 2024 10:51:11 +0100
Subject: [PATCH 2/2] fix test
---
tests/middleware/test_wsgi.py | 2 +-
tests/test_requests.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/middleware/test_wsgi.py b/tests/middleware/test_wsgi.py
index 58696bb65..e4ac66ab4 100644
--- a/tests/middleware/test_wsgi.py
+++ b/tests/middleware/test_wsgi.py
@@ -77,7 +77,7 @@ def test_wsgi_post(test_client_factory: TestClientFactory) -> None:
client = test_client_factory(app)
response = client.post("/", json={"example": 123})
assert response.status_code == 200
- assert response.text == '{"example": 123}'
+ assert response.text == '{"example":123}'
def test_wsgi_exception(test_client_factory: TestClientFactory) -> None:
diff --git a/tests/test_requests.py b/tests/test_requests.py
index f0494e751..665dceb87 100644
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -91,7 +91,7 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
assert response.json() == {"body": ""}
response = client.post("/", json={"a": "123"})
- assert response.json() == {"body": '{"a": "123"}'}
+ assert response.json() == {"body": '{"a":"123"}'}
response = client.post("/", data="abc") # type: ignore
assert response.json() == {"body": "abc"}
@@ -112,7 +112,7 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:
assert response.json() == {"body": ""}
response = client.post("/", json={"a": "123"})
- assert response.json() == {"body": '{"a": "123"}'}
+ assert response.json() == {"body": '{"a":"123"}'}
response = client.post("/", data="abc") # type: ignore
assert response.json() == {"body": "abc"}