Skip to content

Commit ef72b78

Browse files
committed
Imply the |safe on tojson in templates and change escaping logic
1 parent 56d3b74 commit ef72b78

File tree

7 files changed

+46
-31
lines changed

7 files changed

+46
-31
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Release date to be decided.
1717
``template_filter`` method family.
1818
- Set the content-length header for x-sendfile.
1919
- ``tojson`` filter now does not escape script blocks in HTML5 parsers.
20+
- ``tojson`` used in templates is now safe by default due. This was
21+
allowed due to the different escaping behavior.
2022
- Flask will now raise an error if you attempt to register a new function
2123
on an already used endpoint.
2224
- Added wrapper module around simplejson and added default serialization

docs/api.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,16 +364,15 @@ JSON module:
364364
The :func:`~htmlsafe_dumps` function of this json module is also available
365365
as filter called ``|tojson`` in Jinja2. Note that inside `script`
366366
tags no escaping must take place, so make sure to disable escaping
367-
with ``|safe`` if you intend to use it inside `script` tags:
367+
with ``|safe`` if you intend to use it inside `script` tags unless
368+
you are using Flask 0.10 which implies that:
368369

369370
.. sourcecode:: html+jinja
370371

371372
<script type=text/javascript>
372373
doSomethingWith({{ user.username|tojson|safe }});
373374
</script>
374375

375-
Note that the ``|tojson`` filter escapes forward slashes properly.
376-
377376
.. autofunction:: jsonify
378377

379378
.. autofunction:: dumps

docs/patterns/jquery.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ like this:
6363
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
6464
</script>
6565

66-
The ``|safe`` is necessary so that Jinja does not escape the JSON encoded
67-
string with HTML rules. Usually this would be necessary, but we are
68-
inside a `script` block here where different rules apply.
66+
The ``|safe`` is necessary in Flask before 0.10 so that Jinja does not
67+
escape the JSON encoded string with HTML rules. Usually this would be
68+
necessary, but we are inside a `script` block here where different rules
69+
apply.
6970

7071
.. admonition:: Information for Pros
7172

@@ -76,6 +77,10 @@ inside a `script` block here where different rules apply.
7677
escape slashes for you (``{{ "</script>"|tojson|safe }}`` is rendered as
7778
``"<\/script>"``).
7879

80+
In Flask 0.10 it goes a step further and escapes all HTML tags with
81+
unicode escapes. This makes it possible for Flask to automatically
82+
mark the result as HTML safe.
83+
7984

8085
JSON View Functions
8186
-------------------

docs/templating.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,15 @@ by Jinja2 itself:
106106
fly.
107107

108108
Note that inside `script` tags no escaping must take place, so make
109-
sure to disable escaping with ``|safe`` if you intend to use it inside
110-
`script` tags:
109+
sure to disable escaping with ``|safe`` before Flask 0.10 if you intend
110+
to use it inside `script` tags:
111111

112112
.. sourcecode:: html+jinja
113113

114114
<script type=text/javascript>
115115
doSomethingWith({{ user.username|tojson|safe }});
116116
</script>
117117

118-
That the ``|tojson`` filter escapes forward slashes properly for you.
119-
120118
Controlling Autoescaping
121119
------------------------
122120

flask/app.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ def create_jinja_environment(self):
659659
session=session,
660660
g=g
661661
)
662-
rv.filters['tojson'] = json.htmlsafe_dumps
662+
rv.filters['tojson'] = json.tojson_filter
663663
return rv
664664

665665
def create_global_jinja_loader(self):
@@ -1707,13 +1707,6 @@ def do_teardown_request(self, exc=None):
17071707
rv = func(exc)
17081708
request_tearing_down.send(self, exc=exc)
17091709

1710-
# If this interpreter supports clearing the exception information
1711-
# we do that now. This will only go into effect on Python 2.x,
1712-
# on 3.x it disappears automatically at the end of the exception
1713-
# stack.
1714-
if hasattr(sys, 'exc_clear'):
1715-
sys.exc_clear()
1716-
17171710
def do_teardown_appcontext(self, exc=None):
17181711
"""Called when an application context is popped. This works pretty
17191712
much the same as :meth:`do_teardown_request` but for the application

flask/json.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from ._compat import text_type, PY2
1616

1717
from werkzeug.http import http_date
18+
from jinja2 import Markup
1819

1920
# Use the same json implementation as itsdangerous on which we
2021
# depend anyways.
@@ -160,18 +161,26 @@ def load(fp, **kwargs):
160161
def htmlsafe_dumps(obj, **kwargs):
161162
"""Works exactly like :func:`dumps` but is safe for use in ``<script>``
162163
tags. It accepts the same arguments and returns a JSON string. Note that
163-
this is available in templates through the ``|tojson`` filter but it will
164-
have to be wrapped in ``|safe`` unless **true** XHTML is being used.
164+
this is available in templates through the ``|tojson`` filter which will
165+
also mark the result as safe. Due to how this function escapes certain
166+
characters this is safe even if used outside of ``<script>`` tags.
167+
168+
.. versionchanged:: 0.10
169+
This function's return value is now always safe for HTML usage, even
170+
if outside of script tags or if used in XHTML.
165171
"""
166-
rv = dumps(obj, **kwargs)
167-
if _slash_escape:
168-
rv = rv.replace('/', '\\/')
169-
return rv.replace('<!', '<\\u0021')
172+
rv = dumps(obj, **kwargs) \
173+
.replace(u'<', u'\\u003c') \
174+
.replace(u'>', u'\\u003e') \
175+
.replace(u'&', u'\\u0026')
176+
if not _slash_escape:
177+
rv = rv.replace('\\/', '/')
178+
return rv
170179

171180

172181
def htmlsafe_dump(obj, fp, **kwargs):
173182
"""Like :func:`htmlsafe_dumps` but writes into a file object."""
174-
fp.write(htmlsafe_dumps(obj, **kwargs))
183+
fp.write(unicode(htmlsafe_dumps(obj, **kwargs)))
175184

176185

177186
def jsonify(*args, **kwargs):
@@ -213,3 +222,7 @@ def get_current_user():
213222
return current_app.response_class(dumps(dict(*args, **kwargs),
214223
indent=indent),
215224
mimetype='application/json')
225+
226+
227+
def tojson_filter(obj, **kwargs):
228+
return Markup(htmlsafe_dumps(obj, **kwargs))

flask/testsuite/helpers.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,17 @@ def test_template_escaping(self):
9292
app = flask.Flask(__name__)
9393
render = flask.render_template_string
9494
with app.test_request_context():
95-
rv = render('{{ "</script>"|tojson|safe }}')
96-
self.assert_equal(rv, '"<\\/script>"')
97-
rv = render('{{ "<\0/script>"|tojson|safe }}')
98-
self.assert_equal(rv, '"<\\u0000\\/script>"')
99-
rv = render('{{ "<!--<script>"|tojson|safe }}')
100-
self.assert_equal(rv, '"<\\u0021--<script>"')
95+
rv = flask.json.htmlsafe_dumps('</script>')
96+
self.assert_equal(rv, u'"\\u003c/script\\u003e"')
97+
self.assert_equal(type(rv), text_type)
98+
rv = render('{{ "</script>"|tojson }}')
99+
self.assert_equal(rv, '"\\u003c/script\\u003e"')
100+
rv = render('{{ "<\0/script>"|tojson }}')
101+
self.assert_equal(rv, '"\\u003c\\u0000/script\\u003e"')
102+
rv = render('{{ "<!--<script>"|tojson }}')
103+
self.assert_equal(rv, '"\\u003c!--\\u003cscript\\u003e"')
104+
rv = render('{{ "&"|tojson }}')
105+
self.assert_equal(rv, '"\\u0026"')
101106

102107
def test_json_customization(self):
103108
class X(object):

0 commit comments

Comments
 (0)