1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
From a2200dc43d9fe0ee19b9185b30749c204a4dfd45 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Wed, 8 Nov 2023 19:25:05 +0000
Subject: [PATCH] Add HTTP method validation (#6533) (#7806)
(cherry picked from commit 75fca0b00b4297d0a30c51ae97a65428336eb2c1)
Upstream-Status: Backport
[https://github.com/aio-libs/aiohttp/pull/7806/commits/a43bc1779892e7014b7723c59d08fb37a000955e]
CVE: CVE-2023-49082
Co-authored-by: Andrew Svetlov <andrew.svetlov@gmail.com>
Signed-off-by: Jiaying Song <jiaying.song.cn@windriver.com>
---
CHANGES/6533.feature | 1 +
aiohttp/client_reqrep.py | 9 ++++++++-
tests/test_client_request.py | 5 +++++
tests/test_web_request.py | 9 +++++++--
4 files changed, 21 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/6533.feature
diff --git a/CHANGES/6533.feature b/CHANGES/6533.feature
new file mode 100644
index 0000000..36bcbeb
--- /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 d3cd77e..a8135b2 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -78,6 +78,7 @@ if TYPE_CHECKING: # pragma: no cover
from .tracing import Trace
+_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json")
@@ -266,10 +267,16 @@ class ClientRequest:
proxy_headers: Optional[LooseHeaders] = None,
traces: Optional[List["Trace"]] = None,
):
-
if loop is None:
loop = asyncio.get_event_loop()
+ 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 009f1a0..d0f208b 100644
--- a/tests/test_client_request.py
+++ b/tests/test_client_request.py
@@ -89,6 +89,11 @@ def test_method3(make_request) -> None:
assert req.method == "HEAD"
+def test_method_invalid(make_request) -> 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) -> 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 c6aeaf8..2bb0cd5 100644
--- a/tests/test_web_request.py
+++ b/tests/test_web_request.py
@@ -43,7 +43,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
@@ -66,7 +69,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
--
2.25.1
|