Skip to content

Commit 5ed55bf

Browse files
vavsabclaude
andcommitted
fix(#868k5bbdr): Update djangocache tests to reflect SafeUnpickler restrictions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b8fe5ee commit 5ed55bf

1 file changed

Lines changed: 28 additions & 37 deletions

File tree

tests/test_djangocache.py

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -227,31 +227,37 @@ def test_close(self):
227227
cache.close()
228228

229229
def test_data_types(self):
230-
# Many different data types can be cached
230+
# Safe built-in types can be cached
231231
stuff = {
232232
'string': 'this is a string',
233233
'int': 42,
234234
'list': [1, 2, 3, 4],
235235
'tuple': (1, 2, 3, 4),
236236
'dict': {'A': 1, 'B': 2},
237-
'function': f,
238-
'class': C,
239237
}
240238
cache.set('stuff', stuff)
241239
self.assertEqual(cache.get('stuff'), stuff)
242240

241+
def test_data_types_unsafe_rejected(self):
242+
# Arbitrary functions and classes are blocked by SafeUnpickler (CVE-2025-69872)
243+
from diskcache.core import UnpicklingError
244+
cache.set('fn', f)
245+
with self.assertRaises(UnpicklingError):
246+
cache.get('fn')
247+
cache.set('cls', C)
248+
with self.assertRaises(UnpicklingError):
249+
cache.get('cls')
250+
243251
def test_cache_read_for_model_instance(self):
244-
# Don't want fields with callable as default to be called on cache read
252+
# Django model instances use django.db.models.base.model_unpickle which
253+
# is not in the SafeUnpickler allowlist (CVE-2025-69872).
254+
from diskcache.core import UnpicklingError
245255
expensive_calculation.num_runs = 0
246256
Poll.objects.all().delete()
247257
my_poll = Poll.objects.create(question='Well?')
248-
self.assertEqual(Poll.objects.count(), 1)
249-
pub_date = my_poll.pub_date
250258
cache.set('question', my_poll)
251-
cached_poll = cache.get('question')
252-
self.assertEqual(cached_poll.pub_date, pub_date)
253-
# We only want the default expensive calculation run once
254-
self.assertEqual(expensive_calculation.num_runs, 1)
259+
with self.assertRaises(UnpicklingError):
260+
cache.get('question')
255261

256262
def test_cache_write_for_model_instance_with_deferred(self):
257263
# Don't want fields with callable as default to be called on cache write
@@ -267,21 +273,16 @@ def test_cache_write_for_model_instance_with_deferred(self):
267273
self.assertEqual(expensive_calculation.num_runs, 1)
268274

269275
def test_cache_read_for_model_instance_with_deferred(self):
270-
# Don't want fields with callable as default to be called on cache read
276+
# Deferred querysets reference user model classes not in the SafeUnpickler
277+
# allowlist (CVE-2025-69872).
278+
from diskcache.core import UnpicklingError
271279
expensive_calculation.num_runs = 0
272280
Poll.objects.all().delete()
273281
Poll.objects.create(question='What?')
274-
self.assertEqual(expensive_calculation.num_runs, 1)
275282
defer_qs = Poll.objects.all().defer('question')
276-
self.assertEqual(defer_qs.count(), 1)
277283
cache.set('deferred_queryset', defer_qs)
278-
self.assertEqual(expensive_calculation.num_runs, 1)
279-
runs_before_cache_read = expensive_calculation.num_runs
280-
cache.get('deferred_queryset')
281-
# We only want the default expensive calculation run on creation and set
282-
self.assertEqual(
283-
expensive_calculation.num_runs, runs_before_cache_read
284-
)
284+
with self.assertRaises(UnpicklingError):
285+
cache.get('deferred_queryset')
285286

286287
def test_expiration(self):
287288
# Cache values can be set to expire
@@ -869,14 +870,12 @@ def test_custom_key_func(self):
869870
self.assertEqual(caches['custom_key2'].get('answer2'), 42)
870871

871872
def test_cache_write_unpicklable_object(self):
872-
fetch_middleware = FetchFromCacheMiddleware(empty_response)
873+
# HttpResponse and SimpleCookie are not in the SafeUnpickler allowlist
874+
# (CVE-2025-69872). Writing succeeds but reading raises UnpicklingError.
875+
from diskcache.core import UnpicklingError
873876

874877
request = self.factory.get('/cache/test')
875878
request._cache_update_cache = True
876-
get_cache_data = FetchFromCacheMiddleware(
877-
empty_response
878-
).process_request(request)
879-
self.assertIsNone(get_cache_data)
880879

881880
content = 'Testing cookie serialization.'
882881

@@ -885,19 +884,11 @@ def get_response(req):
885884
response.set_cookie('foo', 'bar')
886885
return response
887886

888-
update_middleware = UpdateCacheMiddleware(get_response)
889-
response = update_middleware(request)
887+
UpdateCacheMiddleware(get_response)(request)
890888

891-
get_cache_data = fetch_middleware.process_request(request)
892-
self.assertIsNotNone(get_cache_data)
893-
self.assertEqual(get_cache_data.content, content.encode())
894-
self.assertEqual(get_cache_data.cookies, response.cookies)
895-
896-
UpdateCacheMiddleware(lambda req: get_cache_data)(request)
897-
get_cache_data = fetch_middleware.process_request(request)
898-
self.assertIsNotNone(get_cache_data)
899-
self.assertEqual(get_cache_data.content, content.encode())
900-
self.assertEqual(get_cache_data.cookies, response.cookies)
889+
fetch_middleware = FetchFromCacheMiddleware(empty_response)
890+
with self.assertRaises(UnpicklingError):
891+
fetch_middleware.process_request(request)
901892

902893
def test_add_fail_on_pickleerror(self):
903894
# Shouldn't fail silently if trying to cache an unpicklable type.

0 commit comments

Comments
 (0)