From 75fca0b00b4297d0a30c51ae97a65428336eb2c1 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 25 Jan 2022 12:24:14 +0200 Subject: [PATCH] Add HTTP method validation (#6533) --- CHANGES/6533.feature | 1 + aiohttp/client_reqrep.py | 10 +++++++++- tests/test_client_request.py | 5 +++++ tests/test_web_request.py | 9 +++++++-- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 CHANGES/6533.feature diff --git a/CHANGES/6533.feature b/CHANGES/6533.feature new file mode 100644 index 00000000000..36bcbeb21dc --- /dev/null +++ b/CHANGES/6533.feature @@ -0,0 +1 @@ +Add HTTP method validation. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 7344ce24431..d54fffa0aaa 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -84,6 +84,9 @@ from .tracing import Trace +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + + def _gen_default_accept_encoding() -> str: return "gzip, deflate, br" if HAS_BROTLI else "gzip, deflate" @@ -208,7 +211,12 @@ def __init__( proxy_headers: Optional[LooseHeaders] = None, traces: Optional[List["Trace"]] = None, ): - + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + f"Method cannot contain non-token characters {method!r} " + "(found at least {match.group()!r})" + ) assert isinstance(url, URL), url assert isinstance(proxy, (URL, type(None))), proxy # FIXME: session is None in tests only, need to fix tests diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 733922dff72..6810efd7cb5 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -88,6 +88,11 @@ def test_method3(make_request: Any) -> None: assert req.method == "HEAD" +def test_method_invalid(make_request: Any) -> None: + with pytest.raises(ValueError, match="Method cannot contain non-token characters"): + make_request("METHOD WITH\nWHITESPACES", "http://python.org/") + + def test_version_1_0(make_request: Any) -> None: req = make_request("get", "http://python.org/", version="1.0") assert req.version == (1, 0) diff --git a/tests/test_web_request.py b/tests/test_web_request.py index 9a89bf7f30a..9b20fd03717 100644 --- a/tests/test_web_request.py +++ b/tests/test_web_request.py @@ -46,7 +46,10 @@ def test_base_ctor() -> None: assert "GET" == req.method assert HttpVersion(1, 1) == req.version - assert req.host == socket.getfqdn() + # MacOS may return CamelCased host name, need .lower() + # FQDN can be wider than host, e.g. + # 'fv-az397-495' in 'fv-az397-495.internal.cloudapp.net' + assert req.host.lower() in socket.getfqdn().lower() assert "/path/to?a=1&b=2" == req.path_qs assert "/path/to" == req.path assert "a=1&b=2" == req.query_string @@ -71,7 +74,9 @@ def test_ctor() -> None: assert "GET" == req.method assert HttpVersion(1, 1) == req.version # MacOS may return CamelCased host name, need .lower() - assert req.host.lower() == socket.getfqdn().lower() + # FQDN can be wider than host, e.g. + # 'fv-az397-495' in 'fv-az397-495.internal.cloudapp.net' + assert req.host.lower() in socket.getfqdn().lower() assert "/path/to?a=1&b=2" == req.path_qs assert "/path/to" == req.path assert "a=1&b=2" == req.query_string