Skip to content

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 }}      {# &lt;script&gt;alert(1)&lt;/script&gt; — 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 Transformsescape, escape_once, strip_html, url_escape, url_param_escape.
  • Partials — how a partial reference resolves.