Security and Escaping Notes¶
This page is an honest account of what {{ }}/{{{ }}} interpolation does and doesn't escape for
you, so you know when a template needs to help. If you're the one deciding what data or template
sources are trusted in the first place, see Rendering Untrusted Content
for the other half of this topic.
HTML escaping: what's covered, what isn't¶
{{ identifier }} HTML-escapes its output; {{{ identifier }}} does not. That much matches every
other Mustache/Handlebars-family engine. The part worth reading carefully is what gets escaped:
Escaping only happens when the resolved value is itself a string primitive. Numbers, booleans, and
date-times render as plain text with no HTML metacharacters, so they need no escaping and get none. A
struct or list interpolated directly — {{ someObject }} where someObject resolves to an
object or list, not a single scalar — renders as a compact JSON-like dump ({"name":"<script>"})
with no escaping applied to the string values inside it, even though the tag is the "escaped" {{
}} form. If a context value the template interpolates whole (rather than by drilling into individual
fields) can contain attacker-controlled strings, that's an HTML-injection vector {{ }} does not
close for you. Drill into fields explicitly ({{ someObject/name }}) when the source is untrusted,
rather than dumping the container.
{{ user }} {# {"name":"<script>alert(1)</script>"} — NOT escaped, container path #}
{{ user/name }} {# <script>alert(1)</script> — escaped, scalar path #}
escape/escape_once are not equivalent to {{ }}'s automatic escaping — they're ordinary
string transforms you apply explicitly, useful inside {{{ }}} or when
composing a value before it's placed somewhere other than HTML body text (an HTML attribute, for
example, still needs the value quoted correctly by the surrounding template — kBars escapes the five
XML/HTML metacharacters & < > " ', which covers quoted-attribute contexts but not unquoted attributes
or <script>/<style> bodies, the same caveat that applies to every escaper of this kind).
strip_html is a regex-based tag stripper, not a sanitizer. It removes <script>/<style>
blocks, HTML comments, and remaining tags with a fixed, non-user-supplied set of patterns — good for
cleaning up incidental markup in a description field, not a substitute for a real HTML sanitizer if
you need to accept and safely re-render arbitrary rich-text HTML from users.
Summary¶
| Concern | Bounded automatically? |
|---|---|
| HTML-escaping a scalar string value | Yes, automatically for {{ }} |
| HTML-escaping a struct/list dumped whole | No — drill into fields instead |
| Non-HTML output contexts (JS, SQL, shell, unquoted-attribute) | No — escape explicitly with a transform, or restructure the template |
Where to next¶
- Rendering Untrusted Content — recursion limits, output-size bounds, and other properties that are the responsibility of whoever builds the application rendering your templates.
- String Transforms —
escape,escape_once,strip_html,url_escape,url_param_escape. - Partials — how a partial reference resolves.