diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py index 1f1eaded5c..e42adb76ee 100644 --- a/django/core/cache/backends/base.py +++ b/django/core/cache/backends/base.py @@ -388,8 +388,11 @@ class BaseCache: async def aclose(self, **kwargs): pass - def check(self): - return [] + def check(self, **kwargs): + raise NotImplementedError( + "subclasses may provide a check() method to verify the finder is " + "configured correctly." + ) memcached_error_chars_re = _lazy_re_compile(r"[\x00-\x20\x7f]") diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index 29d49c0ede..7f17f47ea8 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -2,6 +2,7 @@ import glob import os import pickle +import pathlib import random import tempfile import time @@ -11,7 +12,7 @@ from hashlib import md5 from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.core.files import locks from django.core.files.move import file_move_safe - +from django.core.checks import Warning class FileBasedCache(BaseCache): cache_suffix = ".djcache" @@ -168,3 +169,21 @@ class FileBasedCache(BaseCache): os.path.join(self._dir, fname) for fname in glob.glob1(self._dir, "*%s" % self.cache_suffix) ] + + @classmethod + def check(cls, cache, paths, name, alias_name='default'): + cache_path = pathlib.Path(cache._dir).resolve() + if any(path == cache_path for path in paths): + relation = "matches" + elif any(path in cache_path.parents for path in paths): + relation = "is inside" + elif any(cache_path in path.parents for path in paths): + relation = "contains" + else: + return None + + return Warning( + f"Your '{alias_name}' cache configuration might expose your cache " + f"or lead to corruption of your data because its LOCATION " + f"{relation} {name}.", id="caches.W002", + ) diff --git a/django/core/checks/caches.py b/django/core/checks/caches.py index c288a6ff4a..069606f400 100644 --- a/django/core/checks/caches.py +++ b/django/core/checks/caches.py @@ -1,4 +1,5 @@ import pathlib +import json from django.conf import settings from django.core.cache import DEFAULT_CACHE_ALIAS, caches @@ -21,6 +22,16 @@ def check_default_cache_is_configured(app_configs, **kwargs): @register(Tags.caches, deploy=True) def check_cache_location_not_exposed(app_configs, **kwargs): + cache = None + alias_name = '' + for alias, config in settings.CACHES.items(): + if config.get('BACKEND').endswith("FileBasedCache"): + cache = caches[alias] + alias_name = alias + + if cache is None: + return [] + errors = [] for name in ("MEDIA_ROOT", "STATIC_ROOT", "STATICFILES_DIRS"): setting = getattr(settings, name, None) @@ -34,43 +45,27 @@ def check_cache_location_not_exposed(app_configs, **kwargs): paths.add(pathlib.Path(staticfiles_dir).resolve()) else: paths = {pathlib.Path(setting).resolve()} - for alias in settings.CACHES: - cache = caches[alias] - if not isinstance(cache, FileBasedCache): - continue - cache_path = pathlib.Path(cache._dir).resolve() - if any(path == cache_path for path in paths): - relation = "matches" - elif any(path in cache_path.parents for path in paths): - relation = "is inside" - elif any(cache_path in path.parents for path in paths): - relation = "contains" - else: - continue - errors.append( - Warning( - f"Your '{alias}' cache configuration might expose your cache " - f"or lead to corruption of your data because its LOCATION " - f"{relation} {name}.", - id="caches.W002", - ) - ) + + check_result = FileBasedCache.check(cache, paths, name, alias_name) + errors.append(check_result) if check_result else [] + return errors @register(Tags.caches) def check_file_based_cache_is_absolute(app_configs, **kwargs): - errors = [] + alias_name = None + location = None for alias, config in settings.CACHES.items(): - cache = caches[alias] - if not isinstance(cache, FileBasedCache): - continue - if not pathlib.Path(config["LOCATION"]).is_absolute(): - errors.append( - Warning( - f"Your '{alias}' cache LOCATION path is relative. Use an " - f"absolute path instead.", - id="caches.W003", - ) + if config.get('BACKEND').endswith("FileBasedCache"): + alias_name = alias + location = config + + if alias_name is not None and not pathlib.Path(location.get("LOCATION")).is_absolute(): + return Warning( + f"Your '{alias_name}' cache LOCATION path is relative. Use an " + f"absolute path instead.", + id="caches.W003", ) - return errors + + return []