Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 391e562

Browse filesBrowse files
[3.13] gh-123067: Fix quadratic complexity in parsing "-quoted cookie values with backslashes (GH-123075) (#123103)
gh-123067: Fix quadratic complexity in parsing "-quoted cookie values with backslashes (GH-123075) This fixes CVE-2024-7592. (cherry picked from commit 44e4583) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 5709cee commit 391e562
Copy full SHA for 391e562

File tree

Expand file treeCollapse file tree

3 files changed

+47
-26
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+47
-26
lines changed

‎Lib/http/cookies.py

Copy file name to clipboardExpand all lines: Lib/http/cookies.py
+8-26Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,13 @@ def _quote(str):
184184
return '"' + str.translate(_Translator) + '"'
185185

186186

187-
_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
188-
_QuotePatt = re.compile(r"[\\].")
187+
_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub
188+
189+
def _unquote_replace(m):
190+
if m[1]:
191+
return chr(int(m[1], 8))
192+
else:
193+
return m[2]
189194

190195
def _unquote(str):
191196
# If there aren't any doublequotes,
@@ -205,30 +210,7 @@ def _unquote(str):
205210
# \012 --> \n
206211
# \" --> "
207212
#
208-
i = 0
209-
n = len(str)
210-
res = []
211-
while 0 <= i < n:
212-
o_match = _OctalPatt.search(str, i)
213-
q_match = _QuotePatt.search(str, i)
214-
if not o_match and not q_match: # Neither matched
215-
res.append(str[i:])
216-
break
217-
# else:
218-
j = k = -1
219-
if o_match:
220-
j = o_match.start(0)
221-
if q_match:
222-
k = q_match.start(0)
223-
if q_match and (not o_match or k < j): # QuotePatt matched
224-
res.append(str[i:k])
225-
res.append(str[k+1])
226-
i = k + 2
227-
else: # OctalPatt matched
228-
res.append(str[i:j])
229-
res.append(chr(int(str[j+1:j+4], 8)))
230-
i = j + 4
231-
return _nulljoin(res)
213+
return _unquote_sub(_unquote_replace, str)
232214

233215
# The _getdate() routine is used to set the expiration time in the cookie's HTTP
234216
# header. By default, _getdate() returns the current time in the appropriate

‎Lib/test/test_http_cookies.py

Copy file name to clipboardExpand all lines: Lib/test/test_http_cookies.py
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import doctest
66
from http import cookies
77
import pickle
8+
from test import support
89

910

1011
class CookieTests(unittest.TestCase):
@@ -58,6 +59,43 @@ def test_basic(self):
5859
for k, v in sorted(case['dict'].items()):
5960
self.assertEqual(C[k].value, v)
6061

62+
def test_unquote(self):
63+
cases = [
64+
(r'a="b=\""', 'b="'),
65+
(r'a="b=\\"', 'b=\\'),
66+
(r'a="b=\="', 'b=='),
67+
(r'a="b=\n"', 'b=n'),
68+
(r'a="b=\042"', 'b="'),
69+
(r'a="b=\134"', 'b=\\'),
70+
(r'a="b=\377"', 'b=\xff'),
71+
(r'a="b=\400"', 'b=400'),
72+
(r'a="b=\42"', 'b=42'),
73+
(r'a="b=\\042"', 'b=\\042'),
74+
(r'a="b=\\134"', 'b=\\134'),
75+
(r'a="b=\\\""', 'b=\\"'),
76+
(r'a="b=\\\042"', 'b=\\"'),
77+
(r'a="b=\134\""', 'b=\\"'),
78+
(r'a="b=\134\042"', 'b=\\"'),
79+
]
80+
for encoded, decoded in cases:
81+
with self.subTest(encoded):
82+
C = cookies.SimpleCookie()
83+
C.load(encoded)
84+
self.assertEqual(C['a'].value, decoded)
85+
86+
@support.requires_resource('cpu')
87+
def test_unquote_large(self):
88+
n = 10**6
89+
for encoded in r'\\', r'\134':
90+
with self.subTest(encoded):
91+
data = 'a="b=' + encoded*n + ';"'
92+
C = cookies.SimpleCookie()
93+
C.load(data)
94+
value = C['a'].value
95+
self.assertEqual(value[:3], 'b=\\')
96+
self.assertEqual(value[-2:], '\\;')
97+
self.assertEqual(len(value), n + 3)
98+
6199
def test_load(self):
62100
C = cookies.SimpleCookie()
63101
C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme')
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix quadratic complexity in parsing ``"``-quoted cookie values with backslashes by :mod:`http.cookies`.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.