'Escape strings for JavaScript using Jinja2?

How do I escape HTML with Jinja2 so that it can be used as a string in JavaScript (jQuery)?

If I were using Django's templating system I could write:

$("#mydiv").append("{{ html_string|escapejs }}");

Django's |escapejs filter would escape things in html_string (eg quotes, special chars) that could break the intended use of this code block, but Jinja2 does not seem to have an equivalent filter (am I wrong here?).

Is there a cleaner solution than copying/pasting the code from Django?



Solution 1:[1]

Jinja2 has nice filter tojson. If you make json from string, it will generate string enclosed in double quotes "". You can safely use it in javascript. And you don't need put quotes around by yourself.

$("#mydiv").append({{ html_string|tojson }});

It also escapes <>& symbols. So it is safe even when sting contains something XSS dangerous like <script>

Solution 2:[2]

This is a escapejs filter, based on Django's one, that I wrote for use in Jinja2 templates:

_js_escapes = {
        '\\': '\\u005C',
        '\'': '\\u0027',
        '"': '\\u0022',
        '>': '\\u003E',
        '<': '\\u003C',
        '&': '\\u0026',
        '=': '\\u003D',
        '-': '\\u002D',
        ';': '\\u003B',
        u'\u2028': '\\u2028',
        u'\u2029': '\\u2029'
}
# Escape every ASCII character with a value less than 32.
_js_escapes.update(('%c' % z, '\\u%04X' % z) for z in xrange(32))
def jinja2_escapejs_filter(value):
        retval = []
        for letter in value:
                if _js_escapes.has_key(letter):
                        retval.append(_js_escapes[letter])
                else:
                        retval.append(letter)

        return jinja2.Markup("".join(retval))
JINJA_ENVIRONMENT.filters['escapejs'] = jinja2_escapejs_filter

Example safe usage in a template:

<script type="text/javascript">
<!--
var variableName = "{{ variableName | escapejs }}";
…
//-->
</script>

When variableName is a str or unicode.

Solution 3:[3]

I just researched this problem, my solution is to define a filter:

from flask import Flask, Markup
app = Flask(__name__)
app.jinja_env.filters['json'] = lambda v: Markup(json.dumps(v))

and in the template:

<script>
var myvar = {{myvar|json}} ;
</script>

This has the nice feature that myvar can be anything that can be JSON-serialised

Solution 4:[4]

Based on @tometzky here is my Python 3 version:

_js_escapes = {
        '\\': '\\u005C',
        '\'': '\\u0027',
        '"': '\\u0022',
        '>': '\\u003E',
        '<': '\\u003C',
        '&': '\\u0026',
        '=': '\\u003D',
        '-': '\\u002D',
        ';': '\\u003B',
        u'\u2028': '\\u2028',
        u'\u2029': '\\u2029'
}
# Escape every ASCII character with a value less than 32.
_js_escapes.update(('%c' % z, '\\u%04X' % z) for z in range(32))

@register.filter
def escapejs(value):
    return jinja2.Markup("".join(_js_escapes.get(l, l) for l in value))

The usage is exactly the same.

Solution 5:[5]

You can also use jinja2's autoescape. So, for instance, you could add autoescape to your jinja2 Environment in Python:

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    autoescape=True)

Alternatively, you could use the Autoescape Extension added in Jinja 2.4 to have more control over where the autoescaping is used in the HTML. More information on this here and example (in Google App Engine) here.

Python:

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions=['jinja2.ext.autoescape'])

HTML:

{% autoescape true %}
    <html>
        <body>
            {{ IWillBeEscaped }}
        </body>
    </html>
{% endautoescape %}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Tometzky
Solution 3 Mike Richardson
Solution 4 Sebastian Wagner
Solution 5 Tony Wickham