What security measures does Shopify use in Liquid templates to prevent XSS attacks?

I’ve observed that Shopify incorporates automatic escaping in their Liquid templates to protect against cross-site scripting (XSS) vulnerabilities. However, I couldn’t find this feature in the standard liquid gem.

Here’s what I’m looking at:

Template example: <span class="{{ product_class }}">{{ product_class }}</span>

User input: alert('malicious code')" onload="

Output from Shopify:

<span class="alert('malicious code')&quot; onload=&quot;">alert('malicious code')" onload="</span>

Output from Standard Liquid gem:

<span class="alert('malicious code')" onload="">alert('malicious code')" onload="</span>

Example Ruby code I tested:

template_string = '<span class="{{ product_class }}">{{ product_class }}</span>'
liquid_template = Liquid::Template.parse(template_string)

result = liquid_template.render!('product_class' => 'alert(\'malicious code\')" onload="')

How does Shopify achieve this? I’m aware of the escape filter and the need for backend escaping, but their method seems to provide default protection without the hassle of adding filters manually, making it much tidier to use {{ value }} instead of {{ value | escape }} each time.

Does anyone know how they implemented this automatic escaping?

Shopify forked the standard Liquid gem and built in automatic HTML escaping. Their version automatically encodes dangerous characters like quotes and angle brackets during template compilation - every variable gets escaped unless you explicitly mark it as safe. The original Liquid gem doesn’t do this because it’s meant to work with any output format (JSON, plain text, etc.) where HTML escaping would break things. Want the same protection in your app? You’ll need to extend Liquid with custom auto-running filters or wrap the rendering process to sanitize output afterward. Some devs use custom Liquid drops or modify the render context to get automatic sanitization.

for sure! shopify’s engine really helps by auto-escaping stuff, which is super handy. they must have changed things up so users can chill on the manual escaping. django’s kinda got this down too. with regular liquid, tho, you’d gotta do some tweaking to get that safety.