Injection Flaws in Adonisjs with Mongodb
Injection Flaws in Adonisjs with Mongodb — how this specific combination creates or exposes the vulnerability
AdonisJS is a Node.js web framework that encourages an MVC or layered architecture and often uses an Object Document Mapper (ODM) layer to interact with databases, including MongoDB. When working with MongoDB through an ODM or direct driver usage, injection flaws arise if developer-supplied input is concatenated into queries or aggregation pipelines without proper validation, sanitization, or parameterization.
In AdonisJS, a common pattern is to build query conditions from request payloads, for example using Lucid models or raw MongoDB drivers. If these inputs are not strictly constrained, an attacker can inject operators or documents that change the semantic intent of the query. For instance, instead of filtering by a known user identifier, an attacker may supply { "$ne": null } or use nested operators to bypass intended filters.
Specific to MongoDB, injection typically manifests through the misuse of operators such as $where, $eval, or improper use of aggregation stages like $match with uncontrolled strings. If an application dynamically builds an aggregation pipeline using request data and pushes stages without validation, it may allow unintended operations, including data extraction or modification. This is especially risky when the application exposes filtering or sorting parameters that are directly passed to the database without type checking or allowlisting.
AdonisJS applications that expose raw query-building endpoints — for example, a search route that forwards query parameters directly to a MongoDB collection — are vulnerable if those parameters are not sanitized. Attackers may attempt to use JSON-based injection to modify query logic, escalate privileges by injecting conditions that bypass authentication checks, or probe for sensitive collections through error messages returned by malformed queries.
The LLM/AI Security checks in middleBrick highlight risks like system prompt leakage and prompt injection, which are conceptually similar: untrusted input influencing control flow or data exposure. In the context of AdonisJS and MongoDB, the same principle applies — untrusted input must never dictate query structure or pipeline stages.
Mongodb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict input validation, using parameterized queries, and avoiding dynamic concatenation of operators. Prefer AdonisJS ODM constructs where possible, and if using the raw MongoDB driver, validate and sanitize all inputs before building queries or aggregation pipelines.
Example 1: Safe filtering with AdonisJS Lucid model (which internally uses parameterized queries) and explicit schema validation.
import User from 'App/Models/User';
import { schema } from '@ioc:Adonis/Core/Validator';
const userFilterSchema = schema.create({
email: schema.string.optional({ trim: true }),
status: schema.enum(['active', 'inactive']).optional()
});
export async function listUsers(ctx) {
const validated = await validator.validate({ schema: userFilterSchema, data: ctx.request.qs });
const users = await User.query()
.where((query) => {
if (validated.email) {
query.where('email', validated.email);
}
if (validated.status) {
query.where('status', validated.status);
}
})
.exec();
return users;
}
Example 2: Using the MongoDB driver safely with parameterized aggregation in AdonisJS, avoiding injection via uncontrolled $match stages.
import { Db } from 'mongodb';
export async function getFilteredProducts(db: Db, filters: Record) {
// Validate and sanitize filters before using them in an aggregation pipeline
const allowedFields = new Set(['category', 'minPrice', 'inStock']);
const safeMatch: Record = {};
for (const [key, value] of Object.entries(filters)) {
if (allowedFields.has(key) && value !== undefined) {
safeMatch[key] = value;
}
}
const pipeline = [
{ $match: safeMatch },
{ $sort: { createdAt: -1 } }
];
const results = await db.collection('products').aggregate(pipeline).toArray();
return results;
}
Example 3: Avoiding dynamic operator injection by whitelisting allowed operators and rejecting raw user-provided operator objects.
interface QueryInput {
field: string;
operator: '$eq' | '$in' | '$gt' | '$lt';
value: unknown;
}
export function buildSafeQuery(input: QueryInput) {
const allowedOperators = new Set(['$eq', '$in', '$gt', '$lt']);
if (!allowedOperators.has(input.operator)) {
throw new Error('Invalid operator');
}
// Use parameterized query building instead of eval-like constructs
const query: Record = {
[input.field]: {
[input.operator]: input.value
}
};
return query;
}
Example 4: Rejecting untrusted pipeline stages and using allowlists for sort fields.
export function buildSafeAggregation(sortField: string, sortOrder: 1 | -1) {
const allowedSortFields = new Set(['name', 'price', 'createdAt']);
if (!allowedSortFields.has(sortField)) {
throw new Error('Invalid sort field');
}
return [
{ $sort: { [sortField]: sortOrder } },
{ $limit: 50 }
];
}
These approaches ensure that query structure is not dictated by untrusted input, mitigating injection risks while maintaining the flexibility to query MongoDB safely within AdonisJS applications.