HIGH server side template injectionsailsfirestore

Server Side Template Injection in Sails with Firestore

Server Side Template Injection in Sails with Firestore — how this specific combination creates or exposes the vulnerability

Server Side Template Injection (SSTI) occurs when an attacker can inject template expressions that are subsequently evaluated by the server-side rendering engine. In a Sails.js application that uses Firestore as a data store, SSTI can arise when user-controlled input is passed into a template helper or partial and combined with Firestore document reads or writes. Sails ships with support for multiple view engines (e.g., EJS, Pug), and if a developer uses a non‑escaped template expression to inject a Firestore document path or query parameters, the injected code can alter the intended query or cause arbitrary code execution within the template context.

Consider a route that renders a user profile by reading from Firestore using a document ID taken directly from req.params. If the view template embeds that parameter without sanitization, an attacker can supply a path that includes template syntax. For example, an EJS template might include <%- user.name %> where user is populated from Firestore using an ID like attacker@example.com%20%7C%20console.log(process.pid). Depending on how the Sails controller passes the document snapshot into the view, the injected expression can execute in the template scope, leading to information disclosure or server-side behavior changes. Even when using Firestore’s safe get() calls, the vulnerability surface exists if the template logic itself is influenced by attacker input.

Because Firestore operations in Sails often involve asynchronous data retrieval, the risk is compounded when developers chain template logic with Firestore results. An attacker may leverage SSTI to probe environment variables or internal functions available in the template context, especially when custom helpers are registered globally. The interaction between Sails’ controller/view flow and Firestore’s document‑based model can unintentionally expose these vectors if input validation and output encoding are inconsistent. This is not a flaw in Firestore itself, but a design issue where untrusted data reaches the rendering layer without proper escaping or strict allow‑lists.

To detect this class of issue, middleBrick runs checks that analyze OpenAPI specifications and runtime behavior for unsafe data flow into template contexts. It flags scenarios where user input influences template selection or helper execution and highlights missing output encoding. The scanner does not modify code; it reports findings with severity, evidence, and remediation guidance to help developers close the gap.

Firestore-Specific Remediation in Sails — concrete code fixes

Remediation focuses on strict input validation, avoiding direct concatenation of user input into Firestore paths or queries, and ensuring all template output is properly escaped. Below are concrete examples that demonstrate secure patterns when using Firestore with Sails.js.

First, validate and sanitize document identifiers before using them in Firestore operations. Use an allow‑list for expected formats and reject any input containing template characters or unexpected segments.

const userId = req.param('userId');
if (!/^[a-zA-Z0-9_-]{3,32}$/.test(userId)) {
  return res.badRequest('Invalid user identifier');
}
const userDoc = await sails.firestore.collection('users').doc(userId).get();
if (!userDoc.exists) {
  return res.notFound();
}
return res.view('profile', { user: userDoc.data() });

Second, when rendering data in templates, always use the view engine’s auto‑escaping or explicit escaping. For EJS, prefer <%= %> over <%- %> unless you fully trust the content. If you must render rich content, sanitize it with a library such as DOMPurify on the client side after receiving the data.

<!-- In EJS, auto‑escape user fields -->
<div>Name: <%= user.name %></div>
<div>Email: <%= user.email %></div>

Third, avoid passing raw Firestore query parameters derived from user input. Instead, map allowed sort fields to known column names and enforce strict ordering on the server side.

const allowedSort = { createdAt: 'createdAt', updatedAt: 'updatedAt' };
const sortBy = allowedSort[req.query.sort] || 'createdAt';
const snapshot = await sails.firestore.collection('messages')
  .orderBy(sortBy, 'desc')
  .limit(50)
  .get();
const messages = snapshot.docs.map(d => d.data());
return res.view('messages', { messages });

These patterns reduce the risk of SSTI by ensuring that user input never directly shapes executable template logic or Firestore query structure. middleBrick’s scans can verify that such controls are in place and that no unsafe concatenation appears in the request‑to‑template path.

Frequently Asked Questions

Can SSTI in Sails with Firestore lead to remote code execution?
Yes, if user input reaches a server-side template without escaping and the template engine evaluates JavaScript, an attacker can execute arbitrary code. Always escape output and avoid dynamic template selection based on untrusted data.
Does Firestore itself prevent SSTI because it is a NoSQL database?
Firestore does not evaluate template syntax; the risk comes from how application code passes data into templates. Proper input validation and output encoding in Sails views are required to prevent SSTI.