diff options
| author | Rob Hudson <rob@cogit8.org> | 2025-05-03 10:01:58 -0700 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2025-06-27 15:57:02 -0300 |
| commit | d63241ebc7067fdebbaf704989b34fcd8f26bbe9 (patch) | |
| tree | 07b5a5cb0c70c446f5f0fb9ad2834501fc3d6544 /tests/context_processors | |
| parent | 3f59711581bd22ebd0f13fb040b15b69c0eee21f (diff) | |
Fixed #15727 -- Added Content Security Policy (CSP) support.
This initial work adds a pair of settings to configure specific CSP
directives for enforcing or reporting policy violations, a new
`django.middleware.csp.ContentSecurityPolicyMiddleware` to apply the
appropriate headers to responses, and a context processor to support CSP
nonces in templates for safely inlining assets.
Relevant documentation has been added for the 6.0 release notes,
security overview, a new how-to page, and a dedicated reference section.
Thanks to the multiple reviewers for their precise and valuable feedback.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Diffstat (limited to 'tests/context_processors')
| -rw-r--r-- | tests/context_processors/templates/context_processors/csp_nonce.html | 17 | ||||
| -rw-r--r-- | tests/context_processors/tests.py | 65 | ||||
| -rw-r--r-- | tests/context_processors/urls.py | 1 | ||||
| -rw-r--r-- | tests/context_processors/views.py | 4 |
4 files changed, 86 insertions, 1 deletions
diff --git a/tests/context_processors/templates/context_processors/csp_nonce.html b/tests/context_processors/templates/context_processors/csp_nonce.html new file mode 100644 index 0000000000..13612e3840 --- /dev/null +++ b/tests/context_processors/templates/context_processors/csp_nonce.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>CSP Nonce Test</title> +</head> +<body> + <h1>CSP Nonce Test</h1> + <p>CSP Nonce is present: {{ csp_nonce }}</p> + <script nonce="{{ csp_nonce }}"> + console.log("This script is allowed to run due to the nonce."); + </script> + <script> + console.log("This script might be blocked by CSP if a nonce is required."); + </script> +</body> +</html> diff --git a/tests/context_processors/tests.py b/tests/context_processors/tests.py index ba92ff8b05..737ff3e1cf 100644 --- a/tests/context_processors/tests.py +++ b/tests/context_processors/tests.py @@ -2,7 +2,8 @@ Tests for Django's bundled context processors. """ -from django.test import SimpleTestCase, TestCase, override_settings +from django.test import SimpleTestCase, TestCase, modify_settings, override_settings +from django.utils.csp import CSP @override_settings( @@ -96,3 +97,65 @@ class DebugContextProcessorTests(TestCase): self.assertContains(response, "Third query list: 2") # Check queries for DB connection 'other' self.assertContains(response, "Fourth query list: 3") + + +@override_settings( + ROOT_URLCONF="context_processors.urls", + TEMPLATES=[ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.csp", + ], + }, + } + ], + MIDDLEWARE=[ + "django.middleware.csp.ContentSecurityPolicyMiddleware", + ], + SECURE_CSP={ + "script-src": [CSP.SELF, CSP.NONCE], + }, +) +class CSPContextProcessorTests(TestCase): + """ + Tests for the django.template.context_processors.csp_nonce processor. + """ + + def test_csp_nonce_in_context(self): + response = self.client.get("/csp_nonce/") + self.assertIn("csp_nonce", response.context) + + @modify_settings( + MIDDLEWARE={"remove": "django.middleware.csp.ContentSecurityPolicyMiddleware"} + ) + def test_csp_nonce_in_context_no_middleware(self): + response = self.client.get("/csp_nonce/") + self.assertIn("csp_nonce", response.context) + + def test_csp_nonce_in_header(self): + response = self.client.get("/csp_nonce/") + self.assertIn(CSP.HEADER_ENFORCE, response.headers) + csp_header = response.headers[CSP.HEADER_ENFORCE] + nonce = response.context["csp_nonce"] + self.assertIn(f"'nonce-{nonce}'", csp_header) + + def test_different_nonce_per_request(self): + response1 = self.client.get("/csp_nonce/") + response2 = self.client.get("/csp_nonce/") + self.assertNotEqual( + response1.context["csp_nonce"], + response2.context["csp_nonce"], + ) + + def test_csp_nonce_in_template(self): + response = self.client.get("/csp_nonce/") + nonce = response.context["csp_nonce"] + self.assertIn(f'<script nonce="{nonce}">', response.text) + + def test_csp_nonce_length(self): + response = self.client.get("/csp_nonce/") + nonce = response.context["csp_nonce"] + self.assertEqual(len(nonce), 22) # Based on secrets.token_urlsafe of 16 bytes. diff --git a/tests/context_processors/urls.py b/tests/context_processors/urls.py index 4ca298c922..f3ee496907 100644 --- a/tests/context_processors/urls.py +++ b/tests/context_processors/urls.py @@ -5,4 +5,5 @@ from . import views urlpatterns = [ path("request_attrs/", views.request_processor), path("debug/", views.debug_processor), + path("csp_nonce/", views.csp_nonce_processor), ] diff --git a/tests/context_processors/views.py b/tests/context_processors/views.py index 81710c90eb..a78acffb87 100644 --- a/tests/context_processors/views.py +++ b/tests/context_processors/views.py @@ -13,3 +13,7 @@ def debug_processor(request): "other_debug_objects": DebugObject.objects.using("other"), } return render(request, "context_processors/debug.html", context) + + +def csp_nonce_processor(request): + return render(request, "context_processors/csp_nonce.html") |
