diff --git a/django/contrib/auth/checks.py b/django/contrib/auth/checks.py index cf23c51746c5..0de4cf49f1a0 100644 --- a/django/contrib/auth/checks.py +++ b/django/contrib/auth/checks.py @@ -1,5 +1,4 @@ from itertools import chain -from types import MethodType from django.apps import apps from django.conf import settings @@ -98,7 +97,7 @@ def check_user_model(app_configs, **kwargs): ) ) - if isinstance(cls().is_anonymous, MethodType): + if callable(cls().is_anonymous): errors.append( checks.Critical( "%s.is_anonymous must be an attribute or property rather than " @@ -108,7 +107,7 @@ def check_user_model(app_configs, **kwargs): id="auth.C009", ) ) - if isinstance(cls().is_authenticated, MethodType): + if callable(cls().is_authenticated): errors.append( checks.Critical( "%s.is_authenticated must be an attribute or property rather " diff --git a/django/utils/html.py b/django/utils/html.py index 734d7fbfb3d0..b04d6bc5eef6 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -297,6 +297,7 @@ class Urlizer: simple_url_re = _lazy_re_compile(r"^https?://\[?\w", re.IGNORECASE) simple_url_2_re = _lazy_re_compile( rf"^www\.|^(?!http)(?:{DomainNameValidator.hostname_re})" + rf"(?:{DomainNameValidator.domain_re})" r"\.(com|edu|gov|int|mil|net|org)($|/.*)$", re.IGNORECASE, ) diff --git a/tests/auth_tests/test_checks.py b/tests/auth_tests/test_checks.py index 3d70451e9d1c..19e9faddedf8 100644 --- a/tests/auth_tests/test_checks.py +++ b/tests/auth_tests/test_checks.py @@ -206,6 +206,45 @@ def is_authenticated(self): ], ) + @override_settings(AUTH_USER_MODEL="auth_tests.VulnerableStaticUser") + def test_is_anonymous_authenticated_static_methods(self): + """ + .is_anonymous/is_authenticated must not be static methods. + """ + + class VulnerableStaticUser(AbstractBaseUser): + username = models.CharField(max_length=30, unique=True) + USERNAME_FIELD = "username" + + @staticmethod + def is_anonymous(): + return False + + @staticmethod + def is_authenticated(): + return False + + errors = checks.run_checks(app_configs=self.apps.get_app_configs()) + self.assertEqual( + errors, + [ + checks.Critical( + "%s.is_anonymous must be an attribute or property rather than " + "a method. Ignoring this is a security issue as anonymous " + "users will be treated as authenticated!" % VulnerableStaticUser, + obj=VulnerableStaticUser, + id="auth.C009", + ), + checks.Critical( + "%s.is_authenticated must be an attribute or property rather " + "than a method. Ignoring this is a security issue as anonymous " + "users will be treated as authenticated!" % VulnerableStaticUser, + obj=VulnerableStaticUser, + id="auth.C010", + ), + ], + ) + @isolate_apps("auth_tests", attr_name="apps") @override_system_checks([check_models_permissions]) diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index bf00d1449678..b388d3ce52a3 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -455,6 +455,10 @@ def test_urlize(self): 'idna-2008@މިހާރު.example.mv', ), + ( + "host.djangoproject.com", + 'host.djangoproject.com', + ), ) for value, output in tests: with self.subTest(value=value):