HIGH email injectionadonisjsfirestore

Email Injection in Adonisjs with Firestore

Email Injection in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability

Email injection occurs when user-controlled input is improperly sanitized before being used in email-related operations, such as message headers or recipient fields. In an AdonisJS application that uses Google Cloud Firestore as a persistence layer, this risk arises when application code builds email headers or query values using unchecked user input stored in or retrieved from Firestore documents.

AdonisJS does not inherently sanitize email-related inputs; developers must explicitly validate and encode values. When Firestore documents contain user-supplied fields—such as username, email, or display_name—these values may be reused in outbound email logic (e.g., notifications or password resets). If the application directly interpolates these Firestore fields into headers like To, Cc, or Subject, an attacker can inject additional headers or newline characters (%0a, %0d) to redirect email or inject malicious content.

Consider a scenario where Firestore stores a user profile with an email field, and the app sends a welcome email using that value:

  • No validation on the Firestore-stored email allows an attacker to register with an address like attacker@example.com%0aCc: victim@example.com.
  • If the application later reads this value from Firestore and uses it in a raw email header without sanitization, the injected Cc line causes the message to be sent to the unintended recipient.

Because Firestore is often used to store configuration or template data (e.g., email templates or allowed sender domains), an insecure read path can compound the issue. For example, a compromised Firestore document containing a malicious template could lead to server-side template injection or header manipulation when the template is rendered and sent via email.

The risk is heightened when Firestore documents are used to drive bulk email operations (e.g., newsletters or batch notifications) without strict schema enforcement. Missing validation on array fields or nested objects in Firestore documents may allow injection payloads to persist and later be reflected in email headers.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

Remediation focuses on strict input validation, output encoding, and controlled usage of Firestore data in email workflows. Always treat data from Firestore as untrusted, even if it was originally written by your application.

1. Validate and normalize email values before storing in Firestore

Use AdonisJS schema validation to enforce email format and disallow control characters. When writing to Firestore, ensure values conform to a safe pattern.

import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { DateTime } from 'luxon'
import { Firestore, collection, addDoc } from 'firebase/firestore'

const emailSchema = schema.create({
  email: schema.string({}, [
    rules.email(),
    rules.maxLength(255),
    // Reject newlines and carriage returns at the source
    rules.regex(/^[^\r\n]+$/)
  ]),
  displayName: schema.string.optional({}, [
    rules.maxLength(100),
    rules.regex(/^[^\r\n]*$/)
  ])
})

export async function createUserProfile(db: Firestore, payload: any) {
  const validated = await emailSchema.validate(payload)
  const usersCollection = collection(db, 'users')
  await addDoc(usersCollection, {
    email: validated.email.toLowerCase().trim(),
    displayName: validated.displayName?.trim(),
    createdAt: DateTime.local().toISO()
  })
}

2. Encode email values when building headers in outbound messages

When constructing email headers, use a dedicated email library that handles encoding and does not allow raw newline injection. Avoid string concatenation for header lines.

import { MailManager } from '@ioc:Adonis/Addons/Mail'
import { Firestore, doc, getDoc } from 'firebase/firestore'

export async function sendWelcomeEmail(db: Firestore, userId: string) {
  const docSnap = await getDoc(doc(db, 'users', userId))
  if (!docSnap.exists()) {
    throw new Error('User not found')
  }
  const data = docSnap.data()

  // Use the mail library's API to set recipient and subject safely
  await Mail.use((message) => {
    message
      .to(data.email) // Adonis mail library encodes the address appropriately
      .subject('Welcome to our platform')
      .htmlView('emails/welcome', { user: data.displayName })
  }).send()
}

3. Enforce allowlists for sender domains and use Firestore security rules

Restrict Firestore documents to known sender domains. Combine Firestore security rules with application logic to prevent unauthorized updates to email-related fields.

// Firestore security rule snippet (conceptual)
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow update: if request.resource.data.email is string
        && request.resource.data.email.matches('.*@(example|mail)\.com$')
        && request.resource.data.email == request.resource.data.email.lowercase();
    }
  }
}

4. Sanitize data used in email templates and avoid direct header assembly

If you generate email content from Firestore-stored templates, treat them as code artifacts and validate their structure. Do not allow raw user input to dictate header lines.

import { Firestore, doc, getDoc } from 'firebase/firestore'

interface Template {
  subject: string
  body: string
  allowedSender: string
}

export async function getVerifiedTemplate(db: Firestore, templateId: string): Promise