RSTR-SSTI-002 — Node template compile from request input
Summary
A Node template engine (pug, handlebars, ejs, mustache, …)
compiles a template string built from request input. The attacker
controls the template source, not just the data passed into a fixed
template — so they can execute arbitrary code through engine
internals, and SSTI usually escalates straight to RCE.
This is the Node counterpart of RSTR-SSTI-001.
Severity
High.
Languages
JavaScript, TypeScript.
What rastray flags
const tmpl = pug.compile(req.body.template); // ← flagged
handlebars.compile(req.query.body); // ← flagged
ejs.render(req.body.tpl, {}); // ← flagged
What rastray deliberately does not flag
- Rendering a fixed file:
pug.renderFile('views/index.pug', { data }). - Compiling a constant string at module load time.
ejs.render(fixedTemplate, dataFromReq)where the template is a literal.
How to fix it
Templates are code. Treat them like code: ship them with the application, never accept them from clients. Always render a fixed template and pass user input as data:
// templates/email.ejs lives in the repo
const html = await ejs.renderFile(
path.join(__dirname, 'templates/email.ejs'),
{ name: req.body.name, link: req.body.link }
);
If the product really needs user-provided "templates" (mail-merge,
report builder), use a sandboxed micro-syntax — not a full template
engine. Strict mustache (no helpers, no {{{ raw }}}) is the safest
pre-built option.