File: CVE-2024-6844.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 (107 lines) | stat: -rw-r--r-- 4,289 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
From: Adriano Sela Aviles <adriano.selaviles@gmail.com>
Date: Sat, 17 May 2025 07:32:23 -0700
Subject: [PATCH] [CVE-2024-6844] Replace use of (urllib) unquote_plus with
 unquote for paths (#389)

Reviewed-By: Daniel Leidert <dleidert@debian.org>
Origin: https://github.com/corydolphin/flask-cors/commit/35d875319621bd129a38b2b823abf4a2f6cda536
Bug: https://github.com/corydolphin/flask-cors/pull/389
Bug-Debian: https://bugs.debian.org/1100988
Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2024-6844
Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2024-6844
---
 flask_cors/extension.py               |  6 ++--
 tests/extension/test_app_extension.py | 56 +++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/flask_cors/extension.py b/flask_cors/extension.py
index 44b0a09..f3041c5 100644
--- a/flask_cors/extension.py
+++ b/flask_cors/extension.py
@@ -11,9 +11,9 @@
 from flask import request
 from .core import *
 try:
-    from urllib.parse import unquote_plus
+    from urllib.parse import unquote
 except ImportError:
-    from urllib import unquote_plus
+    from urllib import unquote
 
 LOG = logging.getLogger(__name__)
 
@@ -177,7 +177,7 @@ def make_after_request_function(resources):
         if resp.headers is not None and resp.headers.get(ACL_ORIGIN):
             LOG.debug('CORS have been already evaluated, skipping')
             return resp
-        normalized_path = unquote_plus(request.path)
+        normalized_path = unquote(request.path)
         for res_regex, res_options in resources:
             if try_match_pattern(normalized_path, res_regex, caseSensitive=True):
                 LOG.debug("Request to '%r' matches CORS resource '%s'. Using options: %s",
diff --git a/tests/extension/test_app_extension.py b/tests/extension/test_app_extension.py
index 597b744..b836eb7 100644
--- a/tests/extension/test_app_extension.py
+++ b/tests/extension/test_app_extension.py
@@ -378,5 +378,61 @@ class AppExtensionBadRegexp(FlaskCorsTestCase):
             self.assertEqual(resp.status_code, 200)
 
 
+class AppExtensionPlusInPath(FlaskCorsTestCase):
+    '''
+        Regression test for CVE-2024-6844:
+        Ensures that we correctly differentiate '+' from ' ' in URL paths.
+    '''
+
+    def setUp(self):
+        self.app = Flask(__name__)
+        CORS(self.app, resources={
+            r'/service\+path': {'origins': ['http://foo.com']},
+            r'/service path': {'origins': ['http://bar.com']},
+        })
+
+        @self.app.route('/service+path')
+        def plus_path():
+            return 'plus'
+
+        @self.app.route('/service path')
+        def space_path():
+            return 'space'
+
+        self.client = self.app.test_client()
+
+    def test_plus_path_origin_allowed(self):
+        '''
+        Ensure that CORS matches + literally and allows the correct origin
+        '''
+        response = self.client.get('/service+path', headers={'Origin': 'http://foo.com'})
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.headers.get(ACL_ORIGIN), 'http://foo.com')
+
+    def test_space_path_origin_allowed(self):
+        '''
+        Ensure that CORS treats /service path differently and allows correct origin
+        '''
+        response = self.client.get('/service%20path', headers={'Origin': 'http://bar.com'})
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.headers.get(ACL_ORIGIN), 'http://bar.com')
+
+    def test_plus_path_rejects_other_origin(self):
+        '''
+        Origin not allowed for + path should be rejected
+        '''
+        response = self.client.get('/service+path', headers={'Origin': 'http://bar.com'})
+        self.assertEqual(response.status_code, 200)
+        self.assertIsNone(response.headers.get(ACL_ORIGIN))
+
+    def test_space_path_rejects_other_origin(self):
+        '''
+        Origin not allowed for space path should be rejected
+        '''
+        response = self.client.get('/service%20path', headers={'Origin': 'http://foo.com'})
+        self.assertEqual(response.status_code, 200)
+        self.assertIsNone(response.headers.get(ACL_ORIGIN))
+
+
 if __name__ == "__main__":
     unittest.main()