Find the SSTI in a lodash template built from user input
This Node.js code builds a lodash template string from a user-supplied name and then renders it. Identify the vulnerability and how it reaches code execution, then describe the fix.
Constraints:
nameis attacker-controlled;escapeHTMLonly escapes HTML special characters- lodash
_.templatecompiles the string into a function and runs the embedded${...}
const compiled = _.template("Hello " + escapeHTML(name) + ".");
const html = compiled(data);
Diagnose the cause.
Server-side template injection — name becomes part of the template source, and escapeHTML only neutralizes HTML, not template syntax. Any ${...} expression is compiled and executed server-side, giving RCE. Fix: pass name as data to a fixed template, never build the template from input.
- ✗Treating escapeHTML as protection against template-syntax injection
- ✗Confusing SSTI with reflected XSS (execution on the server, not in the browser)
- ✗Building the template string from user input
- →Why doesn't HTML escaping prevent execution of the ${...} expression?
- →What's the difference between passing input as data versus as template text?
The vulnerability
name is concatenated into the template source, not passed as data:
const compiled = _.template("Hello " + escapeHTML(name) + ".");
escapeHTML encodes <, >, &, but leaves the ${...} template syntax alone. So any ${...} expression in the input survives to compilation and runs on the server with the process's privileges — this is SSTI, leading to RCE. The code smell: a template string assembled by concatenating a user value; output escaping does not help here, because it protects the browser, not the template compiler.
The fix
Pass input as data to a fixed template so it is never interpreted as code:
const tpl = _.template("Hello <%- n %>.");
const html = tpl({ n: name }); // name is data, not template text
✅ The template text is now constant; <%- %> safely interpolates name as a value. ⚠️ Any concatenation of input into the template string reintroduces the flaw.