Skip to content

Commit e9e4e27

Browse files
Rob Hudsonrobhudson
authored andcommitted
Fix #249: Add EXCLUDE_URL_PREFIXES check
1 parent 7748b9c commit e9e4e27

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

csp/apps.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from django.apps import AppConfig
2-
from django.core import checks
32

4-
from csp.checks import check_django_csp_lt_4_0
3+
from csp.checks import * # noqa: F403 (here to register the checks)
54

65

76
class CspConfig(AppConfig):
87
name = "csp"
9-
10-
def ready(self) -> None:
11-
checks.register(check_django_csp_lt_4_0, checks.Tags.security)

csp/checks.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import pprint
44
from collections.abc import Sequence
5+
from importlib.metadata import version
56
from typing import TYPE_CHECKING, Any
67

78
from 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

1014
from 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)
8085
def 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

csp/tests/test_checks.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.test.utils import override_settings
22

3-
from csp.checks import check_django_csp_lt_4_0, migrate_settings
3+
from csp.checks import check_django_csp_lt_4_0, check_exclude_url_prefixes_is_not_string, migrate_settings
44
from csp.constants import NONCE
55

66

@@ -50,3 +50,25 @@ def test_check_django_csp_lt_4_0() -> None:
5050

5151
def test_check_django_csp_lt_4_0_no_config() -> None:
5252
assert check_django_csp_lt_4_0(None) == []
53+
54+
55+
@override_settings(
56+
CONTENT_SECURITY_POLICY={"EXCLUDE_URL_PREFIXES": "/admin/"},
57+
)
58+
def test_check_exclude_url_prefixes_is_not_string() -> None:
59+
errors = check_exclude_url_prefixes_is_not_string(None)
60+
assert len(errors) == 1
61+
error = errors[0]
62+
assert error.id == "csp.E002"
63+
assert error.msg == "EXCLUDE_URL_PREFIXES in CONTENT_SECURITY_POLICY settings must be a list or tuple."
64+
65+
66+
@override_settings(
67+
CONTENT_SECURITY_POLICY_REPORT_ONLY={"EXCLUDE_URL_PREFIXES": "/admin/"},
68+
)
69+
def test_check_exclude_url_prefixes_ro_is_not_string() -> None:
70+
errors = check_exclude_url_prefixes_is_not_string(None)
71+
assert len(errors) == 1
72+
error = errors[0]
73+
assert error.id == "csp.E002"
74+
assert error.msg == "EXCLUDE_URL_PREFIXES in CONTENT_SECURITY_POLICY_REPORT_ONLY settings must be a list or tuple."

0 commit comments

Comments
 (0)