Skip to content

Commit 0341d64

Browse files
[PR aio-libs#12231/7043bc56 backport][3.14] Adjust header value character checks to RFC 9110 (aio-libs#12236)
1 parent 9d58822 commit 0341d64

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

CHANGES/12231.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Adjusted pure-Python request header value validation to align with RFC 9110 control-character handling, while preserving lax response parser behavior, and added regression tests for Host/header control-character cases.
2+
-- by :user:`rodrigobnogueira`.

aiohttp/http_parser.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070
VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d)\.(\d)", re.ASCII)
7171
DIGITS: Final[Pattern[str]] = re.compile(r"\d+", re.ASCII)
7272
HEXDIGITS: Final[Pattern[bytes]] = re.compile(rb"[0-9a-fA-F]+")
73+
# https://www.rfc-editor.org/rfc/rfc9110#section-5.5-5
74+
_FIELD_VALUE_FORBIDDEN_CTL_RE: Final[Pattern[str]] = re.compile(
75+
r"[\x00-\x08\x0a-\x1f\x7f]"
76+
)
7377

7478

7579
class RawRequestMessage(NamedTuple):
@@ -194,7 +198,10 @@ def parse_headers(
194198
value = bvalue.decode("utf-8", "surrogateescape")
195199

196200
# https://www.rfc-editor.org/rfc/rfc9110.html#section-5.5-5
197-
if "\n" in value or "\r" in value or "\x00" in value:
201+
if self._lax:
202+
if "\n" in value or "\r" in value or "\x00" in value:
203+
raise InvalidHeader(bvalue)
204+
elif _FIELD_VALUE_FORBIDDEN_CTL_RE.search(value):
198205
raise InvalidHeader(bvalue)
199206

200207
headers.add(name, value)

tests/test_http_parser.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ def test_bad_header_name(parser: Any, rfc9110_5_6_2_token_delim: str) -> None:
221221
"Foo : bar", # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2
222222
"Foo\t: bar",
223223
"\xffoo: bar",
224+
"Foo: abc\x01def", # CTL bytes forbidden per RFC 9110 §5.5
225+
"Foo: abc\x7fdef", # DEL is also a CTL byte
226+
"Foo: abc\x1fdef",
224227
),
225228
)
226229
def test_bad_headers(parser: Any, hdr: str) -> None:
@@ -229,6 +232,13 @@ def test_bad_headers(parser: Any, hdr: str) -> None:
229232
parser.feed_data(text)
230233

231234

235+
def test_ctl_host_header_bad_characters(parser: HttpRequestParser) -> None:
236+
"""CTL byte in Host header must be rejected."""
237+
text = b"GET /test HTTP/1.1\r\nHost: trusted.example\x01@bad.test\r\n\r\n"
238+
with pytest.raises(http_exceptions.BadHttpMessage):
239+
parser.feed_data(text)
240+
241+
232242
def test_unpaired_surrogate_in_header_py(loop: Any, protocol: Any) -> None:
233243
parser = HttpRequestParserPy(
234244
protocol,

0 commit comments

Comments
 (0)