Loading django/forms/utils.py +6 −5 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ import sys import warnings 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 import timezone from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -55,8 +55,8 @@ class ErrorDict(dict): def as_data(self): return {f: e.as_data() for f, e in self.items()} def as_json(self): errors = {f: json.loads(e.as_json()) for f, e in self.items()} def as_json(self, escape_html=False): errors = {f: json.loads(e.as_json(escape_html=escape_html)) for f, e in self.items()} return json.dumps(errors) def as_ul(self): Loading Loading @@ -86,11 +86,12 @@ class ErrorList(UserList, list): def as_data(self): return self.data def as_json(self): def as_json(self, escape_html=False): errors = [] for error in ValidationError(self.data).error_list: message = list(error)[0] errors.append({ 'message': list(error)[0], 'message': escape(message) if escape_html else message, 'code': error.code or '', }) return json.dumps(errors) Loading docs/ref/forms/api.txt +12 −1 Original line number Diff line number Diff line Loading @@ -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 messages in ``Form.errors``. .. method:: Form.errors.as_json() .. method:: Form.errors.as_json(escape_html=False) .. versionadded:: 1.7 Loading @@ -152,6 +152,17 @@ Returns the errors serialized as JSON. {"sender": [{"message": "Enter a valid email address.", "code": "invalid"}], "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) .. versionadded:: 1.7 Loading tests/forms_tests/tests/test_forms.py +27 −0 Original line number Diff line number Diff line Loading @@ -2071,6 +2071,33 @@ class FormsTestCase(TestCase): } 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'] = '<p>Non-field error.</p>' self.assertEqual(errors, control) def test_error_list(self): e = ErrorList() e.append('Foo') Loading Loading
django/forms/utils.py +6 −5 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ import sys import warnings 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 import timezone from django.utils.translation import ugettext_lazy as _ Loading Loading @@ -55,8 +55,8 @@ class ErrorDict(dict): def as_data(self): return {f: e.as_data() for f, e in self.items()} def as_json(self): errors = {f: json.loads(e.as_json()) for f, e in self.items()} def as_json(self, escape_html=False): errors = {f: json.loads(e.as_json(escape_html=escape_html)) for f, e in self.items()} return json.dumps(errors) def as_ul(self): Loading Loading @@ -86,11 +86,12 @@ class ErrorList(UserList, list): def as_data(self): return self.data def as_json(self): def as_json(self, escape_html=False): errors = [] for error in ValidationError(self.data).error_list: message = list(error)[0] errors.append({ 'message': list(error)[0], 'message': escape(message) if escape_html else message, 'code': error.code or '', }) return json.dumps(errors) Loading
docs/ref/forms/api.txt +12 −1 Original line number Diff line number Diff line Loading @@ -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 messages in ``Form.errors``. .. method:: Form.errors.as_json() .. method:: Form.errors.as_json(escape_html=False) .. versionadded:: 1.7 Loading @@ -152,6 +152,17 @@ Returns the errors serialized as JSON. {"sender": [{"message": "Enter a valid email address.", "code": "invalid"}], "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) .. versionadded:: 1.7 Loading
tests/forms_tests/tests/test_forms.py +27 −0 Original line number Diff line number Diff line Loading @@ -2071,6 +2071,33 @@ class FormsTestCase(TestCase): } 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'] = '<p>Non-field error.</p>' self.assertEqual(errors, control) def test_error_list(self): e = ErrorList() e.append('Foo') Loading