Vulnerability GHSA-5vjj-2r48-q622

Medium Risk
MEDIUM RISK
CVSS Score: 6.0
Score Range: 4.0–6.9
Medium severity vulnerabilities (CVSS 4.0–6.9). Important issues that meaningfully reduce security confidence.
8 days ago
September 23, 2026 at 01:58 PM UTC
Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service
0.2.0 - 0.13.0
0.2.0 - 0.13.0

Summary

Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service

Details

Impact

Who is impacted:

  • Any application using Zapros to make HTTP requests to untrusted servers
  • Applications that follow redirects to attacker-controlled hosts

Attack vector:

  • A malicious HTTP server returns a response with many chained content encodings. When the client attempts to decode, it creates a deeply nested decompression chain consuming excessive resources.

Patches

Fixed in version 0.14.0.

The fix adds a hardcoded limit of 5 Content-Encoding layers. Responses exceeding this limit raise DecodingError.

Workarounds

Add middleware that checks for a malicious Content-Encoding header.

from typing import cast

from zapros import (
    AsyncBaseHandler,
    AsyncBaseMiddleware,
    BaseHandler,
    BaseMiddleware,
    Client,
    DecodingError,
    Request,
    Response,
)

MAX_DECODE_LAYERS = 5


class ContentEncodingCheckMiddleware(BaseMiddleware, AsyncBaseMiddleware):
    def __init__(
        self,
        next_handler: BaseHandler | AsyncBaseHandler,
        *,
        max_layers: int = MAX_DECODE_LAYERS,
    ) -> None:
        self.next = cast(BaseHandler, next_handler)
        self.async_next = cast(AsyncBaseHandler, next_handler)
        self._max_layers = max_layers

    def _check(self, response: Response) -> None:
        encoding_header = response.headers.get("Content-Encoding")
        if not encoding_header:
            return

        layers = [enc.strip().lower() for enc in encoding_header.split(",") if enc.strip()]
        if len(layers) > self._max_layers:
            raise DecodingError(f"Too many Content-Encoding layers ({len(layers)}), maximum is {self._max_layers}")

    def handle(self, request: Request) -> Response:
        response = self.next.handle(request)
        self._check(response)
        return response

    async def ahandle(self, request: Request) -> Response:
        response = await self.async_next.ahandle(request)
        self._check(response)
        return response


with Client().wrap_with_middleware(lambda next: ContentEncodingCheckMiddleware(next)) as client:
    ...

Impacted packages

Timeline

Published
8 days ago
September 23, 2026 at 01:58 PM UTC
Fixed (0.14.0)
3 months ago
June 23, 2026 at 09:18 AM UTC
Last Modified
9 hours ago
October 01, 2026 at 05:56 PM UTC