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()
|