login page
This commit is contained in:
25
Lib/site-packages/django/core/checks/__init__.py
Normal file
25
Lib/site-packages/django/core/checks/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from .messages import (
|
||||
CRITICAL, DEBUG, ERROR, INFO, WARNING, CheckMessage, Critical, Debug,
|
||||
Error, Info, Warning,
|
||||
)
|
||||
from .registry import Tags, register, run_checks, tag_exists
|
||||
|
||||
# Import these to force registration of checks
|
||||
import django.core.checks.async_checks # NOQA isort:skip
|
||||
import django.core.checks.caches # NOQA isort:skip
|
||||
import django.core.checks.database # NOQA isort:skip
|
||||
import django.core.checks.model_checks # NOQA isort:skip
|
||||
import django.core.checks.security.base # NOQA isort:skip
|
||||
import django.core.checks.security.csrf # NOQA isort:skip
|
||||
import django.core.checks.security.sessions # NOQA isort:skip
|
||||
import django.core.checks.templates # NOQA isort:skip
|
||||
import django.core.checks.translation # NOQA isort:skip
|
||||
import django.core.checks.urls # NOQA isort:skip
|
||||
|
||||
|
||||
__all__ = [
|
||||
'CheckMessage',
|
||||
'Debug', 'Info', 'Warning', 'Error', 'Critical',
|
||||
'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL',
|
||||
'register', 'run_checks', 'tag_exists', 'Tags',
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
16
Lib/site-packages/django/core/checks/async_checks.py
Normal file
16
Lib/site-packages/django/core/checks/async_checks.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
|
||||
from . import Error, Tags, register
|
||||
|
||||
E001 = Error(
|
||||
'You should not set the DJANGO_ALLOW_ASYNC_UNSAFE environment variable in '
|
||||
'deployment. This disables async safety protection.',
|
||||
id='async.E001',
|
||||
)
|
||||
|
||||
|
||||
@register(Tags.async_support, deploy=True)
|
||||
def check_async_unsafe(app_configs, **kwargs):
|
||||
if os.environ.get('DJANGO_ALLOW_ASYNC_UNSAFE'):
|
||||
return [E001]
|
||||
return []
|
||||
16
Lib/site-packages/django/core/checks/caches.py
Normal file
16
Lib/site-packages/django/core/checks/caches.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.conf import settings
|
||||
from django.core.cache import DEFAULT_CACHE_ALIAS
|
||||
|
||||
from . import Error, Tags, register
|
||||
|
||||
E001 = Error(
|
||||
"You must define a '%s' cache in your CACHES setting." % DEFAULT_CACHE_ALIAS,
|
||||
id='caches.E001',
|
||||
)
|
||||
|
||||
|
||||
@register(Tags.caches)
|
||||
def check_default_cache_is_configured(app_configs, **kwargs):
|
||||
if DEFAULT_CACHE_ALIAS not in settings.CACHES:
|
||||
return [E001]
|
||||
return []
|
||||
Binary file not shown.
14
Lib/site-packages/django/core/checks/database.py
Normal file
14
Lib/site-packages/django/core/checks/database.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.db import connections
|
||||
|
||||
from . import Tags, register
|
||||
|
||||
|
||||
@register(Tags.database)
|
||||
def check_database_backends(databases=None, **kwargs):
|
||||
if databases is None:
|
||||
return []
|
||||
issues = []
|
||||
for alias in databases:
|
||||
conn = connections[alias]
|
||||
issues.extend(conn.validation.check(**kwargs))
|
||||
return issues
|
||||
75
Lib/site-packages/django/core/checks/messages.py
Normal file
75
Lib/site-packages/django/core/checks/messages.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# Levels
|
||||
DEBUG = 10
|
||||
INFO = 20
|
||||
WARNING = 30
|
||||
ERROR = 40
|
||||
CRITICAL = 50
|
||||
|
||||
|
||||
class CheckMessage:
|
||||
|
||||
def __init__(self, level, msg, hint=None, obj=None, id=None):
|
||||
assert isinstance(level, int), "The first argument should be level."
|
||||
self.level = level
|
||||
self.msg = msg
|
||||
self.hint = hint
|
||||
self.obj = obj
|
||||
self.id = id
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
isinstance(other, self.__class__) and
|
||||
all(getattr(self, attr) == getattr(other, attr)
|
||||
for attr in ['level', 'msg', 'hint', 'obj', 'id'])
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
from django.db import models
|
||||
|
||||
if self.obj is None:
|
||||
obj = "?"
|
||||
elif isinstance(self.obj, models.base.ModelBase):
|
||||
# We need to hardcode ModelBase and Field cases because its __str__
|
||||
# method doesn't return "applabel.modellabel" and cannot be changed.
|
||||
obj = self.obj._meta.label
|
||||
else:
|
||||
obj = str(self.obj)
|
||||
id = "(%s) " % self.id if self.id else ""
|
||||
hint = "\n\tHINT: %s" % self.hint if self.hint else ''
|
||||
return "%s: %s%s%s" % (obj, id, self.msg, hint)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: level=%r, msg=%r, hint=%r, obj=%r, id=%r>" % \
|
||||
(self.__class__.__name__, self.level, self.msg, self.hint, self.obj, self.id)
|
||||
|
||||
def is_serious(self, level=ERROR):
|
||||
return self.level >= level
|
||||
|
||||
def is_silenced(self):
|
||||
from django.conf import settings
|
||||
return self.id in settings.SILENCED_SYSTEM_CHECKS
|
||||
|
||||
|
||||
class Debug(CheckMessage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(DEBUG, *args, **kwargs)
|
||||
|
||||
|
||||
class Info(CheckMessage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(INFO, *args, **kwargs)
|
||||
|
||||
|
||||
class Warning(CheckMessage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(WARNING, *args, **kwargs)
|
||||
|
||||
|
||||
class Error(CheckMessage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(ERROR, *args, **kwargs)
|
||||
|
||||
|
||||
class Critical(CheckMessage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(CRITICAL, *args, **kwargs)
|
||||
210
Lib/site-packages/django/core/checks/model_checks.py
Normal file
210
Lib/site-packages/django/core/checks/model_checks.py
Normal file
@@ -0,0 +1,210 @@
|
||||
import inspect
|
||||
import types
|
||||
from collections import defaultdict
|
||||
from itertools import chain
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core.checks import Error, Tags, Warning, register
|
||||
|
||||
|
||||
@register(Tags.models)
|
||||
def check_all_models(app_configs=None, **kwargs):
|
||||
db_table_models = defaultdict(list)
|
||||
indexes = defaultdict(list)
|
||||
constraints = defaultdict(list)
|
||||
errors = []
|
||||
if app_configs is None:
|
||||
models = apps.get_models()
|
||||
else:
|
||||
models = chain.from_iterable(app_config.get_models() for app_config in app_configs)
|
||||
for model in models:
|
||||
if model._meta.managed and not model._meta.proxy:
|
||||
db_table_models[model._meta.db_table].append(model._meta.label)
|
||||
if not inspect.ismethod(model.check):
|
||||
errors.append(
|
||||
Error(
|
||||
"The '%s.check()' class method is currently overridden by %r."
|
||||
% (model.__name__, model.check),
|
||||
obj=model,
|
||||
id='models.E020'
|
||||
)
|
||||
)
|
||||
else:
|
||||
errors.extend(model.check(**kwargs))
|
||||
for model_index in model._meta.indexes:
|
||||
indexes[model_index.name].append(model._meta.label)
|
||||
for model_constraint in model._meta.constraints:
|
||||
constraints[model_constraint.name].append(model._meta.label)
|
||||
if settings.DATABASE_ROUTERS:
|
||||
error_class, error_id = Warning, 'models.W035'
|
||||
error_hint = (
|
||||
'You have configured settings.DATABASE_ROUTERS. Verify that %s '
|
||||
'are correctly routed to separate databases.'
|
||||
)
|
||||
else:
|
||||
error_class, error_id = Error, 'models.E028'
|
||||
error_hint = None
|
||||
for db_table, model_labels in db_table_models.items():
|
||||
if len(model_labels) != 1:
|
||||
model_labels_str = ', '.join(model_labels)
|
||||
errors.append(
|
||||
error_class(
|
||||
"db_table '%s' is used by multiple models: %s."
|
||||
% (db_table, model_labels_str),
|
||||
obj=db_table,
|
||||
hint=(error_hint % model_labels_str) if error_hint else None,
|
||||
id=error_id,
|
||||
)
|
||||
)
|
||||
for index_name, model_labels in indexes.items():
|
||||
if len(model_labels) > 1:
|
||||
model_labels = set(model_labels)
|
||||
errors.append(
|
||||
Error(
|
||||
"index name '%s' is not unique %s %s." % (
|
||||
index_name,
|
||||
'for model' if len(model_labels) == 1 else 'amongst models:',
|
||||
', '.join(sorted(model_labels)),
|
||||
),
|
||||
id='models.E029' if len(model_labels) == 1 else 'models.E030',
|
||||
),
|
||||
)
|
||||
for constraint_name, model_labels in constraints.items():
|
||||
if len(model_labels) > 1:
|
||||
model_labels = set(model_labels)
|
||||
errors.append(
|
||||
Error(
|
||||
"constraint name '%s' is not unique %s %s." % (
|
||||
constraint_name,
|
||||
'for model' if len(model_labels) == 1 else 'amongst models:',
|
||||
', '.join(sorted(model_labels)),
|
||||
),
|
||||
id='models.E031' if len(model_labels) == 1 else 'models.E032',
|
||||
),
|
||||
)
|
||||
return errors
|
||||
|
||||
|
||||
def _check_lazy_references(apps, ignore=None):
|
||||
"""
|
||||
Ensure all lazy (i.e. string) model references have been resolved.
|
||||
|
||||
Lazy references are used in various places throughout Django, primarily in
|
||||
related fields and model signals. Identify those common cases and provide
|
||||
more helpful error messages for them.
|
||||
|
||||
The ignore parameter is used by StateApps to exclude swappable models from
|
||||
this check.
|
||||
"""
|
||||
pending_models = set(apps._pending_operations) - (ignore or set())
|
||||
|
||||
# Short circuit if there aren't any errors.
|
||||
if not pending_models:
|
||||
return []
|
||||
|
||||
from django.db.models import signals
|
||||
model_signals = {
|
||||
signal: name for name, signal in vars(signals).items()
|
||||
if isinstance(signal, signals.ModelSignal)
|
||||
}
|
||||
|
||||
def extract_operation(obj):
|
||||
"""
|
||||
Take a callable found in Apps._pending_operations and identify the
|
||||
original callable passed to Apps.lazy_model_operation(). If that
|
||||
callable was a partial, return the inner, non-partial function and
|
||||
any arguments and keyword arguments that were supplied with it.
|
||||
|
||||
obj is a callback defined locally in Apps.lazy_model_operation() and
|
||||
annotated there with a `func` attribute so as to imitate a partial.
|
||||
"""
|
||||
operation, args, keywords = obj, [], {}
|
||||
while hasattr(operation, 'func'):
|
||||
args.extend(getattr(operation, 'args', []))
|
||||
keywords.update(getattr(operation, 'keywords', {}))
|
||||
operation = operation.func
|
||||
return operation, args, keywords
|
||||
|
||||
def app_model_error(model_key):
|
||||
try:
|
||||
apps.get_app_config(model_key[0])
|
||||
model_error = "app '%s' doesn't provide model '%s'" % model_key
|
||||
except LookupError:
|
||||
model_error = "app '%s' isn't installed" % model_key[0]
|
||||
return model_error
|
||||
|
||||
# Here are several functions which return CheckMessage instances for the
|
||||
# most common usages of lazy operations throughout Django. These functions
|
||||
# take the model that was being waited on as an (app_label, modelname)
|
||||
# pair, the original lazy function, and its positional and keyword args as
|
||||
# determined by extract_operation().
|
||||
|
||||
def field_error(model_key, func, args, keywords):
|
||||
error_msg = (
|
||||
"The field %(field)s was declared with a lazy reference "
|
||||
"to '%(model)s', but %(model_error)s."
|
||||
)
|
||||
params = {
|
||||
'model': '.'.join(model_key),
|
||||
'field': keywords['field'],
|
||||
'model_error': app_model_error(model_key),
|
||||
}
|
||||
return Error(error_msg % params, obj=keywords['field'], id='fields.E307')
|
||||
|
||||
def signal_connect_error(model_key, func, args, keywords):
|
||||
error_msg = (
|
||||
"%(receiver)s was connected to the '%(signal)s' signal with a "
|
||||
"lazy reference to the sender '%(model)s', but %(model_error)s."
|
||||
)
|
||||
receiver = args[0]
|
||||
# The receiver is either a function or an instance of class
|
||||
# defining a `__call__` method.
|
||||
if isinstance(receiver, types.FunctionType):
|
||||
description = "The function '%s'" % receiver.__name__
|
||||
elif isinstance(receiver, types.MethodType):
|
||||
description = "Bound method '%s.%s'" % (receiver.__self__.__class__.__name__, receiver.__name__)
|
||||
else:
|
||||
description = "An instance of class '%s'" % receiver.__class__.__name__
|
||||
signal_name = model_signals.get(func.__self__, 'unknown')
|
||||
params = {
|
||||
'model': '.'.join(model_key),
|
||||
'receiver': description,
|
||||
'signal': signal_name,
|
||||
'model_error': app_model_error(model_key),
|
||||
}
|
||||
return Error(error_msg % params, obj=receiver.__module__, id='signals.E001')
|
||||
|
||||
def default_error(model_key, func, args, keywords):
|
||||
error_msg = "%(op)s contains a lazy reference to %(model)s, but %(model_error)s."
|
||||
params = {
|
||||
'op': func,
|
||||
'model': '.'.join(model_key),
|
||||
'model_error': app_model_error(model_key),
|
||||
}
|
||||
return Error(error_msg % params, obj=func, id='models.E022')
|
||||
|
||||
# Maps common uses of lazy operations to corresponding error functions
|
||||
# defined above. If a key maps to None, no error will be produced.
|
||||
# default_error() will be used for usages that don't appear in this dict.
|
||||
known_lazy = {
|
||||
('django.db.models.fields.related', 'resolve_related_class'): field_error,
|
||||
('django.db.models.fields.related', 'set_managed'): None,
|
||||
('django.dispatch.dispatcher', 'connect'): signal_connect_error,
|
||||
}
|
||||
|
||||
def build_error(model_key, func, args, keywords):
|
||||
key = (func.__module__, func.__name__)
|
||||
error_fn = known_lazy.get(key, default_error)
|
||||
return error_fn(model_key, func, args, keywords) if error_fn else None
|
||||
|
||||
return sorted(filter(None, (
|
||||
build_error(model_key, *extract_operation(func))
|
||||
for model_key in pending_models
|
||||
for func in apps._pending_operations[model_key]
|
||||
)), key=lambda error: error.msg)
|
||||
|
||||
|
||||
@register(Tags.models)
|
||||
def check_lazy_references(app_configs=None, **kwargs):
|
||||
return _check_lazy_references(apps)
|
||||
95
Lib/site-packages/django/core/checks/registry.py
Normal file
95
Lib/site-packages/django/core/checks/registry.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from itertools import chain
|
||||
|
||||
from django.utils.itercompat import is_iterable
|
||||
|
||||
|
||||
class Tags:
|
||||
"""
|
||||
Built-in tags for internal checks.
|
||||
"""
|
||||
admin = 'admin'
|
||||
async_support = 'async_support'
|
||||
caches = 'caches'
|
||||
compatibility = 'compatibility'
|
||||
database = 'database'
|
||||
models = 'models'
|
||||
security = 'security'
|
||||
signals = 'signals'
|
||||
staticfiles = 'staticfiles'
|
||||
templates = 'templates'
|
||||
translation = 'translation'
|
||||
urls = 'urls'
|
||||
|
||||
|
||||
class CheckRegistry:
|
||||
|
||||
def __init__(self):
|
||||
self.registered_checks = set()
|
||||
self.deployment_checks = set()
|
||||
|
||||
def register(self, check=None, *tags, **kwargs):
|
||||
"""
|
||||
Can be used as a function or a decorator. Register given function
|
||||
`f` labeled with given `tags`. The function should receive **kwargs
|
||||
and return list of Errors and Warnings.
|
||||
|
||||
Example::
|
||||
|
||||
registry = CheckRegistry()
|
||||
@registry.register('mytag', 'anothertag')
|
||||
def my_check(apps, **kwargs):
|
||||
# ... perform checks and collect `errors` ...
|
||||
return errors
|
||||
# or
|
||||
registry.register(my_check, 'mytag', 'anothertag')
|
||||
"""
|
||||
def inner(check):
|
||||
check.tags = tags
|
||||
checks = self.deployment_checks if kwargs.get('deploy') else self.registered_checks
|
||||
checks.add(check)
|
||||
return check
|
||||
|
||||
if callable(check):
|
||||
return inner(check)
|
||||
else:
|
||||
if check:
|
||||
tags += (check,)
|
||||
return inner
|
||||
|
||||
def run_checks(self, app_configs=None, tags=None, include_deployment_checks=False, databases=None):
|
||||
"""
|
||||
Run all registered checks and return list of Errors and Warnings.
|
||||
"""
|
||||
errors = []
|
||||
checks = self.get_checks(include_deployment_checks)
|
||||
|
||||
if tags is not None:
|
||||
checks = [check for check in checks if not set(check.tags).isdisjoint(tags)]
|
||||
|
||||
for check in checks:
|
||||
new_errors = check(app_configs=app_configs, databases=databases)
|
||||
assert is_iterable(new_errors), (
|
||||
"The function %r did not return a list. All functions registered "
|
||||
"with the checks registry must return a list." % check)
|
||||
errors.extend(new_errors)
|
||||
return errors
|
||||
|
||||
def tag_exists(self, tag, include_deployment_checks=False):
|
||||
return tag in self.tags_available(include_deployment_checks)
|
||||
|
||||
def tags_available(self, deployment_checks=False):
|
||||
return set(chain.from_iterable(
|
||||
check.tags for check in self.get_checks(deployment_checks)
|
||||
))
|
||||
|
||||
def get_checks(self, include_deployment_checks=False):
|
||||
checks = list(self.registered_checks)
|
||||
if include_deployment_checks:
|
||||
checks.extend(self.deployment_checks)
|
||||
return checks
|
||||
|
||||
|
||||
registry = CheckRegistry()
|
||||
register = registry.register
|
||||
run_checks = registry.run_checks
|
||||
tag_exists = registry.tag_exists
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
238
Lib/site-packages/django/core/checks/security/base.py
Normal file
238
Lib/site-packages/django/core/checks/security/base.py
Normal file
@@ -0,0 +1,238 @@
|
||||
from django.conf import settings
|
||||
|
||||
from .. import Error, Tags, Warning, register
|
||||
|
||||
REFERRER_POLICY_VALUES = {
|
||||
'no-referrer', 'no-referrer-when-downgrade', 'origin',
|
||||
'origin-when-cross-origin', 'same-origin', 'strict-origin',
|
||||
'strict-origin-when-cross-origin', 'unsafe-url',
|
||||
}
|
||||
|
||||
SECRET_KEY_MIN_LENGTH = 50
|
||||
SECRET_KEY_MIN_UNIQUE_CHARACTERS = 5
|
||||
|
||||
W001 = Warning(
|
||||
"You do not have 'django.middleware.security.SecurityMiddleware' "
|
||||
"in your MIDDLEWARE so the SECURE_HSTS_SECONDS, "
|
||||
"SECURE_CONTENT_TYPE_NOSNIFF, SECURE_BROWSER_XSS_FILTER, "
|
||||
"SECURE_REFERRER_POLICY, and SECURE_SSL_REDIRECT settings will have no "
|
||||
"effect.",
|
||||
id='security.W001',
|
||||
)
|
||||
|
||||
W002 = Warning(
|
||||
"You do not have "
|
||||
"'django.middleware.clickjacking.XFrameOptionsMiddleware' in your "
|
||||
"MIDDLEWARE, so your pages will not be served with an "
|
||||
"'x-frame-options' header. Unless there is a good reason for your "
|
||||
"site to be served in a frame, you should consider enabling this "
|
||||
"header to help prevent clickjacking attacks.",
|
||||
id='security.W002',
|
||||
)
|
||||
|
||||
W004 = Warning(
|
||||
"You have not set a value for the SECURE_HSTS_SECONDS setting. "
|
||||
"If your entire site is served only over SSL, you may want to consider "
|
||||
"setting a value and enabling HTTP Strict Transport Security. "
|
||||
"Be sure to read the documentation first; enabling HSTS carelessly "
|
||||
"can cause serious, irreversible problems.",
|
||||
id='security.W004',
|
||||
)
|
||||
|
||||
W005 = Warning(
|
||||
"You have not set the SECURE_HSTS_INCLUDE_SUBDOMAINS setting to True. "
|
||||
"Without this, your site is potentially vulnerable to attack "
|
||||
"via an insecure connection to a subdomain. Only set this to True if "
|
||||
"you are certain that all subdomains of your domain should be served "
|
||||
"exclusively via SSL.",
|
||||
id='security.W005',
|
||||
)
|
||||
|
||||
W006 = Warning(
|
||||
"Your SECURE_CONTENT_TYPE_NOSNIFF setting is not set to True, "
|
||||
"so your pages will not be served with an "
|
||||
"'X-Content-Type-Options: nosniff' header. "
|
||||
"You should consider enabling this header to prevent the "
|
||||
"browser from identifying content types incorrectly.",
|
||||
id='security.W006',
|
||||
)
|
||||
|
||||
W008 = Warning(
|
||||
"Your SECURE_SSL_REDIRECT setting is not set to True. "
|
||||
"Unless your site should be available over both SSL and non-SSL "
|
||||
"connections, you may want to either set this setting True "
|
||||
"or configure a load balancer or reverse-proxy server "
|
||||
"to redirect all connections to HTTPS.",
|
||||
id='security.W008',
|
||||
)
|
||||
|
||||
W009 = Warning(
|
||||
"Your SECRET_KEY has less than %(min_length)s characters or less than "
|
||||
"%(min_unique_chars)s unique characters. Please generate a long and random "
|
||||
"SECRET_KEY, otherwise many of Django's security-critical features will be "
|
||||
"vulnerable to attack." % {
|
||||
'min_length': SECRET_KEY_MIN_LENGTH,
|
||||
'min_unique_chars': SECRET_KEY_MIN_UNIQUE_CHARACTERS,
|
||||
},
|
||||
id='security.W009',
|
||||
)
|
||||
|
||||
W018 = Warning(
|
||||
"You should not have DEBUG set to True in deployment.",
|
||||
id='security.W018',
|
||||
)
|
||||
|
||||
W019 = Warning(
|
||||
"You have "
|
||||
"'django.middleware.clickjacking.XFrameOptionsMiddleware' in your "
|
||||
"MIDDLEWARE, but X_FRAME_OPTIONS is not set to 'DENY'. "
|
||||
"Unless there is a good reason for your site to serve other parts of "
|
||||
"itself in a frame, you should change it to 'DENY'.",
|
||||
id='security.W019',
|
||||
)
|
||||
|
||||
W020 = Warning(
|
||||
"ALLOWED_HOSTS must not be empty in deployment.",
|
||||
id='security.W020',
|
||||
)
|
||||
|
||||
W021 = Warning(
|
||||
"You have not set the SECURE_HSTS_PRELOAD setting to True. Without this, "
|
||||
"your site cannot be submitted to the browser preload list.",
|
||||
id='security.W021',
|
||||
)
|
||||
|
||||
W022 = Warning(
|
||||
'You have not set the SECURE_REFERRER_POLICY setting. Without this, your '
|
||||
'site will not send a Referrer-Policy header. You should consider '
|
||||
'enabling this header to protect user privacy.',
|
||||
id='security.W022',
|
||||
)
|
||||
|
||||
E023 = Error(
|
||||
'You have set the SECURE_REFERRER_POLICY setting to an invalid value.',
|
||||
hint='Valid values are: {}.'.format(', '.join(sorted(REFERRER_POLICY_VALUES))),
|
||||
id='security.E023',
|
||||
)
|
||||
|
||||
E100 = Error(
|
||||
"DEFAULT_HASHING_ALGORITHM must be 'sha1' or 'sha256'.",
|
||||
id='security.E100',
|
||||
)
|
||||
|
||||
|
||||
def _security_middleware():
|
||||
return 'django.middleware.security.SecurityMiddleware' in settings.MIDDLEWARE
|
||||
|
||||
|
||||
def _xframe_middleware():
|
||||
return 'django.middleware.clickjacking.XFrameOptionsMiddleware' in settings.MIDDLEWARE
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_security_middleware(app_configs, **kwargs):
|
||||
passed_check = _security_middleware()
|
||||
return [] if passed_check else [W001]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_xframe_options_middleware(app_configs, **kwargs):
|
||||
passed_check = _xframe_middleware()
|
||||
return [] if passed_check else [W002]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_sts(app_configs, **kwargs):
|
||||
passed_check = not _security_middleware() or settings.SECURE_HSTS_SECONDS
|
||||
return [] if passed_check else [W004]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_sts_include_subdomains(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
not _security_middleware() or
|
||||
not settings.SECURE_HSTS_SECONDS or
|
||||
settings.SECURE_HSTS_INCLUDE_SUBDOMAINS is True
|
||||
)
|
||||
return [] if passed_check else [W005]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_sts_preload(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
not _security_middleware() or
|
||||
not settings.SECURE_HSTS_SECONDS or
|
||||
settings.SECURE_HSTS_PRELOAD is True
|
||||
)
|
||||
return [] if passed_check else [W021]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_content_type_nosniff(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
not _security_middleware() or
|
||||
settings.SECURE_CONTENT_TYPE_NOSNIFF is True
|
||||
)
|
||||
return [] if passed_check else [W006]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_ssl_redirect(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
not _security_middleware() or
|
||||
settings.SECURE_SSL_REDIRECT is True
|
||||
)
|
||||
return [] if passed_check else [W008]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_secret_key(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
getattr(settings, 'SECRET_KEY', None) and
|
||||
len(set(settings.SECRET_KEY)) >= SECRET_KEY_MIN_UNIQUE_CHARACTERS and
|
||||
len(settings.SECRET_KEY) >= SECRET_KEY_MIN_LENGTH
|
||||
)
|
||||
return [] if passed_check else [W009]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_debug(app_configs, **kwargs):
|
||||
passed_check = not settings.DEBUG
|
||||
return [] if passed_check else [W018]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_xframe_deny(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
not _xframe_middleware() or
|
||||
settings.X_FRAME_OPTIONS == 'DENY'
|
||||
)
|
||||
return [] if passed_check else [W019]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_allowed_hosts(app_configs, **kwargs):
|
||||
return [] if settings.ALLOWED_HOSTS else [W020]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_referrer_policy(app_configs, **kwargs):
|
||||
if _security_middleware():
|
||||
if settings.SECURE_REFERRER_POLICY is None:
|
||||
return [W022]
|
||||
# Support a comma-separated string or iterable of values to allow fallback.
|
||||
if isinstance(settings.SECURE_REFERRER_POLICY, str):
|
||||
values = {v.strip() for v in settings.SECURE_REFERRER_POLICY.split(',')}
|
||||
else:
|
||||
values = set(settings.SECURE_REFERRER_POLICY)
|
||||
if not values <= REFERRER_POLICY_VALUES:
|
||||
return [E023]
|
||||
return []
|
||||
|
||||
|
||||
# RemovedInDjango40Warning
|
||||
@register(Tags.security)
|
||||
def check_default_hashing_algorithm(app_configs, **kwargs):
|
||||
if settings.DEFAULT_HASHING_ALGORITHM not in {'sha1', 'sha256'}:
|
||||
return [E100]
|
||||
return []
|
||||
40
Lib/site-packages/django/core/checks/security/csrf.py
Normal file
40
Lib/site-packages/django/core/checks/security/csrf.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from django.conf import settings
|
||||
|
||||
from .. import Tags, Warning, register
|
||||
|
||||
W003 = Warning(
|
||||
"You don't appear to be using Django's built-in "
|
||||
"cross-site request forgery protection via the middleware "
|
||||
"('django.middleware.csrf.CsrfViewMiddleware' is not in your "
|
||||
"MIDDLEWARE). Enabling the middleware is the safest approach "
|
||||
"to ensure you don't leave any holes.",
|
||||
id='security.W003',
|
||||
)
|
||||
|
||||
W016 = Warning(
|
||||
"You have 'django.middleware.csrf.CsrfViewMiddleware' in your "
|
||||
"MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. "
|
||||
"Using a secure-only CSRF cookie makes it more difficult for network "
|
||||
"traffic sniffers to steal the CSRF token.",
|
||||
id='security.W016',
|
||||
)
|
||||
|
||||
|
||||
def _csrf_middleware():
|
||||
return 'django.middleware.csrf.CsrfViewMiddleware' in settings.MIDDLEWARE
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_csrf_middleware(app_configs, **kwargs):
|
||||
passed_check = _csrf_middleware()
|
||||
return [] if passed_check else [W003]
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_csrf_cookie_secure(app_configs, **kwargs):
|
||||
passed_check = (
|
||||
settings.CSRF_USE_SESSIONS or
|
||||
not _csrf_middleware() or
|
||||
settings.CSRF_COOKIE_SECURE
|
||||
)
|
||||
return [] if passed_check else [W016]
|
||||
97
Lib/site-packages/django/core/checks/security/sessions.py
Normal file
97
Lib/site-packages/django/core/checks/security/sessions.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from django.conf import settings
|
||||
|
||||
from .. import Tags, Warning, register
|
||||
|
||||
|
||||
def add_session_cookie_message(message):
|
||||
return message + (
|
||||
" Using a secure-only session cookie makes it more difficult for "
|
||||
"network traffic sniffers to hijack user sessions."
|
||||
)
|
||||
|
||||
|
||||
W010 = Warning(
|
||||
add_session_cookie_message(
|
||||
"You have 'django.contrib.sessions' in your INSTALLED_APPS, "
|
||||
"but you have not set SESSION_COOKIE_SECURE to True."
|
||||
),
|
||||
id='security.W010',
|
||||
)
|
||||
|
||||
W011 = Warning(
|
||||
add_session_cookie_message(
|
||||
"You have 'django.contrib.sessions.middleware.SessionMiddleware' "
|
||||
"in your MIDDLEWARE, but you have not set "
|
||||
"SESSION_COOKIE_SECURE to True."
|
||||
),
|
||||
id='security.W011',
|
||||
)
|
||||
|
||||
W012 = Warning(
|
||||
add_session_cookie_message("SESSION_COOKIE_SECURE is not set to True."),
|
||||
id='security.W012',
|
||||
)
|
||||
|
||||
|
||||
def add_httponly_message(message):
|
||||
return message + (
|
||||
" Using an HttpOnly session cookie makes it more difficult for "
|
||||
"cross-site scripting attacks to hijack user sessions."
|
||||
)
|
||||
|
||||
|
||||
W013 = Warning(
|
||||
add_httponly_message(
|
||||
"You have 'django.contrib.sessions' in your INSTALLED_APPS, "
|
||||
"but you have not set SESSION_COOKIE_HTTPONLY to True.",
|
||||
),
|
||||
id='security.W013',
|
||||
)
|
||||
|
||||
W014 = Warning(
|
||||
add_httponly_message(
|
||||
"You have 'django.contrib.sessions.middleware.SessionMiddleware' "
|
||||
"in your MIDDLEWARE, but you have not set "
|
||||
"SESSION_COOKIE_HTTPONLY to True."
|
||||
),
|
||||
id='security.W014',
|
||||
)
|
||||
|
||||
W015 = Warning(
|
||||
add_httponly_message("SESSION_COOKIE_HTTPONLY is not set to True."),
|
||||
id='security.W015',
|
||||
)
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_session_cookie_secure(app_configs, **kwargs):
|
||||
errors = []
|
||||
if not settings.SESSION_COOKIE_SECURE:
|
||||
if _session_app():
|
||||
errors.append(W010)
|
||||
if _session_middleware():
|
||||
errors.append(W011)
|
||||
if len(errors) > 1:
|
||||
errors = [W012]
|
||||
return errors
|
||||
|
||||
|
||||
@register(Tags.security, deploy=True)
|
||||
def check_session_cookie_httponly(app_configs, **kwargs):
|
||||
errors = []
|
||||
if not settings.SESSION_COOKIE_HTTPONLY:
|
||||
if _session_app():
|
||||
errors.append(W013)
|
||||
if _session_middleware():
|
||||
errors.append(W014)
|
||||
if len(errors) > 1:
|
||||
errors = [W015]
|
||||
return errors
|
||||
|
||||
|
||||
def _session_middleware():
|
||||
return 'django.contrib.sessions.middleware.SessionMiddleware' in settings.MIDDLEWARE
|
||||
|
||||
|
||||
def _session_app():
|
||||
return "django.contrib.sessions" in settings.INSTALLED_APPS
|
||||
35
Lib/site-packages/django/core/checks/templates.py
Normal file
35
Lib/site-packages/django/core/checks/templates.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import copy
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from . import Error, Tags, register
|
||||
|
||||
E001 = Error(
|
||||
"You have 'APP_DIRS': True in your TEMPLATES but also specify 'loaders' "
|
||||
"in OPTIONS. Either remove APP_DIRS or remove the 'loaders' option.",
|
||||
id='templates.E001',
|
||||
)
|
||||
E002 = Error(
|
||||
"'string_if_invalid' in TEMPLATES OPTIONS must be a string but got: {} ({}).",
|
||||
id="templates.E002",
|
||||
)
|
||||
|
||||
|
||||
@register(Tags.templates)
|
||||
def check_setting_app_dirs_loaders(app_configs, **kwargs):
|
||||
return [E001] if any(
|
||||
conf.get('APP_DIRS') and 'loaders' in conf.get('OPTIONS', {})
|
||||
for conf in settings.TEMPLATES
|
||||
) else []
|
||||
|
||||
|
||||
@register(Tags.templates)
|
||||
def check_string_if_invalid_is_string(app_configs, **kwargs):
|
||||
errors = []
|
||||
for conf in settings.TEMPLATES:
|
||||
string_if_invalid = conf.get('OPTIONS', {}).get('string_if_invalid', '')
|
||||
if not isinstance(string_if_invalid, str):
|
||||
error = copy.copy(E002)
|
||||
error.msg = error.msg.format(string_if_invalid, type(string_if_invalid).__name__)
|
||||
errors.append(error)
|
||||
return errors
|
||||
64
Lib/site-packages/django/core/checks/translation.py
Normal file
64
Lib/site-packages/django/core/checks/translation.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from django.conf import settings
|
||||
from django.utils.translation import get_supported_language_variant
|
||||
from django.utils.translation.trans_real import language_code_re
|
||||
|
||||
from . import Error, Tags, register
|
||||
|
||||
E001 = Error(
|
||||
'You have provided an invalid value for the LANGUAGE_CODE setting: {!r}.',
|
||||
id='translation.E001',
|
||||
)
|
||||
|
||||
E002 = Error(
|
||||
'You have provided an invalid language code in the LANGUAGES setting: {!r}.',
|
||||
id='translation.E002',
|
||||
)
|
||||
|
||||
E003 = Error(
|
||||
'You have provided an invalid language code in the LANGUAGES_BIDI setting: {!r}.',
|
||||
id='translation.E003',
|
||||
)
|
||||
|
||||
E004 = Error(
|
||||
'You have provided a value for the LANGUAGE_CODE setting that is not in '
|
||||
'the LANGUAGES setting.',
|
||||
id='translation.E004',
|
||||
)
|
||||
|
||||
|
||||
@register(Tags.translation)
|
||||
def check_setting_language_code(app_configs, **kwargs):
|
||||
"""Error if LANGUAGE_CODE setting is invalid."""
|
||||
tag = settings.LANGUAGE_CODE
|
||||
if not isinstance(tag, str) or not language_code_re.match(tag):
|
||||
return [Error(E001.msg.format(tag), id=E001.id)]
|
||||
return []
|
||||
|
||||
|
||||
@register(Tags.translation)
|
||||
def check_setting_languages(app_configs, **kwargs):
|
||||
"""Error if LANGUAGES setting is invalid."""
|
||||
return [
|
||||
Error(E002.msg.format(tag), id=E002.id)
|
||||
for tag, _ in settings.LANGUAGES if not isinstance(tag, str) or not language_code_re.match(tag)
|
||||
]
|
||||
|
||||
|
||||
@register(Tags.translation)
|
||||
def check_setting_languages_bidi(app_configs, **kwargs):
|
||||
"""Error if LANGUAGES_BIDI setting is invalid."""
|
||||
return [
|
||||
Error(E003.msg.format(tag), id=E003.id)
|
||||
for tag in settings.LANGUAGES_BIDI if not isinstance(tag, str) or not language_code_re.match(tag)
|
||||
]
|
||||
|
||||
|
||||
@register(Tags.translation)
|
||||
def check_language_settings_consistent(app_configs, **kwargs):
|
||||
"""Error if language settings are not consistent with each other."""
|
||||
try:
|
||||
get_supported_language_variant(settings.LANGUAGE_CODE)
|
||||
except LookupError:
|
||||
return [E004]
|
||||
else:
|
||||
return []
|
||||
110
Lib/site-packages/django/core/checks/urls.py
Normal file
110
Lib/site-packages/django/core/checks/urls.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from collections import Counter
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from . import Error, Tags, Warning, register
|
||||
|
||||
|
||||
@register(Tags.urls)
|
||||
def check_url_config(app_configs, **kwargs):
|
||||
if getattr(settings, 'ROOT_URLCONF', None):
|
||||
from django.urls import get_resolver
|
||||
resolver = get_resolver()
|
||||
return check_resolver(resolver)
|
||||
return []
|
||||
|
||||
|
||||
def check_resolver(resolver):
|
||||
"""
|
||||
Recursively check the resolver.
|
||||
"""
|
||||
check_method = getattr(resolver, 'check', None)
|
||||
if check_method is not None:
|
||||
return check_method()
|
||||
elif not hasattr(resolver, 'resolve'):
|
||||
return get_warning_for_invalid_pattern(resolver)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
@register(Tags.urls)
|
||||
def check_url_namespaces_unique(app_configs, **kwargs):
|
||||
"""
|
||||
Warn if URL namespaces used in applications aren't unique.
|
||||
"""
|
||||
if not getattr(settings, 'ROOT_URLCONF', None):
|
||||
return []
|
||||
|
||||
from django.urls import get_resolver
|
||||
resolver = get_resolver()
|
||||
all_namespaces = _load_all_namespaces(resolver)
|
||||
counter = Counter(all_namespaces)
|
||||
non_unique_namespaces = [n for n, count in counter.items() if count > 1]
|
||||
errors = []
|
||||
for namespace in non_unique_namespaces:
|
||||
errors.append(Warning(
|
||||
"URL namespace '{}' isn't unique. You may not be able to reverse "
|
||||
"all URLs in this namespace".format(namespace),
|
||||
id="urls.W005",
|
||||
))
|
||||
return errors
|
||||
|
||||
|
||||
def _load_all_namespaces(resolver, parents=()):
|
||||
"""
|
||||
Recursively load all namespaces from URL patterns.
|
||||
"""
|
||||
url_patterns = getattr(resolver, 'url_patterns', [])
|
||||
namespaces = [
|
||||
':'.join(parents + (url.namespace,)) for url in url_patterns
|
||||
if getattr(url, 'namespace', None) is not None
|
||||
]
|
||||
for pattern in url_patterns:
|
||||
namespace = getattr(pattern, 'namespace', None)
|
||||
current = parents
|
||||
if namespace is not None:
|
||||
current += (namespace,)
|
||||
namespaces.extend(_load_all_namespaces(pattern, current))
|
||||
return namespaces
|
||||
|
||||
|
||||
def get_warning_for_invalid_pattern(pattern):
|
||||
"""
|
||||
Return a list containing a warning that the pattern is invalid.
|
||||
|
||||
describe_pattern() cannot be used here, because we cannot rely on the
|
||||
urlpattern having regex or name attributes.
|
||||
"""
|
||||
if isinstance(pattern, str):
|
||||
hint = (
|
||||
"Try removing the string '{}'. The list of urlpatterns should not "
|
||||
"have a prefix string as the first element.".format(pattern)
|
||||
)
|
||||
elif isinstance(pattern, tuple):
|
||||
hint = "Try using path() instead of a tuple."
|
||||
else:
|
||||
hint = None
|
||||
|
||||
return [Error(
|
||||
"Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list "
|
||||
"of path() and/or re_path() instances.".format(pattern),
|
||||
hint=hint,
|
||||
id="urls.E004",
|
||||
)]
|
||||
|
||||
|
||||
@register(Tags.urls)
|
||||
def check_url_settings(app_configs, **kwargs):
|
||||
errors = []
|
||||
for name in ('STATIC_URL', 'MEDIA_URL'):
|
||||
value = getattr(settings, name)
|
||||
if value and not value.endswith('/'):
|
||||
errors.append(E006(name))
|
||||
return errors
|
||||
|
||||
|
||||
def E006(name):
|
||||
return Error(
|
||||
'The {} setting must end with a slash.'.format(name),
|
||||
id='urls.E006',
|
||||
)
|
||||
Reference in New Issue
Block a user