Prototype Pollution in Adonisjs
How Prototype Pollution Manifests in Adonisjs
Prototype pollution in Adonisjs occurs when untrusted user input can modify JavaScript object prototypes, particularly through the framework's request body parsing and query parameter handling. Adonisjs uses the qs library for parsing query strings, which is vulnerable to prototype pollution when the allowPrototypes option is enabled or when nested objects are improperly handled.
The most common attack vector in Adonisjs applications is through request bodies that contain nested objects with special keys like __proto__, constructor, or prototype. When Adonisjs parses these requests without proper safeguards, an attacker can inject properties into Object.prototype that affect all objects in the application.
Here's a vulnerable Adonisjs controller pattern:
class UserController {
async create({ request }) {
const userData = request.post()
const user = new User()
Object.assign(user, userData) // Vulnerable if userData contains prototype pollution
await user.save()
}
}An attacker could send a request with a body like:
{
"name": "John Doe",
"__proto__": {
"isAdmin": true
}
}This would add an isAdmin property to Object.prototype, making all objects in the application appear as administrators. In Adonisjs, this is particularly dangerous because the framework's Lucid ORM models inherit from base classes, so prototype pollution can affect database queries, authentication checks, and authorization logic.
Another Adonisjs-specific vector is through the framework's validation system. When using validateAll with user-provided schemas or when dynamically constructing validation rules based on request parameters, prototype pollution can bypass security checks:
class ProductController {
async update({ request }) {
const rules = request.input('rules', {}) // User controls validation rules!
const data = await request.validate({
rules
})
// Proceed with potentially malicious data
}
}The framework's middleware system can also be affected. If middleware properties are dynamically set based on request parameters, prototype pollution can modify middleware behavior across the entire application.
Adonisjs-Specific Detection
Detecting prototype pollution in Adonisjs requires both static analysis and runtime scanning. middleBrick's API security scanner specifically targets this vulnerability through its Property Authorization check, which examines how your Adonisjs application handles nested object properties and special keys.
For manual detection in Adonisjs applications, start by examining your route handlers and controller methods. Look for patterns where request data is directly assigned to objects without sanitization:
grep -r "request\.\(post\|all\|only\|except\)" app/Controllers/HttpThen check if these handlers use Object.assign, spread operators, or direct property assignment with the parsed data. Adonisjs's request.post() method returns a plain JavaScript object that's vulnerable if not properly handled.
middleBrick scans for prototype pollution by sending specially crafted payloads to your Adonisjs endpoints. The scanner tests for:
- Modification of
Object.prototypethrough__proto__keys - Prototype chain manipulation via
constructorandprototypeproperties - Symbol-based prototype pollution attempts
- Nested object traversal that could affect prototype properties
The scanner's Input Validation check specifically looks for Adonisjs's use of the qs library and whether your application properly configures it. By default, qs.parse() in Adonisjs can be vulnerable if not configured with allowPrototypes: false.
To test locally, you can use middleBrick's CLI tool:
npx middlebrick scan https://your-adonisjs-app.com/api/usersThis will test your Adonisjs API endpoints for prototype pollution vulnerabilities and provide a security score with specific findings about property authorization issues.
Adonisjs-Specific Remediation
Remediating prototype pollution in Adonisjs requires a multi-layered approach. The most effective solution is to use Adonisjs's built-in sanitization and validation features to prevent malicious data from reaching your application logic.
First, configure Adonisjs to use safe parsing by default. In your config/app.js, ensure that request parsing is configured securely:
module.exports = {
http: {
allowMethodOverride: true,
generateRequestId: true,
trustProxy: false
}
}Then, in your controllers, always use Adonisjs's validation system before processing request data:
class UserController {
async create({ request }) {
const validatedData = await request.validate({
schema: schema.create({
name: schema.string(),
email: schema.string({}, [rules.email()]),
role: schema.enum.optional(['user', 'admin', 'moderator'])
}),
messages: {
'role.enum': 'Invalid role value'
}
})
// Safe to use validatedData - prototype pollution prevented
const user = new User(validatedData)
await user.save()
}
}For cases where you need to merge user data with existing objects, use a safe merge function that prevents prototype pollution:
const safeMerge = (target, source) => {
const result = { ...target }
for (const [key, value] of Object.entries(source)) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue // Skip dangerous keys
}
if (typeof value === 'object' && value !== null) {
result[key] = safeMerge(result[key] || {}, value)
} else {
result[key] = value
}
}
return result
}
// Usage in Adonisjs controller
const userData = request.post()
const safeData = safeMerge({}, userData)
Adonisjs's HttpContext provides additional protection through its request.clean() method, which removes potentially dangerous properties:
const cleanData = request.clean()
// cleanData has dangerous properties removedFor maximum security, implement a global middleware that sanitizes all incoming requests:
class PrototypePollutionProtection {
async handle({ request }, next) {
const originalPost = request.post.bind(request)
request.post = () => {
const data = originalPost()
return this.sanitizeObject(data)
}
await next()
}
sanitizeObject(obj) {
if (typeof obj !== 'object' || obj === null) return obj
const sanitized = Array.isArray(obj) ? [] : {}
for (const [key, value] of Object.entries(obj)) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue
}
sanitized[key] = this.sanitizeObject(value)
}
return sanitized
}
}
// Register in start/kernel.js
const globalMiddleware = [
'Adonis/Core/BodyParser',
'App/Middleware/PrototypePollutionProtection'
]Finally, use middleBrick's continuous monitoring in your Pro plan to automatically scan your Adonisjs API endpoints on a schedule, ensuring that any new prototype pollution vulnerabilities introduced during development are caught before production deployment.