VYPR
Moderate severityNVD Advisory· Published Mar 20, 2025· Updated Nov 3, 2025

Case-Insensitive Path Matching in corydolphin/flask-cors

CVE-2024-6866

Description

corydolphin/flask-cors version 4.01 contains a vulnerability where the request path matching is case-insensitive due to the use of the try_match function, which is originally intended for matching hosts. This results in a mismatch because paths in URLs are case-sensitive, but the regex matching treats them as case-insensitive. This misconfiguration can lead to significant security vulnerabilities, allowing unauthorized origins to access paths meant to be restricted, resulting in data exposure and potential data leaks.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
flask-corsPyPI
< 6.0.06.0.0

Affected products

1

Patches

1
eb39516a3c96

[CVE-2024-6866] Case Sensitive Request Path Matching (#390)

https://github.com/corydolphin/flask-corsAdriano Sela AvilesMay 15, 2025via ghsa
3 files changed · +36 23
  • flask_cors/core.py+28 18 modified
    @@ -121,9 +121,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.",
                 )
    @@ -164,7 +165,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))
     
    @@ -277,22 +278,31 @@ 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):
         """
    
  • flask_cors/extension.py+2 2 modified
    @@ -3,7 +3,7 @@
     
     from flask import request
     
    -from .core import ACL_ORIGIN, get_cors_options, get_regexp_pattern, parse_resources, set_cors_headers, try_match
    +from .core import ACL_ORIGIN, get_cors_options, get_regexp_pattern, parse_resources, set_cors_headers, try_match_pattern
     
     LOG = logging.getLogger(__name__)
     
    @@ -190,7 +190,7 @@ def cors_after_request(resp):
                 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,
    
  • tests/core/helper_tests.py+6 3 modified
    @@ -17,9 +17,12 @@
     
     
     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.assertEqual(flexible_str('Bar, Foo, Qux'), 'Bar, Foo, Qux')
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.