summaryrefslogtreecommitdiff
path: root/tests/context_processors
diff options
context:
space:
mode:
authorRob Hudson <rob@cogit8.org>2025-05-03 10:01:58 -0700
committernessita <124304+nessita@users.noreply.github.com>2025-06-27 15:57:02 -0300
commitd63241ebc7067fdebbaf704989b34fcd8f26bbe9 (patch)
tree07b5a5cb0c70c446f5f0fb9ad2834501fc3d6544 /tests/context_processors
parent3f59711581bd22ebd0f13fb040b15b69c0eee21f (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.html17
-rw-r--r--tests/context_processors/tests.py65
-rw-r--r--tests/context_processors/urls.py1
-rw-r--r--tests/context_processors/views.py4
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")