From d63241ebc7067fdebbaf704989b34fcd8f26bbe9 Mon Sep 17 00:00:00 2001 From: Rob Hudson Date: Sat, 3 May 2025 10:01:58 -0700 Subject: 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> --- tests/check_framework/test_security.py | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'tests/check_framework/test_security.py') diff --git a/tests/check_framework/test_security.py b/tests/check_framework/test_security.py index cb035a90a4..db21f13ea2 100644 --- a/tests/check_framework/test_security.py +++ b/tests/check_framework/test_security.py @@ -1,3 +1,5 @@ +import itertools + from django.conf import settings from django.core.checks.messages import Error, Warning from django.core.checks.security import base, csrf, sessions @@ -678,3 +680,54 @@ class CheckCrossOriginOpenerPolicyTest(SimpleTestCase): ) def test_with_invalid_coop(self): self.assertEqual(base.check_cross_origin_opener_policy(None), [base.E024]) + + +class CheckSecureCSPTests(SimpleTestCase): + """Tests for the CSP settings check function.""" + + def test_secure_csp_allowed_values(self): + """Check should pass when both CSP settings are None or dicts.""" + allowed_values = (None, {}, {"key": "value"}) + combinations = itertools.product(allowed_values, repeat=2) + for csp_value, csp_report_only_value in combinations: + with ( + self.subTest( + csp_value=csp_value, csp_report_only_value=csp_report_only_value + ), + self.settings( + SECURE_CSP=csp_value, SECURE_CSP_REPORT_ONLY=csp_report_only_value + ), + ): + errors = base.check_csp_settings(None) + self.assertEqual(errors, []) + + def test_secure_csp_invalid_values(self): + """Check should fail when either CSP setting is not a dict.""" + for value in ( + False, + True, + 0, + 42, + "", + "not-a-dict", + set(), + {"a", "b"}, + [], + [1, 2, 3, 4], + ): + with self.subTest(value=value): + csp_error = Error( + base.E026.msg % ("SECURE_CSP", value), id=base.E026.id + ) + with self.settings(SECURE_CSP=value): + errors = base.check_csp_settings(None) + self.assertEqual(errors, [csp_error]) + csp_report_only_error = Error( + base.E026.msg % ("SECURE_CSP_REPORT_ONLY", value), id=base.E026.id + ) + with self.settings(SECURE_CSP_REPORT_ONLY=value): + errors = base.check_csp_settings(None) + self.assertEqual(errors, [csp_report_only_error]) + with self.settings(SECURE_CSP=value, SECURE_CSP_REPORT_ONLY=value): + errors = base.check_csp_settings(None) + self.assertEqual(errors, [csp_error, csp_report_only_error]) -- cgit v1.3