Skip to content

Commit 24d7b67

Browse files
authored
Merge commit from fork
* Add a hard-coded limit for the decompression chain * Reuse new list
1 parent c19571d commit 24d7b67

3 files changed

Lines changed: 25 additions & 1 deletion

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed a security issue where an attacker could compose an HTTP response with
2+
virtually unlimited links in the ``Content-Encoding`` header, potentially
3+
leading to a denial of service (DoS) attack by exhausting system resources
4+
during decoding. The number of allowed chained encodings is now limited to 5.

‎src/urllib3/response.py‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,18 @@ class MultiDecoder(ContentDecoder):
306306
they were applied.
307307
"""
308308

309+
# Maximum allowed number of chained HTTP encodings in the
310+
# Content-Encoding header.
311+
max_decode_links = 5
312+
309313
def __init__(self, modes: str) -> None:
310-
self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")]
314+
encodings = [m.strip() for m in modes.split(",")]
315+
if len(encodings) > self.max_decode_links:
316+
raise DecodeError(
317+
"Too many content encodings in the chain: "
318+
f"{len(encodings)} > {self.max_decode_links}"
319+
)
320+
self._decoders = [_get_decoder(e) for e in encodings]
311321

312322
def flush(self) -> bytes:
313323
return self._decoders[0].flush()

‎test/test_response.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,16 @@ def test_read_multi_decoding_deflate_deflate(self) -> None:
847847
assert r.read(9 * 37) == b"foobarbaz" * 37
848848
assert r.read() == b""
849849

850+
def test_read_multi_decoding_too_many_links(self) -> None:
851+
fp = BytesIO(b"foo")
852+
with pytest.raises(
853+
DecodeError, match="Too many content encodings in the chain: 6 > 5"
854+
):
855+
HTTPResponse(
856+
fp,
857+
headers={"content-encoding": "gzip, deflate, br, zstd, gzip, deflate"},
858+
)
859+
850860
def test_body_blob(self) -> None:
851861
resp = HTTPResponse(b"foo")
852862
assert resp.data == b"foo"

0 commit comments

Comments
 (0)