File: CVE-2024-6866.patch

package info (click to toggle)
python-flask-cors 3.0.10-2%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 548 kB
  • sloc: python: 1,791; makefile: 138; sh: 17
file content (130 lines) | stat: -rw-r--r-- 6,060 bytes parent folder | download
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
From: Adriano Sela Aviles <adriano.selaviles@gmail.com>
Date: Wed, 14 May 2025 21:13:34 -0700
Subject: [PATCH] [CVE-2024-6866] Case Sensitive Request Path Matching (#390)

* [CVE-2024-6866] Case Sensitive Request Path Matching

* Update flask_cors/core.py

Co-authored-by: Cory Dolphin <corydolphin@gmail.com>

Reviewed-By: Daniel Leidert <dleidert@debian.org>
Origin: https://github.com/corydolphin/flask-cors/commit/eb39516a3c96b90d0ae5f51293972395ec3ef358
Bug: https://github.com/corydolphin/flask-cors/pull/390
Bug-Debian: https://bugs.debian.org/1100988
Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-6866
Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-6866
---
 flask_cors/core.py         | 47 +++++++++++++++++++++++++++-------------------
 flask_cors/extension.py    |  2 +-
 tests/core/helper_tests.py |  9 ++++++---
 3 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/flask_cors/core.py b/flask_cors/core.py
index 7654cb1..ac9682d 100644
--- a/flask_cors/core.py
+++ b/flask_cors/core.py
@@ -123,9 +123,10 @@ def get_cors_origins(options, request_origin):
         if wildcard and options.get('send_wildcard'):
             LOG.debug("Allowed origins are set to '*'. Sending wildcard CORS header.")
             return ['*']
-        # If the value of the Origin header is a case-sensitive match
-        # for any of the values in list of origins
-        elif try_match_any(request_origin, origins):
+        # If the value of the Origin header is a case-insensitive match
+        # for any of the values in list of origins.
+        # NOTE: Per RFC 1035 and RFC 4343 schemes and hostnames are case insensitive.
+        elif try_match_any_pattern(request_origin, origins, caseSensitive=False):
             LOG.debug("The request's Origin header matches. Sending CORS headers.", )
             # Add a single Access-Control-Allow-Origin header, with either
             # the value of the Origin header or the string "*" as value.
@@ -163,10 +164,7 @@ def get_allow_headers(options, acl_request_headers):
         request_headers = [h.strip() for h in acl_request_headers.split(',')]
 
         # any header that matches in the allow_headers
-        matching_headers = filter(
-            lambda h: try_match_any(h, options.get('allow_headers')),
-            request_headers
-        )
+        matching_headers = filter(lambda h: try_match_any_pattern(h, options.get("allow_headers"), caseSensitive=False), request_headers)
 
         return ', '.join(sorted(matching_headers))
 
@@ -268,21 +266,32 @@ def re_fix(reg):
     return r'.*' if reg == r'*' else reg
 
 
-def try_match_any(inst, patterns):
-    return any(try_match(inst, pattern) for pattern in patterns)
+def try_match_any_pattern(inst, patterns, caseSensitive=True):
+    return any(try_match_pattern(inst, pattern, caseSensitive) for pattern in patterns)
 
 
-def try_match(request_origin, maybe_regex):
-    """Safely attempts to match a pattern or string to a request origin."""
-    if isinstance(maybe_regex, RegexObject):
-        return re.match(maybe_regex, request_origin)
-    elif probably_regex(maybe_regex):
-        return re.match(maybe_regex, request_origin, flags=re.IGNORECASE)
-    else:
+def try_match_pattern(value, pattern, caseSensitive=True):
+    """
+    Safely attempts to match a pattern or string to a value. This
+    function can be used to match request origins, headers, or paths.
+    The value of caseSensitive should be set in accordance to the
+    data being compared e.g. origins and headers are case insensitive
+    whereas paths are case-sensitive
+    """
+    if isinstance(pattern, RegexObject):
+        return re.match(pattern, value)
+    if probably_regex(pattern):
+        flags = 0 if caseSensitive else re.IGNORECASE
         try:
-            return request_origin.lower() == maybe_regex.lower()
-        except AttributeError:
-            return request_origin == maybe_regex
+            return re.match(pattern, value, flags=flags)
+        except re.error:
+            return False
+    try:
+        v = str(value)
+        p = str(pattern)
+        return v == p if caseSensitive else v.casefold() == p.casefold()
+    except Exception:
+        return value == pattern
 
 
 def get_cors_options(appInstance, *dicts):
diff --git a/flask_cors/extension.py b/flask_cors/extension.py
index 77cf47d..44b0a09 100644
--- a/flask_cors/extension.py
+++ b/flask_cors/extension.py
@@ -179,7 +179,7 @@ def make_after_request_function(resources):
             return resp
         normalized_path = unquote_plus(request.path)
         for res_regex, res_options in resources:
-            if try_match(normalized_path, res_regex):
+            if try_match_pattern(normalized_path, res_regex, caseSensitive=True):
                 LOG.debug("Request to '%r' matches CORS resource '%s'. Using options: %s",
                       request.path, get_regexp_pattern(res_regex), res_options)
                 set_cors_headers(resp, res_options)
diff --git a/tests/core/helper_tests.py b/tests/core/helper_tests.py
index d7811da..4eedca8 100644
--- a/tests/core/helper_tests.py
+++ b/tests/core/helper_tests.py
@@ -17,9 +17,12 @@ from flask_cors.core import *
 
 
 class InternalsTestCase(unittest.TestCase):
-    def test_try_match(self):
-        self.assertFalse(try_match('www.com/foo', 'www.com/fo'))
-        self.assertTrue(try_match('www.com/foo', 'www.com/fo*'))
+    def test_try_match_pattern(self):
+        self.assertFalse(try_match_pattern('www.com/foo', 'www.com/fo', caseSensitive=True))
+        self.assertTrue(try_match_pattern('www.com/foo', 'www.com/fo*', caseSensitive=True))
+        self.assertTrue(try_match_pattern('www.com', 'WwW.CoM', caseSensitive=False))
+        self.assertTrue(try_match_pattern('/foo', '/fo*', caseSensitive=True))
+        self.assertFalse(try_match_pattern('/foo', '/Fo*', caseSensitive=True))
 
     def test_flexible_str_str(self):
         self.assertEquals(flexible_str('Bar, Foo, Qux'), 'Bar, Foo, Qux')