#31654 closed Bug (fixed)
Memcached key validation raises InvalidCacheKey with clunky message.
Reported by: | Tim McCormack | Owned by: | Mariusz Felisiak |
---|---|---|---|
Component: | Core (Cache system) | Version: | 2.2 |
Severity: | Release blocker | Keywords: | memcached |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
On Django 2.2.13 the code for memcache_key_warnings
in django/core/cache/backends/base.py
has a bad format string that results in raising an exception rather than just producing a warning. This can be reproduced with a memcached key with a space in it, e.g. "foo bar"
.
This code was present before the 2.2.13 release, but becomes more exposed with that release, since it begins validating cache keys.
I think it's as simple as removing the , CacheKeyWarning
.
Change History (14)
comment:1 by , 4 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Summary: | Memcached key validation raises TypeError on invalid characters → Memcached key validation raises InvalidCacheKey with clunky message. |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 4 years ago
... results in raising an exception rather than just producing a warning.
The expected behaviour is to raise the InvalidCacheKey for memcached backends, addressing CVE-2020-13254.
Tim, can you post the traceback?
(How did you hit this?)
comment:3 by , 4 years ago
Ah, this was actually against the dummy backend. It was caught in tests: https://build.testeng.edx.org/job/edx-platform-python-pipeline-pr/18276/testReport/junit/common.djangoapps.xblock_django.tests.test_api/XBlockSupportTestCase/Run_Tests___lms_unit___test_disabled_blocks/
self = <xblock_django.tests.test_api.XBlockSupportTestCase testMethod=test_disabled_blocks> def setUp(self): super(XBlockSupportTestCase, self).setUp() # Set up XBlockConfigurations for disabled and deprecated states block_config = [ ("poll", True, True), ("survey", False, True), ("done", True, False), ] for name, enabled, deprecated in block_config: > XBlockConfiguration(name=name, enabled=enabled, deprecated=deprecated).save() common/djangoapps/xblock_django/tests/test_api.py:28: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/config_models/models.py:110: in save update_fields ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/db/models/base.py:741: in save force_update=force_update, update_fields=update_fields) ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/db/models/base.py:790: in save_base update_fields=update_fields, raw=raw, using=using, ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/dispatch/dispatcher.py:175: in send for receiver in self._live_receivers(sender) ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/dispatch/dispatcher.py:175: in <listcomp> for receiver in self._live_receivers(sender) openedx/core/lib/cache_utils.py:187: in invalidate TieredCache.delete_all_tiers(key) ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/edx_django_utils/cache/utils.py:226: in delete_all_tiers django_cache.delete(key) ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/core/cache/backends/dummy.py:30: in delete self.validate_key(key) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <django.core.cache.backends.dummy.DummyCache object at 0x7fc3211f7d30> key = ":1:<class 'xblock_django.models.XBlockConfiguration'>.xblock_django.api.deprecated_xblocks" def validate_key(self, key): """ Warn about keys that would not be portable to the memcached backend. This encourages (but does not force) writing backend-portable cache code. """ for warning in memcache_key_warnings(key): > warnings.warn(warning, CacheKeyWarning) E TypeError: 'type' object cannot be interpreted as an integer ../edx-venv-3.5/edx-venv/lib/python3.5/site-packages/django/core/cache/backends/base.py:250: TypeError
This error only started happening in tests with the Django 2.2.12 -> 2.2.13 upgrade. I don't understand why we wouldn't have seen the InvalidCacheKey error in production, where we use memcached.
(We were in fact creating an invalid cache key, and I have a patch ready for that on our side that unblocks the upgrade, so I'm not sure how much we'll end up digging into that mystery!)
comment:4 by , 4 years ago
Severity: | Normal → Release blocker |
---|
Thanks for the follow up Tim.
I still don't get the error using 3.5...
Python 3.5.9 (default, Jun 4 2020, 16:47:18) [GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.46.4)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from django.core.cache.backends.base import CacheKeyWarning >>> import warnings >>> a_tuple = ('A string message, plus...', CacheKeyWarning) >>> warnings.warn(a_tuple, CacheKeyWarning) __main__:1: CacheKeyWarning: ('A string message, plus...', <class 'django.core.cache.backends.base.CacheKeyWarning'>)
There must be something else going on. Maybe a custom warning formatting?
(How else is the TypeError: 'type' object cannot be interpreted as an integer
— the warning class being converted to an int? 🤔)
The EdX change will stop the warning coming up, and is exactly the case that the patch was meant to catch.
Treating CacheKeyWarnings as errors would be good — I wonder how many folks would be seeing those and ignoring them... 😬
I think there's enough of a regression here to backport the fix.
comment:5 by , 4 years ago
I don't understand it either! I'll take a little more time to poke at it today. (For anyone who wants to reproduce this strange test error in place: Python 3.5 virtualenv with pip 20.0.2, make requirements
, pytest -s common/djangoapps/xblock_django/tests/test_api.py::XBlockSupportTestCase::test_authorable_blocks
-- on commit 9f53525c.) The farthest I've gotten is that it's definitely coming from inside warnings.warn(...)
:
(Pdb) warnings.warn(("hi", str), RuntimeWarning) *** TypeError: 'type' object cannot be interpreted as an integer
Why it doesn't happen on a fresh REPL with all dependencies loaded, I'm not sure. My best guess is that it has something to do with warning filtering, since that's configurable and appears to be what's inspecting the warning message. Probably not specifically relevant to Django or the edX code, though.
(Edit: Specified pip version.)
comment:6 by , 4 years ago
Out of curiosity, do you get the same crash if passing category
as a keyword argument? warnings.warn(warning, category=CacheKeyWarning)
comment:7 by , 4 years ago
I can reproduce this error when unpacking tuple
>>> warnings.warn(*("hi", str), RuntimeWarning) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'type' object cannot be interpreted as an integer
because RuntimeWarning
is passed as stacklevel
, it must be somewhere in EdX. Nevertheless we will fix this message.
comment:13 by , 4 years ago
I get the error even with warnings.warn(message=("hi", dict), category=UserWarning)
. I suspect it depends on which warning filters are loaded in your environment.
comment:14 by , 4 years ago
Do you plan to re-release 3.0.7 and 2.2.13 to fix this, out of interest?
As an aside, I don't believe this was present before: See https://i.imgur.com/fZOegeI.jpg and https://i.imgur.com/hyc3k4J.jpg - you can see that the CacheKeyWarning was removed/added (it was factored out into eventual call to warnings.warn.
Thanks for the report, we've already noticed this, see PR. It raises
InvalidCacheKey
for me (even if a message is not perfect), e.g.