22
33import pprint
44from collections .abc import Sequence
5+ from importlib .metadata import version
56from typing import TYPE_CHECKING , Any
67
78from django .conf import settings
8- from django .core .checks import Error
9+ from django .core import checks
10+ from django .core .checks import Error , register
11+
12+ from packaging .version import Version
913
1014from csp .constants import NONCE
1115
@@ -77,6 +81,7 @@ def migrate_settings() -> tuple[dict[str, Any], bool]:
7781 return config , REPORT_ONLY
7882
7983
84+ @register (checks .Tags .security )
8085def check_django_csp_lt_4_0 (app_configs : Sequence [AppConfig ] | None , ** kwargs : Any ) -> list [Error ]:
8186 check_settings = OUTDATED_SETTINGS + ["CSP_REPORT_ONLY" , "CSP_EXCLUDE_URL_PREFIXES" , "CSP_REPORT_PERCENTAGE" ]
8287 if any (hasattr (settings , setting ) for setting in check_settings ):
@@ -91,3 +96,34 @@ def check_django_csp_lt_4_0(app_configs: Sequence[AppConfig] | None, **kwargs: A
9196 return [Error (warning , id = "csp.E001" )]
9297
9398 return []
99+
100+
101+ @register (checks .Tags .security )
102+ def check_exclude_url_prefixes_is_not_string (app_configs : Sequence [AppConfig ] | None , ** kwargs : Any ) -> list [Error ]:
103+ """
104+ Check that EXCLUDE_URL_PREFIXES in settings is not a string.
105+
106+ If it is a string it can lead to a security issue where the string is treated as a list of
107+ characters, resulting in '/' matching all paths excluding the CSP header from all responses.
108+
109+ """
110+ # Skip check for django-csp < 4.0.
111+ if Version (version ("django-csp" )) < Version ("4.0a1" ):
112+ return []
113+
114+ errors = []
115+ keys = (
116+ "CONTENT_SECURITY_POLICY" ,
117+ "CONTENT_SECURITY_POLICY_REPORT_ONLY" ,
118+ )
119+ for key in keys :
120+ config = getattr (settings , key , {})
121+ if isinstance (config , dict ) and isinstance (config .get ("EXCLUDE_URL_PREFIXES" ), str ):
122+ errors .append (
123+ Error (
124+ f"EXCLUDE_URL_PREFIXES in { key } settings must be a list or tuple." ,
125+ id = "csp.E002" ,
126+ )
127+ )
128+
129+ return errors
0 commit comments