Mass Assignment in Adonisjs with Firestore
Mass Assignment in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability
Mass assignment occurs when an API accepts user-provided input and directly maps it to model or document fields without explicit allowlisting. In Adonisjs, this commonly happens when controller methods pass request body data straight into model creation or update routines. When the persistence layer is Google Cloud Firestore, the risk pattern shifts slightly because Firestore operates on documents and explicit field paths rather than relational ORM instances, but the core issue remains: unchecked input can write to sensitive or unintended fields.
Adonisjs does not perform schema-level field filtering by default; it relies on developers to define which fields are fillable or guarded. If a developer mistakenly marks sensitive fields as fillable (or omits proper guards), and the code then uses something like User.create(body) or merges request body into a Firestore document reference, an attacker can inject fields such as isAdmin, role, or permissions. Because Firestore rules are not a substitute for application-level mass assignment protection, these injected fields may be persisted and later used in authorization checks, leading to privilege escalation or data exposure.
With Firestore, documents are often structured with nested maps and dynamic keys. If an Adonisjs service deserializes request JSON and writes it directly into a document path like users/{userId} without pruning unknown keys, an attacker can add or modify nested fields (e.g., metadata/role or settings/payment/enabled). The combination of Adonisjs’s flexible request handling and Firestore’s schemaless document model increases the attack surface. Additionally, if the application uses Firestore batched writes or server-side admin SDKs with elevated privileges, a mass assignment vector can lead to changes that bypass intended business logic or validation layers.
Real-world attack patterns include authentication bypass via injected admin flags, data exfiltration through writable nested fields, and unauthorized configuration changes. These map to OWASP API Top 10 A01:2023 broken object level authorization when IDOR is involved, and can be surfaced by middleBrick’s BOLA/IDOR and Property Authorization checks. middleBrick scans detect whether endpoints accept extensive body payloads and then attempt to assign unexpected fields to sensitive resources, highlighting where mass assignment may occur in an Adonisjs + Firestore integration.
Firestore-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on explicit field allowlisting, avoiding direct passthrough of request bodies, and validating data before it reaches Firestore. Below are concrete patterns for Adonisjs that reduce risk when working with Cloud Firestore.
- Use a DTO or schema to whitelist allowed fields. For example, with Joi or Adonisjs schema compiler, define permitted fields and transform input before writing to Firestore.
import { schema, rules } from '@ioc:Adonis/Core/Validator'
const userSchema = schema.create({
type: 'object',
props: {
email: schema.string.optional(),
name: schema.string.optional(),
role: schema.enum.optional(['user', 'editor']),
// explicitly exclude sensitive fields like isAdmin
},
})
export const UserValidator = {
schema: userSchema,
messages: {
'role.enum': 'The role field must be one of: user, editor.',
},
}
- When writing to Firestore, reference only allowed fields by mapping them explicitly rather than forwarding the raw body. This prevents nested or unexpected keys from being written.
import { Firestore } from '@google-cloud/firestore'
import { DateTime } from 'luxon'
const firestore = new Firestore()
export async function createUser(uid: string, input: Record) {
const userRef = firestore.collection('users').doc(uid)
const allowed = {
email: input.email,
name: input.name,
role: input.role || 'user',
updatedAt: DateTime.local().toISO(),
}
await userRef.set(allowed, { merge: true })
return allowed
}
- For updates, prefer explicit field paths and avoid merging entire objects. If you must merge, first read the existing document and selectively overwrite only vetted fields.
import { Firestore } from '@google-cloud/firestore'
const firestore = new Firestore()
export async function updateUserProfile(userId: string, input: Partial<Record<'email' | 'name', string>>) {
const userRef = firestore.collection('users').doc(userId)
const doc = await userRef.get()
if (!doc.exists) {
throw new Error('User not found')
}
const current = doc.data()
const updated = {
email: input.email ?? current.email,
name: input.name ?? current.name,
updatedAt: new Date().toISOString(),
}
await userRef.update(updated)
return updated
}
- Enforce Firestore security rules as a secondary control, but do not rely on them alone. Rules should mirror the allowlist defined in your Adonisjs validators. For example, ensure only known fields are writable.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null
&& request.resource.data.keys().hasAll(['email', 'name', 'role', 'updatedAt'])
&& request.resource.data.keys().size() == 4;
}
}
}
These steps ensure that even if an API endpoint accepts broader input, only vetted fields propagate into Firestore. middleBrick’s API scans can surface endpoints that accept large payloads and attempt to assign unexpected fields, helping you identify where additional hardening is required.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |