+135
-7
lines changedFilter options
+135
-7
lines changed Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
22
22
from django.utils.timesince import timesince, timeuntil
23
23
from django.utils.translation import gettext, ngettext
24
24
25
-
from .base import Variable, VariableDoesNotExist
25
+
from .base import VARIABLE_ATTRIBUTE_SEPARATOR
26
26
from .library import Library
27
27
28
28
register = Library()
@@ -503,7 +503,7 @@ def striptags(value):
503
503
def _property_resolver(arg):
504
504
"""
505
505
When arg is convertible to float, behave like operator.itemgetter(arg)
506
-
Otherwise, behave like Variable(arg).resolve
506
+
Otherwise, chain __getitem__() and getattr().
507
507
508
508
>>> _property_resolver(1)('abc')
509
509
'b'
@@ -521,7 +521,19 @@ def _property_resolver(arg):
521
521
try:
522
522
float(arg)
523
523
except ValueError:
524
-
return Variable(arg).resolve
524
+
if VARIABLE_ATTRIBUTE_SEPARATOR + '_' in arg or arg[0] == '_':
525
+
raise AttributeError('Access to private variables is forbidden.')
526
+
parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR)
527
+
528
+
def resolve(value):
529
+
for part in parts:
530
+
try:
531
+
value = value[part]
532
+
except (AttributeError, IndexError, KeyError, TypeError, ValueError):
533
+
value = getattr(value, part)
534
+
return value
535
+
536
+
return resolve
525
537
else:
526
538
return itemgetter(arg)
527
539
@@ -534,7 +546,7 @@ def dictsort(value, arg):
534
546
"""
535
547
try:
536
548
return sorted(value, key=_property_resolver(arg))
537
-
except (TypeError, VariableDoesNotExist):
549
+
except (AttributeError, TypeError):
538
550
return ''
539
551
540
552
@@ -546,7 +558,7 @@ def dictsortreversed(value, arg):
546
558
"""
547
559
try:
548
560
return sorted(value, key=_property_resolver(arg), reverse=True)
549
-
except (TypeError, VariableDoesNotExist):
561
+
except (AttributeError, TypeError):
550
562
return ''
551
563
552
564
Original file line number Diff line number Diff line change
@@ -1577,6 +1577,13 @@ produce empty output::
1577
1577
1578
1578
{{ values|dictsort:"0" }}
1579
1579
1580
+
Ordering by elements at specified index is not supported on dictionaries.
1581
+
1582
+
.. versionchanged:: 2.2.26
1583
+
1584
+
In older versions, ordering elements at specified index was supported on
1585
+
dictionaries.
1586
+
1580
1587
.. templatefilter:: dictsortreversed
1581
1588
1582
1589
``dictsortreversed``
Original file line number Diff line number Diff line change
@@ -20,3 +20,19 @@ In order to mitigate this issue, relatively long values are now ignored by
20
20
21
21
This issue has severity "medium" according to the :ref:`Django security policy
22
22
<security-disclosure>`.
23
+
24
+
CVE-2021-45116: Potential information disclosure in ``dictsort`` template filter
25
+
================================================================================
26
+
27
+
Due to leveraging the Django Template Language's variable resolution logic, the
28
+
:tfilter:`dictsort` template filter was potentially vulnerable to information
29
+
disclosure or unintended method calls, if passed a suitably crafted key.
30
+
31
+
In order to avoid this possibility, ``dictsort`` now works with a restricted
32
+
resolution logic, that will not call methods, nor allow indexing on
33
+
dictionaries.
34
+
35
+
As a reminder, all untrusted user input should be validated before use.
36
+
37
+
This issue has severity "low" according to the :ref:`Django security policy
38
+
<security-disclosure>`.
Original file line number Diff line number Diff line change
@@ -20,3 +20,19 @@ In order to mitigate this issue, relatively long values are now ignored by
20
20
21
21
This issue has severity "medium" according to the :ref:`Django security policy
22
22
<security-disclosure>`.
23
+
24
+
CVE-2021-45116: Potential information disclosure in ``dictsort`` template filter
25
+
================================================================================
26
+
27
+
Due to leveraging the Django Template Language's variable resolution logic, the
28
+
:tfilter:`dictsort` template filter was potentially vulnerable to information
29
+
disclosure or unintended method calls, if passed a suitably crafted key.
30
+
31
+
In order to avoid this possibility, ``dictsort`` now works with a restricted
32
+
resolution logic, that will not call methods, nor allow indexing on
33
+
dictionaries.
34
+
35
+
As a reminder, all untrusted user input should be validated before use.
36
+
37
+
This issue has severity "low" according to the :ref:`Django security policy
38
+
<security-disclosure>`.
Original file line number Diff line number Diff line change
@@ -21,6 +21,22 @@ In order to mitigate this issue, relatively long values are now ignored by
21
21
This issue has severity "medium" according to the :ref:`Django security policy
22
22
<security-disclosure>`.
23
23
24
+
CVE-2021-45116: Potential information disclosure in ``dictsort`` template filter
25
+
================================================================================
26
+
27
+
Due to leveraging the Django Template Language's variable resolution logic, the
28
+
:tfilter:`dictsort` template filter was potentially vulnerable to information
29
+
disclosure or unintended method calls, if passed a suitably crafted key.
30
+
31
+
In order to avoid this possibility, ``dictsort`` now works with a restricted
32
+
resolution logic, that will not call methods, nor allow indexing on
33
+
dictionaries.
34
+
35
+
As a reminder, all untrusted user input should be validated before use.
36
+
37
+
This issue has severity "low" according to the :ref:`Django security policy
38
+
<security-disclosure>`.
39
+
24
40
Bugfixes
25
41
========
26
42
Original file line number Diff line number Diff line change
@@ -1,9 +1,58 @@
1
-
from django.template.defaultfilters import dictsort
1
+
from django.template.defaultfilters import _property_resolver, dictsort
2
2
from django.test import SimpleTestCase
3
3
4
4
5
+
class User:
6
+
password = 'abc'
7
+
8
+
_private = 'private'
9
+
10
+
@property
11
+
def test_property(self):
12
+
return 'cde'
13
+
14
+
def test_method(self):
15
+
"""This is just a test method."""
16
+
17
+
5
18
class FunctionTests(SimpleTestCase):
6
19
20
+
def test_property_resolver(self):
21
+
user = User()
22
+
dict_data = {'a': {
23
+
'b1': {'c': 'result1'},
24
+
'b2': user,
25
+
'b3': {'0': 'result2'},
26
+
'b4': [0, 1, 2],
27
+
}}
28
+
list_data = ['a', 'b', 'c']
29
+
tests = [
30
+
('a.b1.c', dict_data, 'result1'),
31
+
('a.b2.password', dict_data, 'abc'),
32
+
('a.b2.test_property', dict_data, 'cde'),
33
+
# The method should not get called.
34
+
('a.b2.test_method', dict_data, user.test_method),
35
+
('a.b3.0', dict_data, 'result2'),
36
+
(0, list_data, 'a'),
37
+
]
38
+
for arg, data, expected_value in tests:
39
+
with self.subTest(arg=arg):
40
+
self.assertEqual(_property_resolver(arg)(data), expected_value)
41
+
# Invalid lookups.
42
+
fail_tests = [
43
+
('a.b1.d', dict_data, AttributeError),
44
+
('a.b2.password.0', dict_data, AttributeError),
45
+
('a.b2._private', dict_data, AttributeError),
46
+
('a.b4.0', dict_data, AttributeError),
47
+
('a', list_data, AttributeError),
48
+
('0', list_data, TypeError),
49
+
(4, list_data, IndexError),
50
+
]
51
+
for arg, data, expected_exception in fail_tests:
52
+
with self.subTest(arg=arg):
53
+
with self.assertRaises(expected_exception):
54
+
_property_resolver(arg)(data)
55
+
7
56
def test_sort(self):
8
57
sorted_dicts = dictsort(
9
58
[{'age': 23, 'name': 'Barbara-Ann'},
@@ -21,7 +70,7 @@ def test_sort(self):
21
70
22
71
def test_dictsort_complex_sorting_key(self):
23
72
"""
24
-
Since dictsort uses template.Variable under the hood, it can sort
73
+
Since dictsort uses dict.get()/getattr() under the hood, it can sort
25
74
on keys like 'foo.bar'.
26
75
"""
27
76
data = [
@@ -60,3 +109,9 @@ def test_invalid_values(self):
60
109
self.assertEqual(dictsort('Hello!', 'age'), '')
61
110
self.assertEqual(dictsort({'a': 1}, 'age'), '')
62
111
self.assertEqual(dictsort(1, 'age'), '')
112
+
113
+
def test_invalid_args(self):
114
+
"""Fail silently if invalid lookups are passed."""
115
+
self.assertEqual(dictsort([{}], '._private'), '')
116
+
self.assertEqual(dictsort([{'_private': 'test'}], '_private'), '')
117
+
self.assertEqual(dictsort([{'nested': {'_private': 'test'}}], 'nested._private'), '')
Original file line number Diff line number Diff line change
@@ -46,3 +46,9 @@ def test_invalid_values(self):
46
46
self.assertEqual(dictsortreversed('Hello!', 'age'), '')
47
47
self.assertEqual(dictsortreversed({'a': 1}, 'age'), '')
48
48
self.assertEqual(dictsortreversed(1, 'age'), '')
49
+
50
+
def test_invalid_args(self):
51
+
"""Fail silently if invalid lookups are passed."""
52
+
self.assertEqual(dictsortreversed([{}], '._private'), '')
53
+
self.assertEqual(dictsortreversed([{'_private': 'test'}], '_private'), '')
54
+
self.assertEqual(dictsortreversed([{'nested': {'_private': 'test'}}], 'nested._private'), '')
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4