{"id":"CVE-2026-61541","aliases":["EUVD-2026-84182","GHSA-5vjj-2r48-q622","PYSEC-2026-4181"],"status":"published","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"Zapros, a Python HTTP client, prior to version 0.14.0 is vulnerable to denial of service when an application requests content from an untrusted server, or follows a redirect to one, because a malicious response containing an excessive number of chained `Content-Encoding` values causes Zapros to construct a deeply nested decompression chain that consumes excessive resources. Version 0.14.0 patches the vulnerability by limiting responses to five content-encoding layers and raising `DecodingError` when that limit is exceeded. As a workaround, applications can add response middleware that inspects the `Content-Encoding` header and rejects responses containing more than a safe number of encoding layers.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00428,"percentile":0.3731,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-770"],"published":"2026-09-21T21:21:21Z","modified":"2026-10-28T02:30:27Z","fixes":[{"ecosystem":"PyPI","package":"zapros","fixed_in":["0.14.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"zapros","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.14.0"}]}]},{"source":"osv","ecosystem":"PyPI","package":"zapros","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.14.0"}]}],"versions":["0.10.0","0.11.0","0.11.1","0.12.0","0.13.0","0.2.0","0.2.1","0.2.2","0.2.3","0.3.0","0.4.0","0.5.0","0.5.1","0.6.0","0.7.0","0.8.0","0.9.0"]},{"source":"osv","ecosystem":"PyPI","package":"zapros","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.14.0"}]}],"versions":["0.10.0","0.11.0","0.11.1","0.12.0","0.13.0","0.2.0","0.2.1","0.2.2","0.2.3","0.3.0","0.4.0","0.5.0","0.5.1","0.6.0","0.7.0","0.8.0","0.9.0"]}],"products":[{"vendor":"kap-sh","product":"zapros"}],"references":[{"url":"https://github.com/kap-sh/zapros/security/advisories/GHSA-5vjj-2r48-q622","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-61541","tags":["advisory"]},{"url":"https://github.com/kap-sh/zapros/commit/7971fbca9707eb01455ca2d73416ac091f96908b","tags":["web"]},{"url":"https://github.com/kap-sh/zapros/releases/tag/v0.14.0","tags":["web"]},{"url":"https://github.com/advisories/GHSA-5vjj-2r48-q622","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61541.json","tags":["advisory"]},{"url":"https://github.com/kap-sh/zapros","tags":["package"]},{"url":"https://pypi.org/project/zapros","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-61541","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"Zapros, a Python HTTP client, prior to version 0.14.0 is vulnerable to denial of service when an application requests content from an untrusted server, or follows a redirect to one, because a malicious response containing an excessive number of chained `Content-Encoding` values causes Zapros to construct a deeply nested decompression chain that consumes excessive resources. Version 0.14.0 patches the vulnerability by limiting responses to five content-encoding layers and raising `DecodingError` when that limit is exceeded. As a workaround, applications can add response middleware that inspects the `Content-Encoding` header and rejects responses containing more than a safe number of encoding layers.","status":"published","published":"2026-09-21T21:21:21.321Z","modified":"2026-09-23T19:07:58.617Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-770"],"updated_at":"2026-11-02T14:19:53.4444Z"}],"euvd":[{"id":"EUVD-2026-84182","description":"Zapros, a Python HTTP client, prior to version 0.14.0 is vulnerable to denial of service when an application requests content from an untrusted server, or follows a redirect to one, because a malicious response containing an excessive number of chained `Content-Encoding` values causes Zapros to construct a deeply nested decompression chain that consumes excessive resources. Version 0.14.0 patches the vulnerability by limiting responses to five content-encoding layers and raising `DecodingError` when that limit is exceeded. As a workaround, applications can add response middleware that inspects the `Content-Encoding` header and rejects responses containing more than a safe number of encoding layers.","status":"published","published":"2026-09-21T21:21:21Z","modified":"2026-09-23T19:07:58Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T17:28:00.170339Z"}],"ghsa":[{"id":"GHSA-5vjj-2r48-q622","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"### Impact\n\n**Who is impacted**:\n  - Any application using Zapros to make HTTP requests to untrusted servers\n  - Applications that follow redirects to attacker-controlled hosts\n\n**Attack vector**:\n  - 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.\n\n### Patches\n\nFixed in version 0.14.0.\n\n  The fix adds a hardcoded limit of **5** `Content-Encoding` layers. Responses exceeding this limit raise `DecodingError`.\n\n### Workarounds\n\nAdd middleware that checks for a malicious Content-Encoding header.\n\n```python\nfrom typing import cast\n\nfrom zapros import (\n    AsyncBaseHandler,\n    AsyncBaseMiddleware,\n    BaseHandler,\n    BaseMiddleware,\n    Client,\n    DecodingError,\n    Request,\n    Response,\n)\n\nMAX_DECODE_LAYERS = 5\n\n\nclass ContentEncodingCheckMiddleware(BaseMiddleware, AsyncBaseMiddleware):\n    def __init__(\n        self,\n        next_handler: BaseHandler | AsyncBaseHandler,\n        *,\n        max_layers: int = MAX_DECODE_LAYERS,\n    ) -\u003e None:\n        self.next = cast(BaseHandler, next_handler)\n        self.async_next = cast(AsyncBaseHandler, next_handler)\n        self._max_layers = max_layers\n\n    def _check(self, response: Response) -\u003e None:\n        encoding_header = response.headers.get(\"Content-Encoding\")\n        if not encoding_header:\n            return\n\n        layers = [enc.strip().lower() for enc in encoding_header.split(\",\") if enc.strip()]\n        if len(layers) \u003e self._max_layers:\n            raise DecodingError(f\"Too many Content-Encoding layers ({len(layers)}), maximum is {self._max_layers}\")\n\n    def handle(self, request: Request) -\u003e Response:\n        response = self.next.handle(request)\n        self._check(response)\n        return response\n\n    async def ahandle(self, request: Request) -\u003e Response:\n        response = await self.async_next.ahandle(request)\n        self._check(response)\n        return response\n\n\nwith Client().wrap_with_middleware(lambda next: ContentEncodingCheckMiddleware(next)) as client:\n    ...\n```","status":"published","published":"2026-09-23T13:58:35Z","modified":"2026-09-23T13:58:36Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-770"],"updated_at":"2026-11-02T14:41:20.575159Z"}],"nvd":[{"id":"CVE-2026-61541","description":"Zapros, a Python HTTP client, prior to version 0.14.0 is vulnerable to denial of service when an application requests content from an untrusted server, or follows a redirect to one, because a malicious response containing an excessive number of chained `Content-Encoding` values causes Zapros to construct a deeply nested decompression chain that consumes excessive resources. Version 0.14.0 patches the vulnerability by limiting responses to five content-encoding layers and raising `DecodingError` when that limit is exceeded. As a workaround, applications can add response middleware that inspects the `Content-Encoding` header and rejects responses containing more than a safe number of encoding layers.","status":"published","published":"2026-09-21T22:16:57.527Z","modified":"2026-09-30T17:32:07.107Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"}],"cwes":["CWE-770"],"updated_at":"2026-11-02T14:12:50.368368Z"}],"osv":[{"id":"CVE-2026-61541","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"Zapros, a Python HTTP client, prior to version 0.14.0 is vulnerable to denial of service when an application requests content from an untrusted server, or follows a redirect to one, because a malicious response containing an excessive number of chained `Content-Encoding` values causes Zapros to construct a deeply nested decompression chain that consumes excessive resources. Version 0.14.0 patches the vulnerability by limiting responses to five content-encoding layers and raising `DecodingError` when that limit is exceeded. As a workaround, applications can add response middleware that inspects the `Content-Encoding` header and rejects responses containing more than a safe number of encoding layers.","status":"published","published":"2026-09-21T21:21:21.321Z","modified":"2026-10-28T02:30:27.91107419Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-770"],"updated_at":"2026-11-02T15:45:12.933503Z"},{"id":"GHSA-5vjj-2r48-q622","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"### Impact\n\n**Who is impacted**:\n  - Any application using Zapros to make HTTP requests to untrusted servers\n  - Applications that follow redirects to attacker-controlled hosts\n\n**Attack vector**:\n  - 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.\n\n### Patches\n\nFixed in version 0.14.0.\n\n  The fix adds a hardcoded limit of **5** `Content-Encoding` layers. Responses exceeding this limit raise `DecodingError`.\n\n### Workarounds\n\nAdd middleware that checks for a malicious Content-Encoding header.\n\n```python\nfrom typing import cast\n\nfrom zapros import (\n    AsyncBaseHandler,\n    AsyncBaseMiddleware,\n    BaseHandler,\n    BaseMiddleware,\n    Client,\n    DecodingError,\n    Request,\n    Response,\n)\n\nMAX_DECODE_LAYERS = 5\n\n\nclass ContentEncodingCheckMiddleware(BaseMiddleware, AsyncBaseMiddleware):\n    def __init__(\n        self,\n        next_handler: BaseHandler | AsyncBaseHandler,\n        *,\n        max_layers: int = MAX_DECODE_LAYERS,\n    ) -\u003e None:\n        self.next = cast(BaseHandler, next_handler)\n        self.async_next = cast(AsyncBaseHandler, next_handler)\n        self._max_layers = max_layers\n\n    def _check(self, response: Response) -\u003e None:\n        encoding_header = response.headers.get(\"Content-Encoding\")\n        if not encoding_header:\n            return\n\n        layers = [enc.strip().lower() for enc in encoding_header.split(\",\") if enc.strip()]\n        if len(layers) \u003e self._max_layers:\n            raise DecodingError(f\"Too many Content-Encoding layers ({len(layers)}), maximum is {self._max_layers}\")\n\n    def handle(self, request: Request) -\u003e Response:\n        response = self.next.handle(request)\n        self._check(response)\n        return response\n\n    async def ahandle(self, request: Request) -\u003e Response:\n        response = await self.async_next.ahandle(request)\n        self._check(response)\n        return response\n\n\nwith Client().wrap_with_middleware(lambda next: ContentEncodingCheckMiddleware(next)) as client:\n    ...\n```","status":"published","published":"2026-09-23T13:58:35Z","modified":"2026-10-01T17:56:21.759535591Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-770"],"updated_at":"2026-11-02T15:46:38.911014Z"},{"id":"PYSEC-2026-4181","title":"Zapros has an Unbounded Content-Encoding decompression chain that allows denial of service","description":"### Impact\n\n**Who is impacted**:\n  - Any application using Zapros to make HTTP requests to untrusted servers\n  - Applications that follow redirects to attacker-controlled hosts\n\n**Attack vector**:\n  - 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.\n\n### Patches\n\nFixed in version 0.14.0.\n\n  The fix adds a hardcoded limit of **5** `Content-Encoding` layers. Responses exceeding this limit raise `DecodingError`.\n\n### Workarounds\n\nAdd middleware that checks for a malicious Content-Encoding header.\n\n```python\nfrom typing import cast\n\nfrom zapros import (\n    AsyncBaseHandler,\n    AsyncBaseMiddleware,\n    BaseHandler,\n    BaseMiddleware,\n    Client,\n    DecodingError,\n    Request,\n    Response,\n)\n\nMAX_DECODE_LAYERS = 5\n\n\nclass ContentEncodingCheckMiddleware(BaseMiddleware, AsyncBaseMiddleware):\n    def __init__(\n        self,\n        next_handler: BaseHandler | AsyncBaseHandler,\n        *,\n        max_layers: int = MAX_DECODE_LAYERS,\n    ) -\u003e None:\n        self.next = cast(BaseHandler, next_handler)\n        self.async_next = cast(AsyncBaseHandler, next_handler)\n        self._max_layers = max_layers\n\n    def _check(self, response: Response) -\u003e None:\n        encoding_header = response.headers.get(\"Content-Encoding\")\n        if not encoding_header:\n            return\n\n        layers = [enc.strip().lower() for enc in encoding_header.split(\",\") if enc.strip()]\n        if len(layers) \u003e self._max_layers:\n            raise DecodingError(f\"Too many Content-Encoding layers ({len(layers)}), maximum is {self._max_layers}\")\n\n    def handle(self, request: Request) -\u003e Response:\n        response = self.next.handle(request)\n        self._check(response)\n        return response\n\n    async def ahandle(self, request: Request) -\u003e Response:\n        response = await self.async_next.ahandle(request)\n        self._check(response)\n        return response\n\n\nwith Client().wrap_with_middleware(lambda next: ContentEncodingCheckMiddleware(next)) as client:\n    ...\n```","status":"published","published":"2026-10-01T16:38:38.510801Z","modified":"2026-10-01T17:45:18.544159815Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T15:52:57.671918Z"}]}}
