forked from pool/python39
- Security
- gh-135034: Fixes multiple issues that allowed tarfile
extraction filters (filter="data" and filter="tar") to be
bypassed using crafted symlinks and hard links.
- Addresses CVE-2024-12718 (bsc#1244056), CVE-2025-4138
(bsc#1244059), CVE-2025-4330 (bsc#1244060), and
CVE-2025-4517 (bsc#1244032).
- gh-133767: Fix use-after-free in the “unicode-escape”
decoder with a non-“strict” error handler (CVE-2025-4516,
bsc#1243273).
- gh-128840: Short-circuit the processing of long IPv6
addresses early in ipaddress to prevent excessive memory
consumption and a minor denial-of-service.
- gh-80222: Fix bug in the folding of quoted strings
when flattening an email message using a modern email
policy. Previously when a quoted string was folded so
that it spanned more than one line, the surrounding
quotes and internal escapes would be omitted. This could
theoretically be used to spoof header lines using a
carefully constructed quoted string if the resulting
rendered email was transmitted or re-parsed.
- Library
- gh-128840: Fix parsing long IPv6 addresses with embedded
IPv4 address.
- gh-134062: ipaddress: fix collisions in __hash__() for
IPv4Network and IPv6Network objects.
- gh-123409: Fix ipaddress.IPv6Address.reverse_pointer output
according to RFC 3596, §2.5. Patch by Bénédikt Tran.
- bpo-43633: Improve the textual representation of
OBS-URL: https://build.opensuse.org/package/show/devel:languages:python:Factory/python39?expand=0&rev=233
126 lines
4.4 KiB
Diff
126 lines
4.4 KiB
Diff
From 15eec9d5076b780463c3dc73afcef688651c5295 Mon Sep 17 00:00:00 2001
|
|
From: Serhiy Storchaka <storchaka@gmail.com>
|
|
Date: Sat, 17 Aug 2024 16:30:52 +0300
|
|
Subject: [PATCH] gh-123067: Fix quadratic complexity in parsing "-quoted
|
|
cookie values with backslashes (GH-123075)
|
|
|
|
This fixes CVE-2024-7592.
|
|
(cherry picked from commit 44e458357fca05ca0ae2658d62c8c595b048b5ef)
|
|
|
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
|
---
|
|
Lib/http/cookies.py | 34 ++------
|
|
Lib/test/test_http_cookies.py | 38 ++++++++++
|
|
Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst | 1
|
|
3 files changed, 47 insertions(+), 26 deletions(-)
|
|
create mode 100644 Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst
|
|
|
|
--- a/Lib/http/cookies.py
|
|
+++ b/Lib/http/cookies.py
|
|
@@ -184,8 +184,13 @@ def _quote(str):
|
|
return '"' + str.translate(_Translator) + '"'
|
|
|
|
|
|
-_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
|
|
-_QuotePatt = re.compile(r"[\\].")
|
|
+_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub
|
|
+
|
|
+def _unquote_replace(m):
|
|
+ if m[1]:
|
|
+ return chr(int(m[1], 8))
|
|
+ else:
|
|
+ return m[2]
|
|
|
|
def _unquote(str):
|
|
# If there aren't any doublequotes,
|
|
@@ -205,30 +210,7 @@ def _unquote(str):
|
|
# \012 --> \n
|
|
# \" --> "
|
|
#
|
|
- i = 0
|
|
- n = len(str)
|
|
- res = []
|
|
- while 0 <= i < n:
|
|
- o_match = _OctalPatt.search(str, i)
|
|
- q_match = _QuotePatt.search(str, i)
|
|
- if not o_match and not q_match: # Neither matched
|
|
- res.append(str[i:])
|
|
- break
|
|
- # else:
|
|
- j = k = -1
|
|
- if o_match:
|
|
- j = o_match.start(0)
|
|
- if q_match:
|
|
- k = q_match.start(0)
|
|
- if q_match and (not o_match or k < j): # QuotePatt matched
|
|
- res.append(str[i:k])
|
|
- res.append(str[k+1])
|
|
- i = k + 2
|
|
- else: # OctalPatt matched
|
|
- res.append(str[i:j])
|
|
- res.append(chr(int(str[j+1:j+4], 8)))
|
|
- i = j + 4
|
|
- return _nulljoin(res)
|
|
+ return _unquote_sub(_unquote_replace, str)
|
|
|
|
# The _getdate() routine is used to set the expiration time in the cookie's HTTP
|
|
# header. By default, _getdate() returns the current time in the appropriate
|
|
--- a/Lib/test/test_http_cookies.py
|
|
+++ b/Lib/test/test_http_cookies.py
|
|
@@ -5,6 +5,7 @@ from test.support import run_unittest, r
|
|
import unittest
|
|
from http import cookies
|
|
import pickle
|
|
+from test import support
|
|
|
|
|
|
class CookieTests(unittest.TestCase):
|
|
@@ -58,6 +59,43 @@ class CookieTests(unittest.TestCase):
|
|
for k, v in sorted(case['dict'].items()):
|
|
self.assertEqual(C[k].value, v)
|
|
|
|
+ def test_unquote(self):
|
|
+ cases = [
|
|
+ (r'a="b=\""', 'b="'),
|
|
+ (r'a="b=\\"', 'b=\\'),
|
|
+ (r'a="b=\="', 'b=='),
|
|
+ (r'a="b=\n"', 'b=n'),
|
|
+ (r'a="b=\042"', 'b="'),
|
|
+ (r'a="b=\134"', 'b=\\'),
|
|
+ (r'a="b=\377"', 'b=\xff'),
|
|
+ (r'a="b=\400"', 'b=400'),
|
|
+ (r'a="b=\42"', 'b=42'),
|
|
+ (r'a="b=\\042"', 'b=\\042'),
|
|
+ (r'a="b=\\134"', 'b=\\134'),
|
|
+ (r'a="b=\\\""', 'b=\\"'),
|
|
+ (r'a="b=\\\042"', 'b=\\"'),
|
|
+ (r'a="b=\134\""', 'b=\\"'),
|
|
+ (r'a="b=\134\042"', 'b=\\"'),
|
|
+ ]
|
|
+ for encoded, decoded in cases:
|
|
+ with self.subTest(encoded):
|
|
+ C = cookies.SimpleCookie()
|
|
+ C.load(encoded)
|
|
+ self.assertEqual(C['a'].value, decoded)
|
|
+
|
|
+ @support.requires_resource('cpu')
|
|
+ def test_unquote_large(self):
|
|
+ n = 10**6
|
|
+ for encoded in r'\\', r'\134':
|
|
+ with self.subTest(encoded):
|
|
+ data = 'a="b=' + encoded*n + ';"'
|
|
+ C = cookies.SimpleCookie()
|
|
+ C.load(data)
|
|
+ value = C['a'].value
|
|
+ self.assertEqual(value[:3], 'b=\\')
|
|
+ self.assertEqual(value[-2:], '\\;')
|
|
+ self.assertEqual(len(value), n + 3)
|
|
+
|
|
def test_load(self):
|
|
C = cookies.SimpleCookie()
|
|
C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme')
|
|
--- /dev/null
|
|
+++ b/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst
|
|
@@ -0,0 +1 @@
|
|
+Fix quadratic complexity in parsing ``"``-quoted cookie values with backslashes by :mod:`http.cookies`.
|