Improper Access Control in corydolphin/flask-cors
Description
A vulnerability in corydolphin/flask-cors version 4.0.1 allows the Access-Control-Allow-Private-Network CORS header to be set to true by default. This behavior can expose private network resources to unauthorized external access, leading to significant security risks such as data breaches, unauthorized access to sensitive information, and potential network intrusions.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
Flask-CorsPyPI | < 4.0.2 | 4.0.2 |
Affected products
1- Range: unspecified
Patches
3c8514760cf03V5: Breaking: Change default to disable private network access (#368)
4 files changed · +17 −17
docs/configuration.rst+14 −14 modified@@ -54,37 +54,37 @@ CORS_INTERCEPT_EXCEPTIONS (:py:class:`bool`) Whether to deal with Flask exception handlers or leave them alone (with respect to CORS headers). CORS_MAX_AGE (:py:class:`~datetime.timedelta`, :py:class:`int` or :py:class:`str`) - The maximum time for which this CORS request may be cached. + The maximum time for which this CORS request may be cached. This value is set as the :http:header:`Access-Control-Max-Age` header. CORS_METHODS (:py:class:`~typing.List` or :py:class:`str`) The method(s) which the allowed origins are allowed to access. These are included in the :http:header:`Access-Control-Allow-Methods` response headers to the preflight OPTIONS requests. - + .. _cors_origins_setting: CORS_ORIGINS (:py:class:`~typing.List`, :py:class:`str` or :py:class:`re.Pattern`) The origin(s) to allow requests from. An origin configured here that matches the value of the :http:header:`Origin` header in a preflight OPTIONS request is returned as the value of the :http:header:`Access-Control-Allow-Origin` response header. CORS_RESOURCES (:py:class:`~typing.Dict`, :py:class:`~typing.List` or :py:class:`str`) - The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. - + The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. + If the value is a dictionary, it's keys must be regular expressions matching resources, and the values must be another dictionary of configuration options, as described in this section. - - If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied. - - If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied. + + If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied. + + If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied. CORS_SEND_WILDCARD (:py:class:`bool`) If :ref:`CORS_ORIGINS <cors_origins_setting>` is ``"*"`` and this is true, then the :http:header:`Access-Control-Allow-Origin` response header's value with be ``"*"`` as well, instead of the value of the :http:header:`Origin` request header. CORS_SUPPORTS_CREDENTIALS (:py:class:`bool`) - Allows users to make authenticated requests. - If true, injects the :http:header:`Access-Control-Allow-Credentials` header in responses. - This allows cookies and credentials to be submitted across domains. - - :note: This option cannot be used in conjunction with a "*" origin + Allows users to make authenticated requests. + If true, injects the :http:header:`Access-Control-Allow-Credentials` header in responses. + This allows cookies and credentials to be submitted across domains. + + :note: This option cannot be used in conjunction with a "*" origin CORS_VARY_HEADER: (:py:class:`bool`) Enables or disables the injection of the :http:header:`Vary` response header is set to ``Origin``. @@ -96,7 +96,7 @@ Default values ~~~~~~~~~~~~~~ * CORS_ALLOW_HEADERS: "*" -* CORS_ALLOW_PRIVATE_NETWORK: True +* CORS_ALLOW_PRIVATE_NETWORK: False * CORS_ALWAYS_SEND: True * CORS_AUTOMATIC_OPTIONS: True * CORS_EXPOSE_HEADERS: None
flask_cors/core.py+1 −1 modified@@ -57,7 +57,7 @@ resources=r'/*', intercept_exceptions=True, always_send=True, - allow_private_network=True) + allow_private_network=False) def parse_resources(resources):
flask_cors/version.py+1 −1 modified@@ -1 +1 @@ -__version__ = '4.0.2' +__version__ = '5.0.0'
tests/decorator/test_private_network_headers.py+1 −1 modified@@ -37,7 +37,7 @@ def test_default(self): """ The default behavior should be to allow private network access. """ resp = self.get('/test_default', origin='www.example.com', headers={ACL_REQUEST_HEADER_PRIVATE_NETWORK:'true'}) - self.assertTrue(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers) + self.assertFalse(resp.headers.get('ACL_RESPONSE_PRIVATE_NETWORK')) resp = self.get('/test_default') self.assertFalse(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers)
03aa3f8e2256V5: Breaking: Change default to disable private network access
4 files changed · +17 −17
docs/configuration.rst+14 −14 modified@@ -54,37 +54,37 @@ CORS_INTERCEPT_EXCEPTIONS (:py:class:`bool`) Whether to deal with Flask exception handlers or leave them alone (with respect to CORS headers). CORS_MAX_AGE (:py:class:`~datetime.timedelta`, :py:class:`int` or :py:class:`str`) - The maximum time for which this CORS request may be cached. + The maximum time for which this CORS request may be cached. This value is set as the :http:header:`Access-Control-Max-Age` header. CORS_METHODS (:py:class:`~typing.List` or :py:class:`str`) The method(s) which the allowed origins are allowed to access. These are included in the :http:header:`Access-Control-Allow-Methods` response headers to the preflight OPTIONS requests. - + .. _cors_origins_setting: CORS_ORIGINS (:py:class:`~typing.List`, :py:class:`str` or :py:class:`re.Pattern`) The origin(s) to allow requests from. An origin configured here that matches the value of the :http:header:`Origin` header in a preflight OPTIONS request is returned as the value of the :http:header:`Access-Control-Allow-Origin` response header. CORS_RESOURCES (:py:class:`~typing.Dict`, :py:class:`~typing.List` or :py:class:`str`) - The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. - + The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. + If the value is a dictionary, it's keys must be regular expressions matching resources, and the values must be another dictionary of configuration options, as described in this section. - - If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied. - - If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied. + + If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied. + + If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied. CORS_SEND_WILDCARD (:py:class:`bool`) If :ref:`CORS_ORIGINS <cors_origins_setting>` is ``"*"`` and this is true, then the :http:header:`Access-Control-Allow-Origin` response header's value with be ``"*"`` as well, instead of the value of the :http:header:`Origin` request header. CORS_SUPPORTS_CREDENTIALS (:py:class:`bool`) - Allows users to make authenticated requests. - If true, injects the :http:header:`Access-Control-Allow-Credentials` header in responses. - This allows cookies and credentials to be submitted across domains. - - :note: This option cannot be used in conjunction with a "*" origin + Allows users to make authenticated requests. + If true, injects the :http:header:`Access-Control-Allow-Credentials` header in responses. + This allows cookies and credentials to be submitted across domains. + + :note: This option cannot be used in conjunction with a "*" origin CORS_VARY_HEADER: (:py:class:`bool`) Enables or disables the injection of the :http:header:`Vary` response header is set to ``Origin``. @@ -96,7 +96,7 @@ Default values ~~~~~~~~~~~~~~ * CORS_ALLOW_HEADERS: "*" -* CORS_ALLOW_PRIVATE_NETWORK: True +* CORS_ALLOW_PRIVATE_NETWORK: False * CORS_ALWAYS_SEND: True * CORS_AUTOMATIC_OPTIONS: True * CORS_EXPOSE_HEADERS: None
flask_cors/core.py+1 −1 modified@@ -57,7 +57,7 @@ resources=r'/*', intercept_exceptions=True, always_send=True, - allow_private_network=True) + allow_private_network=False) def parse_resources(resources):
flask_cors/version.py+1 −1 modified@@ -1 +1 @@ -__version__ = '4.0.2' +__version__ = '5.0.0'
tests/decorator/test_private_network_headers.py+1 −1 modified@@ -37,7 +37,7 @@ def test_default(self): """ The default behavior should be to allow private network access. """ resp = self.get('/test_default', origin='www.example.com', headers={ACL_REQUEST_HEADER_PRIVATE_NETWORK:'true'}) - self.assertTrue(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers) + self.assertFalse(resp.headers.get('ACL_RESPONSE_PRIVATE_NETWORK')) resp = self.get('/test_default') self.assertFalse(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers)
7ae310c56ac3Backwards Compatible Fix for CVE-2024-6221 (#363)
4 files changed · +36 −4
docs/configuration.rst+14 −0 modified@@ -23,6 +23,19 @@ CORS_ALLOW_HEADERS (:py:class:`~typing.List` or :py:class:`str`) Headers to accept from the client. Headers in the :http:header:`Access-Control-Request-Headers` request header (usually part of the preflight OPTIONS request) matching headers in this list will be included in the :http:header:`Access-Control-Allow-Headers` response header. +CORS_ALLOW_PRIVATE_NETWORK (:py:class:`bool`) + If True, the response header :http:header:`Access-Control-Allow-Private-Network` + will be set with the value 'true' whenever the request header + :http:header:`Access-Control-Request-Private-Network` has a value 'true'. + + If False, the reponse header :http:header:`Access-Control-Allow-Private-Network` + will be set with the value 'false' whenever the request header + :http:header:`Access-Control-Request-Private-Network` has a value of 'true'. + + If the request header :http:header:`Access-Control-Request-Private-Network` is + not present or has a value other than 'true', the response header + :http:header:`Access-Control-Allow-Private-Network` will not be set. + CORS_ALWAYS_SEND (:py:class:`bool`) Usually, if a request doesn't include an :http:header:`Origin` header, the client did not request CORS. This means we can ignore this request. @@ -83,6 +96,7 @@ Default values ~~~~~~~~~~~~~~ * CORS_ALLOW_HEADERS: "*" +* CORS_ALLOW_PRIVATE_NETWORK: True * CORS_ALWAYS_SEND: True * CORS_AUTOMATIC_OPTIONS: True * CORS_EXPOSE_HEADERS: None
flask_cors/core.py+5 −3 modified@@ -36,7 +36,7 @@ 'CORS_MAX_AGE', 'CORS_SEND_WILDCARD', 'CORS_AUTOMATIC_OPTIONS', 'CORS_VARY_HEADER', 'CORS_RESOURCES', 'CORS_INTERCEPT_EXCEPTIONS', - 'CORS_ALWAYS_SEND'] + 'CORS_ALWAYS_SEND', 'CORS_ALLOW_PRIVATE_NETWORK'] # Attribute added to request object by decorator to indicate that CORS # was evaluated, in case the decorator and extension are both applied # to a view. @@ -56,7 +56,8 @@ vary_header=True, resources=r'/*', intercept_exceptions=True, - always_send=True) + always_send=True, + allow_private_network=True) def parse_resources(resources): @@ -186,7 +187,8 @@ def get_cors_headers(options, request_headers, request_method): if ACL_REQUEST_HEADER_PRIVATE_NETWORK in request_headers \ and request_headers.get(ACL_REQUEST_HEADER_PRIVATE_NETWORK) == 'true': - headers[ACL_RESPONSE_PRIVATE_NETWORK] = 'true' + allow_private_network = 'true' if options.get('allow_private_network') else 'false' + headers[ACL_RESPONSE_PRIVATE_NETWORK] = allow_private_network # This is a preflight request # http://www.w3.org/TR/cors/#resource-preflight-requests
flask_cors/extension.py+16 −0 modified@@ -138,6 +138,22 @@ class CORS(object): Default : True :type vary_header: bool + + :param allow_private_network: + If True, the response header `Access-Control-Allow-Private-Network` + will be set with the value 'true' whenever the request header + `Access-Control-Request-Private-Network` has a value 'true'. + + If False, the reponse header `Access-Control-Allow-Private-Network` + will be set with the value 'false' whenever the request header + `Access-Control-Request-Private-Network` has a value of 'true'. + + If the request header `Access-Control-Request-Private-Network` is + not present or has a value other than 'true', the response header + `Access-Control-Allow-Private-Network` will not be set. + + Default : True + :type allow_private_network: bool """ def __init__(self, app=None, **kwargs):
flask_cors/version.py+1 −1 modified@@ -1 +1 @@ -__version__ = '4.0.1' +__version__ = '4.0.2'
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
10- github.com/advisories/GHSA-hxwh-jpp2-84pmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-6221ghsaADVISORY
- github.com/corydolphin/flask-cors/commit/03aa3f8e2256437f7bad96422a747b98ab5e31bfghsaWEB
- github.com/corydolphin/flask-cors/commit/7ae310c56ac30e0b94fb42129aa377bf633256ecghsaWEB
- github.com/corydolphin/flask-cors/commit/c8514760cf03fcce16d77f6db7007aad429c4548ghsaWEB
- github.com/corydolphin/flask-cors/issues/362ghsaWEB
- github.com/corydolphin/flask-cors/pull/363ghsaWEB
- github.com/corydolphin/flask-cors/pull/368ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/flask-cors/PYSEC-2024-71.yamlghsaWEB
- huntr.com/bounties/a42935fc-6f57-4818-bca4-3d528235df4dghsaWEB
News mentions
0No linked articles in our index yet.