Imply the |safe on tojson in templates and change escaping logic

This commit is contained in:
Armin Ronacher 2013-06-03 12:25:08 +01:00
parent 56d3b74488
commit ef72b78042
7 changed files with 46 additions and 31 deletions

View file

@ -659,7 +659,7 @@ class Flask(_PackageBoundObject):
session=session,
g=g
)
rv.filters['tojson'] = json.htmlsafe_dumps
rv.filters['tojson'] = json.tojson_filter
return rv
def create_global_jinja_loader(self):
@ -1707,13 +1707,6 @@ class Flask(_PackageBoundObject):
rv = func(exc)
request_tearing_down.send(self, exc=exc)
# If this interpreter supports clearing the exception information
# we do that now. This will only go into effect on Python 2.x,
# on 3.x it disappears automatically at the end of the exception
# stack.
if hasattr(sys, 'exc_clear'):
sys.exc_clear()
def do_teardown_appcontext(self, exc=None):
"""Called when an application context is popped. This works pretty
much the same as :meth:`do_teardown_request` but for the application

View file

@ -15,6 +15,7 @@ from .globals import current_app, request
from ._compat import text_type, PY2
from werkzeug.http import http_date
from jinja2 import Markup
# Use the same json implementation as itsdangerous on which we
# depend anyways.
@ -160,18 +161,26 @@ def load(fp, **kwargs):
def htmlsafe_dumps(obj, **kwargs):
"""Works exactly like :func:`dumps` but is safe for use in ``<script>``
tags. It accepts the same arguments and returns a JSON string. Note that
this is available in templates through the ``|tojson`` filter but it will
have to be wrapped in ``|safe`` unless **true** XHTML is being used.
this is available in templates through the ``|tojson`` filter which will
also mark the result as safe. Due to how this function escapes certain
characters this is safe even if used outside of ``<script>`` tags.
.. versionchanged:: 0.10
This function's return value is now always safe for HTML usage, even
if outside of script tags or if used in XHTML.
"""
rv = dumps(obj, **kwargs)
if _slash_escape:
rv = rv.replace('/', '\\/')
return rv.replace('<!', '<\\u0021')
rv = dumps(obj, **kwargs) \
.replace(u'<', u'\\u003c') \
.replace(u'>', u'\\u003e') \
.replace(u'&', u'\\u0026')
if not _slash_escape:
rv = rv.replace('\\/', '/')
return rv
def htmlsafe_dump(obj, fp, **kwargs):
"""Like :func:`htmlsafe_dumps` but writes into a file object."""
fp.write(htmlsafe_dumps(obj, **kwargs))
fp.write(unicode(htmlsafe_dumps(obj, **kwargs)))
def jsonify(*args, **kwargs):
@ -213,3 +222,7 @@ def jsonify(*args, **kwargs):
return current_app.response_class(dumps(dict(*args, **kwargs),
indent=indent),
mimetype='application/json')
def tojson_filter(obj, **kwargs):
return Markup(htmlsafe_dumps(obj, **kwargs))

View file

@ -92,12 +92,17 @@ class JSONTestCase(FlaskTestCase):
app = flask.Flask(__name__)
render = flask.render_template_string
with app.test_request_context():
rv = render('{{ "</script>"|tojson|safe }}')
self.assert_equal(rv, '"<\\/script>"')
rv = render('{{ "<\0/script>"|tojson|safe }}')
self.assert_equal(rv, '"<\\u0000\\/script>"')
rv = render('{{ "<!--<script>"|tojson|safe }}')
self.assert_equal(rv, '"<\\u0021--<script>"')
rv = flask.json.htmlsafe_dumps('</script>')
self.assert_equal(rv, u'"\\u003c/script\\u003e"')
self.assert_equal(type(rv), text_type)
rv = render('{{ "</script>"|tojson }}')
self.assert_equal(rv, '"\\u003c/script\\u003e"')
rv = render('{{ "<\0/script>"|tojson }}')
self.assert_equal(rv, '"\\u003c\\u0000/script\\u003e"')
rv = render('{{ "<!--<script>"|tojson }}')
self.assert_equal(rv, '"\\u003c!--\\u003cscript\\u003e"')
rv = render('{{ "&"|tojson }}')
self.assert_equal(rv, '"\\u0026"')
def test_json_customization(self):
class X(object):