Formula Injection in Adonisjs with Firestore
Formula Injection in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
Formula Injection occurs when an attacker-supplied value is interpreted as a formula or expression by a downstream system that evaluates or renders content in a spreadsheet-like context. In a web application built with AdonisJS that uses Google Firestore as a backend, this typically happens when user-controlled data is stored in Firestore and later exported or rendered in a spreadsheet (for example, via a generated CSV or an integration with Google Sheets). If the stored data begins with characters such as =, +, -, or @, and that data is placed into a CSV file or a Google Sheet without proper escaping, spreadsheet applications may interpret it as a formula and execute it in the context of the user opening the file.
In the AdonisJS + Firestore scenario, the risk emerges through the application’s data flow: user input is accepted by an AdonisJS controller, written to Firestore (often without strict schema validation on string content), and later retrieved for export. If the export routine produces CSV or inserts values directly into a Google Sheet via an API, the raw user input is treated as spreadsheet content. A value like =HYPERLINK("https://evil.example") or =1+2 in a cell can trigger network calls or unwanted calculations when the sheet is opened. Because Firestore itself does not evaluate formulas, the vulnerability is not in Firestore but in how the exported data is consumed by downstream systems that do. The combination of AdonisJS handling user input and Firestore storing and serving that data to export workflows creates a path where untrusted data reaches a context that interprets it as code.
Consider a route in AdonisJS that accepts a displayName and saves it to Firestore:
import { DateTime } from 'luxon'
import User from 'App/Models/User'
export default class UsersController {
public async store({ request, response }) {
const payload = request.only(['displayName', 'email'])
const user = await User.create(payload)
return response.status(201).json(user)
}
}
If displayName contains a formula-like string and the application later exports all displayName values to CSV for a Google Sheet integration, the CSV will contain the raw string. Opening the CSV in a spreadsheet application may cause the formula to execute. Attackers might use this to phish credentials (e.g., =HYPERLINK("https://attacker.example")) or to perform unwanted actions (e.g., =1+2 changing cell appearance or behavior). This illustrates why input validation and output encoding must account for the downstream consumption format, not just the database layer.
Additionally, if the Firestore data is used to generate pre-filled Google Sheets via Google Apps Script or the Google Sheets API, and the AdonisJS backend passes user data directly into cell values without escaping, the formula injection risk persists. Firestore stores the string as-is; the vulnerability manifests when the export or integration layer treats the string as executable content. This underscores the importance of context-aware output handling: sanitizing data based on whether it will appear in CSV, a sheet cell, a JSON API response, or an HTML page.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
To mitigate Formula Injection when using Firestore with AdonisJS, apply context-specific escaping and validation at the boundaries where data enters and leaves your system. Do not rely on Firestore to neutralize formulas; handle escaping in your AdonisJS code before data is exported or rendered in spreadsheets.
1. Input validation and normalization
Validate and, if appropriate, normalize user input that could be used in exports. For display names or free-text fields, consider stripping or encoding leading formula indicators when the data will be exported to CSV or Sheets. You can implement a small sanitizer utility in AdonisJS:
// app/Helpers/sanitizeForSpreadsheet.ts
export function escapeCsvValue(value: string): string {
// If the value starts with =, +, -, or @, prepend a space or a tab to prevent formula evaluation
if (/^[=\+\-@]/.test(value)) {
return ' ' + value
}
return value
}
Use this helper when preparing data for export:
import { escapeCsvValue } from 'App/Helpers/sanitizeForSpreadsheet'
export const userDataForCsv = users.map(user => ({
email: user.email,
displayName: escapeCsvValue(user.displayName || '')
}))
2. Safe CSV generation
When generating CSV output in AdonisJS, ensure fields containing user-controlled content are properly quoted and escaped. Use a robust CSV library rather than string concatenation to avoid injection issues around commas and quotes as well:
import { stringify } from 'csv-stringify/sync'
const records = userDocs.map(doc => ({
email: doc.email,
displayName: escapeCsvValue(doc.displayName || '')
}))
const csv = stringify(records, { header: true, columns: ['email', 'displayName'] })
return response.type('text/csv').send(csv)
3. Google Sheets integration safety
If you use the Google Sheets API from AdonisJS to write values, explicitly set values as strings and avoid using formulas in the payload. The Google Sheets API allows you to control how data is interpreted via the valueInputOption. Use USER_ENTERED with caution — it can parse formulas — and prefer RAW when you want to store exactly what you send:
import { google } from 'googleapis'
const sheets = google.sheets({ version: 'v4', auth })
await sheets.spreadsheets.values.update({
auth,
spreadsheetId,
range: 'Sheet1!A2:C2',
valueInputOption: 'RAW', // Prevents formula evaluation on write
requestBody: {
values: [['[email protected]', ' ' + displayName]] // prepend space if needed
}
})
When pre-filling forms or generating links, if you must preserve the original display value, store the raw value in Firestore but encode or transform it at render time for contexts that interpret formulas. This keeps Firestore as a source of truth while neutralizing execution risks at the export or rendering layer.
Finally, document and test the export workflow with sample formula-like inputs to confirm that spreadsheets open safely and no remote code execution or unwanted calculations occur. Regularly review integration points between Firestore and downstream systems to ensure escaping remains appropriate as consumption contexts evolve.