From e710393d142edf56294c4490dbe08e5980160b19 Mon Sep 17 00:00:00 2001 From: "Paul J. Dorn" Date: Sun, 17 Dec 2023 17:11:58 +0100 Subject: [PATCH] HTTP parser: stricter chunk-ext OBS handling chunk extensions are silently ignored before and after this change; its just the whitespace handling for the case without extensions that matters applying same strip(WS)->rstrip(BWS) replacement as already done in related cases half-way fix: could probably reject all BWS cases, rejecting only misplaced ones --- gunicorn/http/body.py | 5 ++++- tests/requests/invalid/chunked_09.http | 7 +++++++ tests/requests/invalid/chunked_09.py | 2 ++ tests/requests/invalid/chunked_10.http | 7 +++++++ tests/requests/invalid/chunked_10.py | 2 ++ tests/requests/invalid/chunked_11.http | 7 +++++++ tests/requests/invalid/chunked_11.py | 2 ++ tests/requests/valid/025.http | 2 +- 8 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/requests/invalid/chunked_09.http create mode 100644 tests/requests/invalid/chunked_09.py create mode 100644 tests/requests/invalid/chunked_10.http create mode 100644 tests/requests/invalid/chunked_10.py create mode 100644 tests/requests/invalid/chunked_11.http create mode 100644 tests/requests/invalid/chunked_11.py diff --git a/gunicorn/http/body.py b/gunicorn/http/body.py index 2ae0eb84..78f03214 100644 --- a/gunicorn/http/body.py +++ b/gunicorn/http/body.py @@ -85,7 +85,10 @@ class ChunkedReader(object): data = buf.getvalue() line, rest_chunk = data[:idx], data[idx + 2:] - chunk_size = line.split(b";", 1)[0].strip() + # RFC9112 7.1.1: BWS before chunk-ext - but ONLY then + chunk_size, *chunk_ext = line.split(b";", 1) + if chunk_ext: + chunk_size = chunk_size.rstrip(b" \t") if any(n not in b"0123456789abcdefABCDEF" for n in chunk_size): raise InvalidChunkSize(chunk_size) chunk_size = int(chunk_size, 16) diff --git a/tests/requests/invalid/chunked_09.http b/tests/requests/invalid/chunked_09.http new file mode 100644 index 00000000..6207310c --- /dev/null +++ b/tests/requests/invalid/chunked_09.http @@ -0,0 +1,7 @@ +POST /chunked_ows_without_ext HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +\r\n +5\r\n +hello\r\n +0 \r\n +\r\n diff --git a/tests/requests/invalid/chunked_09.py b/tests/requests/invalid/chunked_09.py new file mode 100644 index 00000000..0571e118 --- /dev/null +++ b/tests/requests/invalid/chunked_09.py @@ -0,0 +1,2 @@ +from gunicorn.http.errors import InvalidChunkSize +request = InvalidChunkSize diff --git a/tests/requests/invalid/chunked_10.http b/tests/requests/invalid/chunked_10.http new file mode 100644 index 00000000..db1e4c38 --- /dev/null +++ b/tests/requests/invalid/chunked_10.http @@ -0,0 +1,7 @@ +POST /chunked_ows_before HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +\r\n +5\r\n +hello\r\n + 0\r\n +\r\n diff --git a/tests/requests/invalid/chunked_10.py b/tests/requests/invalid/chunked_10.py new file mode 100644 index 00000000..0571e118 --- /dev/null +++ b/tests/requests/invalid/chunked_10.py @@ -0,0 +1,2 @@ +from gunicorn.http.errors import InvalidChunkSize +request = InvalidChunkSize diff --git a/tests/requests/invalid/chunked_11.http b/tests/requests/invalid/chunked_11.http new file mode 100644 index 00000000..da1f72e5 --- /dev/null +++ b/tests/requests/invalid/chunked_11.http @@ -0,0 +1,7 @@ +POST /chunked_ows_before HTTP/1.1\r\n +Transfer-Encoding: chunked\r\n +\r\n +5\n;\r\n +hello\r\n +0\r\n +\r\n diff --git a/tests/requests/invalid/chunked_11.py b/tests/requests/invalid/chunked_11.py new file mode 100644 index 00000000..0571e118 --- /dev/null +++ b/tests/requests/invalid/chunked_11.py @@ -0,0 +1,2 @@ +from gunicorn.http.errors import InvalidChunkSize +request = InvalidChunkSize diff --git a/tests/requests/valid/025.http b/tests/requests/valid/025.http index f8d7fae2..214f3094 100644 --- a/tests/requests/valid/025.http +++ b/tests/requests/valid/025.http @@ -3,7 +3,7 @@ Transfer-Encoding: chunked\r\n \r\n 5; some; parameters=stuff\r\n hello\r\n -6; blahblah; blah\r\n +6 \t;\tblahblah; blah\r\n world\r\n 0\r\n \r\n