diff --git a/django/core/checks/security/csrf.py b/django/core/checks/security/csrf.py index 75c9813e7f..758ff299e4 100644 --- a/django/core/checks/security/csrf.py +++ b/django/core/checks/security/csrf.py @@ -1,6 +1,8 @@ +import inspect + from django.conf import settings -from .. import Tags, Warning, register +from .. import Error, Tags, Warning, register W003 = Warning( "You don't appear to be using Django's built-in " @@ -38,3 +40,28 @@ def check_csrf_cookie_secure(app_configs, **kwargs): settings.CSRF_COOKIE_SECURE ) return [] if passed_check else [W016] + + +@register(Tags.security) +def check_csrf_failure_view(app_configs, **kwargs): + from django.middleware.csrf import _get_failure_view + + errors = [] + try: + view = _get_failure_view() + except ImportError: + msg = ( + "The CSRF failure view '%s' could not be imported." % + settings.CSRF_FAILURE_VIEW + ) + errors.append(Error(msg, id='security.E025')) + else: + try: + inspect.signature(view).bind(None, reason=None) + except TypeError: + msg = ( + "The CSRF failure view '%s' does not take the correct number of arguments." % + settings.CSRF_FAILURE_VIEW + ) + errors.append(Error(msg, id='security.E024')) + return errors diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 6b66b18112..a31bea19ba 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -495,6 +495,10 @@ The following checks are run if you use the :option:`check --deploy` option: should consider enabling this header to protect user privacy. * **security.E023**: You have set the :setting:`SECURE_REFERRER_POLICY` setting to an invalid value. +* **security.E024**: The CSRF failure view ``'path.to.view'`` does not take the + correct number of arguments. +* **security.E025**: The CSRF failure view ``'path.to.view'`` could not be + imported. The following checks verify that your security-related settings are correctly configured: diff --git a/tests/check_framework/test_security.py b/tests/check_framework/test_security.py index 8225b99995..9bf9bb2a0a 100644 --- a/tests/check_framework/test_security.py +++ b/tests/check_framework/test_security.py @@ -1,4 +1,5 @@ from django.conf import settings +from django.core.checks.messages import Error from django.core.checks.security import base, csrf, sessions from django.core.management.utils import get_random_secret_key from django.test import SimpleTestCase @@ -471,3 +472,35 @@ class CheckReferrerPolicyTest(SimpleTestCase): ) def test_with_invalid_referrer_policy(self): self.assertEqual(base.check_referrer_policy(None), [base.E023]) + + +def failure_view_with_invalid_signature(): + pass + + +class CSRFFailureViewTest(SimpleTestCase): + @override_settings(CSRF_FAILURE_VIEW='') + def test_failure_view_import_error(self): + self.assertEqual( + csrf.check_csrf_failure_view(None), + [ + Error( + "The CSRF failure view '' could not be imported.", + id='security.E025', + ) + ], + ) + + @override_settings( + CSRF_FAILURE_VIEW=f'{__name__}.failure_view_with_invalid_signature', + ) + def test_failure_view_invalid_signature(self): + msg = ( + "The CSRF failure view " + "'check_framework.test_security.failure_view_with_invalid_signature' " + "does not take the correct number of arguments." + ) + self.assertEqual( + csrf.check_csrf_failure_view(None), + [Error(msg, id='security.E024')], + )