Description
Replacing a template engine's HTML escape function with one that returns input unchanged prevents automatic escaping of special characters. In Mustache.js, overriding Mustache.escape this way can let attacker-controlled content become executable HTML and cause cross-site scripting (XSS).
Potential impact
- Injected scripts may run in a user's browser.
- Data accessible to those scripts, including unprotected cookies or session information, may be stolen.
- Attackers may alter the displayed page and undermine trust in the service.
Remediation
- Keep the template's default escaping when inserting values into HTML text.
- Do not replace
Mustache.escapewith a function that returns input unchanged. - Validate and encode values for their actual output context, such as URLs, JavaScript or CSS. HTML escaping does not protect every context.
Examples
Before
javascript
const Mustache = require('mustache');
// BAD: Override HTML escaping to return the input unchanged
Mustache.escape = function(text) {
return text;
};
const unsafeTemplate = '<div>{{userInput}}</div>';
const html = Mustache.render(unsafeTemplate, { userInput: req.query.text });
After
javascript
const Mustache = require('mustache');
// GOOD: Keep the default Mustache escaping
const safeTemplate = '<div>{{userInput}}</div>';
const html = Mustache.render(safeTemplate, { userInput: req.query.text });
The first excerpt inserts unescaped input into the page. The second escapes special characters in this HTML text position. The excerpts assume separate executions: calling require again does not restore an already overridden global function.