Fixed #21962 -- Added escape_html flag to ErrorDict.as_json()

This commit is contained in:
vvojvoda 2014-02-18 20:00:09 +01:00 committed by Tim Graham
parent 7b4743580a
commit c23b3717be
3 changed files with 45 additions and 6 deletions

View File

@ -5,7 +5,7 @@ import sys
import warnings import warnings
from django.conf import settings from django.conf import settings
from django.utils.html import format_html, format_html_join from django.utils.html import format_html, format_html_join, escape
from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -55,8 +55,8 @@ class ErrorDict(dict):
def as_data(self): def as_data(self):
return {f: e.as_data() for f, e in self.items()} return {f: e.as_data() for f, e in self.items()}
def as_json(self): def as_json(self, escape_html=False):
errors = {f: json.loads(e.as_json()) for f, e in self.items()} errors = {f: json.loads(e.as_json(escape_html=escape_html)) for f, e in self.items()}
return json.dumps(errors) return json.dumps(errors)
def as_ul(self): def as_ul(self):
@ -86,11 +86,12 @@ class ErrorList(UserList, list):
def as_data(self): def as_data(self):
return self.data return self.data
def as_json(self): def as_json(self, escape_html=False):
errors = [] errors = []
for error in ValidationError(self.data).error_list: for error in ValidationError(self.data).error_list:
message = list(error)[0]
errors.append({ errors.append({
'message': list(error)[0], 'message': escape(message) if escape_html else message,
'code': error.code or '', 'code': error.code or '',
}) })
return json.dumps(errors) return json.dumps(errors)

View File

@ -142,7 +142,7 @@ and methods with an ``as_`` prefix could render them, but it had to be done
the other way around in order not to break code that expects rendered error the other way around in order not to break code that expects rendered error
messages in ``Form.errors``. messages in ``Form.errors``.
.. method:: Form.errors.as_json() .. method:: Form.errors.as_json(escape_html=False)
.. versionadded:: 1.7 .. versionadded:: 1.7
@ -152,6 +152,17 @@ Returns the errors serialized as JSON.
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}], {"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]} "subject": [{"message": "This field is required.", "code": "required"}]}
By default, ``as_json()`` does not escape its output. If you are using it for
something like AJAX requests to a form view where the client interprets the
response and inserts errors into the page, you'll want to be sure to escape the
results on the client-side to avoid the possibility of a cross-site scripting
attack. It's trivial to do so using a JavaScript library like jQuery - simply
use ``$(el).text(errorText)`` rather than ``.html()``.
If for some reason you don't want to use client-side escaping, you can also
set ``escape_html=True`` and error messages will be escaped so you can use them
directly in HTML.
.. method:: Form.add_error(field, error) .. method:: Form.add_error(field, error)
.. versionadded:: 1.7 .. versionadded:: 1.7

View File

@ -2071,6 +2071,33 @@ class FormsTestCase(TestCase):
} }
self.assertEqual(errors, control) self.assertEqual(errors, control)
def test_error_dict_as_json_escape_html(self):
"""#21962 - adding html escape flag to ErrorDict"""
class MyForm(Form):
foo = CharField()
bar = CharField()
def clean(self):
raise ValidationError('<p>Non-field error.</p>',
code='secret',
params={'a': 1, 'b': 2})
control = {
'foo': [{'code': 'required', 'message': 'This field is required.'}],
'bar': [{'code': 'required', 'message': 'This field is required.'}],
'__all__': [{'code': 'secret', 'message': '<p>Non-field error.</p>'}]
}
form = MyForm({})
self.assertFalse(form.is_valid())
errors = json.loads(form.errors.as_json())
self.assertEqual(errors, control)
errors = json.loads(form.errors.as_json(escape_html=True))
control['__all__'][0]['message'] = '&lt;p&gt;Non-field error.&lt;/p&gt;'
self.assertEqual(errors, control)
def test_error_list(self): def test_error_list(self):
e = ErrorList() e = ErrorList()
e.append('Foo') e.append('Foo')