+119
-7
lines changedFilter options
+119
-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()
@@ -481,7 +481,7 @@ def striptags(value):
481
481
def _property_resolver(arg):
482
482
"""
483
483
When arg is convertible to float, behave like operator.itemgetter(arg)
484
-
Otherwise, behave like Variable(arg).resolve
484
+
Otherwise, chain __getitem__() and getattr().
485
485
486
486
>>> _property_resolver(1)('abc')
487
487
'b'
@@ -499,7 +499,19 @@ def _property_resolver(arg):
499
499
try:
500
500
float(arg)
501
501
except ValueError:
502
-
return Variable(arg).resolve
502
+
if VARIABLE_ATTRIBUTE_SEPARATOR + '_' in arg or arg[0] == '_':
503
+
raise AttributeError('Access to private variables is forbidden.')
504
+
parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR)
505
+
506
+
def resolve(value):
507
+
for part in parts:
508
+
try:
509
+
value = value[part]
510
+
except (AttributeError, IndexError, KeyError, TypeError, ValueError):
511
+
value = getattr(value, part)
512
+
return value
513
+
514
+
return resolve
503
515
else:
504
516
return itemgetter(arg)
505
517
@@ -512,7 +524,7 @@ def dictsort(value, arg):
512
524
"""
513
525
try:
514
526
return sorted(value, key=_property_resolver(arg))
515
-
except (TypeError, VariableDoesNotExist):
527
+
except (AttributeError, TypeError):
516
528
return ''
517
529
518
530
@@ -524,7 +536,7 @@ def dictsortreversed(value, arg):
524
536
"""
525
537
try:
526
538
return sorted(value, key=_property_resolver(arg), reverse=True)
527
-
except (TypeError, VariableDoesNotExist):
539
+
except (AttributeError, TypeError):
528
540
return ''
529
541
530
542
Original file line number Diff line number Diff line change
@@ -1586,6 +1586,13 @@ produce empty output::
1586
1586
1587
1587
{{ values|dictsort:"0" }}
1588
1588
1589
+
Ordering by elements at specified index is not supported on dictionaries.
1590
+
1591
+
.. versionchanged:: 2.2.26
1592
+
1593
+
In older versions, ordering elements at specified index was supported on
1594
+
dictionaries.
1595
+
1589
1596
.. templatefilter:: dictsortreversed
1590
1597
1591
1598
``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
@@ -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