Guide 03 · Developer docs

The theme repo and the asset pipeline

Everything a developer changes on a Core dna site lives in its theme repository. This guide covers the repository and its build, printing records in templates, stylesheets and client-side code, and the parts of a site around the main content.

L1–L2
Unit 02 · Records in templates

Escaping output

On Core dna neither Smarty nor Twig escapes output by default: every value prints exactly as it is held. Escaping for each place a value can go, in both engines, the few cases where raw output is right, and the two questions to ask before every raw print.

Who this is for. Every theme developer, and every reviewer of template changes. Afterwards you escape each value for the place it is printed, recognise the values that must never be printed raw, and can review a template for injection in minutes. The full comparison of the two engines' escaping is on Smarty and Twig side by side.

The default: no escaping, in either engine

The platform configures Twig with auto-escaping off and leaves Smarty's off as well. {$value} and {{ value }} both print exactly what they hold, and Twig's |raw and Smarty's nofilter change nothing. Developers who know Twig from other frameworks expect the opposite; a template ported from one carries no escaping because it never needed any there.

Escape for the place you print

Printed intoSmartyTwig
HTML text|escape|e
An attribute value, in quotes|escape (quotes included)|e('html_attr')
A query-string parameter|escape:'url'|e('url')
JavaScript, or a JSON block|json_encode|json_encode
Template code
<h1>{$page->getName()|escape}</h1>
<a href="/search?q={$term|escape:'url'}" title="{$title|escape}">Search again</a>
<script nonce="{$_inlineScriptNonce}">
  window.pageData = {['title' => $page->getName(), 'path' => $page->getFullPath()]|json_encode};
</script>
<h1>{{ page.getName()|e }}</h1>
<a href="/search?q={{ term|e('url') }}" title="{{ title|e('html_attr') }}">Search again</a>
<script nonce="{{ _inlineScriptNonce }}">
  window.pageData = {{ {'title': page.getName(), 'path': page.getFullPath()}|json_encode }};
</script>

For JavaScript, prefer json_encode to escaping a string and wrapping it in quotes yourself: it produces a complete, valid value of any type, and it escapes /, so a value containing </script> cannot end the script block.

When raw output is right

  • Rich text from an editor, such as a Richtext area. It is HTML on purpose, written by people the site trusts with its markup.
  • Markup the platform renders: a component, a layout area's HTML, a form.
  • Output of json_encode, which is already safe for its context.

Never print raw anything a visitor controls: the query string, the path, the hostname and every other value from _request, a search term, a form value echoed back, a member's own profile fields. Those are the values an attacker chooses.

The review questions

Before every output that is not escaped, ask:

  1. Where did this value come from? An editor, a visitor, the request, another system through an import, or your own code.
  2. Where am I printing it? HTML text, an attribute, a URL, a script, a style, JSON.

If the first answer is anyone but an editor or your own code, escape it for the second answer. A reviewer can find every candidate by searching the changed templates for {$ and {{ without a following |escape, |e or |json_encode.

Porting from Twig elsewhere, or between the engines

A template from a framework with auto-escaping prints unescaped here. A Smarty template ported to Twig, or back, keeps whatever escaping it had: neither engine adds any. Check output after every port.

Go deeper

Was this page helpful?

Your answer goes to the docs team.