CRITICAL adonisjsmass assignment exploit

Mass Assignment Exploit in Adonisjs

How Mass Assignment Exploit Manifests in Adonisjs

Mass assignment exploits in Adonisjs occur when user-controlled input is directly bound to model properties without explicit allowlisting, enabling attackers to modify sensitive fields like is_admin, role, or balance. Adonisjs uses Lucid ORM, and mass assignment typically happens in controller methods when request.only() or request.except() is omitted, or when request.all() is passed directly to create() or merge().

For example, consider a user registration endpoint in an Adonisjs controller:

// Controllers/UserController.js
async store ({ request, response }) {
  const userData = request.all() // DANGEROUS: binds all input fields
  const user = await User.create(userData)
  return response.created(user)
}

If the User model has a is_admin column, an attacker can send { "email": "attacker@example.com", "password": "secret", "is_admin": true } to escalate privileges. This maps to OWASP API Security Top 10:2023 API1:2023 – Broken Object Level Authorization (BOLA) when combined with IDOR, but mass assignment itself is a distinct flaw enabling unauthorized property changes.

Another common pattern is during profile updates where developers mistakenly use request.all() without filtering:

// Controllers/ProfileController.js
async update ({ params, request, response }) {
  const user = await User.findOrFail(params.id)
  user.merge(request.all()) // UNSAFE: allows overwriting any column
  await user.save()
  return response.ok(user)
}

Here, an attacker could modify email to take over another user's account or set balance in a financial API. Adonisjs does not enable guarded attributes by default, unlike some frameworks, making explicit protection necessary.

Adonisjs-Specific Detection

Detecting mass assignment in Adonisjs requires scanning for patterns where user input is bound to Lucid models without field restriction. middleBrick identifies this by analyzing API endpoints for unsafe data flow from request to model operations. It checks for calls to request.all(), request.only() with insufficient fields, or request.except() missing critical exclusions, followed by Model.create(), model.merge(), or model.fill().

For instance, middleBrick flags endpoints where:

  • The request body is not validated via Adonisjs validators (request.validate()) before model binding
  • No $fillable or $guarded arrays are defined in the Lucid model
  • Input is passed directly to model methods without sanitization

Consider this vulnerable model lacking guards:

// Models/User.js
class User extends Model {
  static get table () {
    return 'users'
  }
  // Missing $fillable or $guarded — all columns are mass-assignable
}
module.exports = User

middleBrick correlates runtime behavior (e.g., accepting unexpected fields in POST /users) with static code patterns. It does not require source code access; instead, it sends probing requests with atypical parameters (like is_admin, role) and observes if they are persisted in the response or affect behavior. If a field like is_admin is reflected or leads to privilege changes, it reports a mass assignment finding with severity based on impact (e.g., critical if it enables admin takeover).

This detection aligns with OWASP API Security Top 10 API6:2023 – Unrestricted Access to Sensitive Business Flows, as mass assignment often bypasses intended business logic by altering object state directly.

Adonisjs-Specific Remediation

Fixing mass assignment in Adonisjs involves explicitly defining which attributes are mass-assignable using Lucid’s built-in $fillable or $guarded properties, combined with input validation via Adonisjs validator. Never rely on client-side filtering.

First, define guards in your model. For a User model, specify safe fields:

// Models/User.js
class User extends Model {
  static get table () {
    return 'users'
  }

  static get fillable () {
    return ['username', 'email', 'password'] // Only allow these
  }

  // OR use guarded:
  // static get guarded () {
  //   return ['is_admin', 'id', 'created_at', 'updated_at']
  // }
}
module.exports = User

With $fillable set, user.merge(request.all()) will ignore is_admin even if sent. However, combining this with explicit validation is stronger:

// Controllers/UserController.js
import { schema, rules } from '@ioc:Adonis/Core/Validator'

async store ({ request, response }) {
  const userSchema = schema.create({
    email: schema.string({}, [
      rules.email(),
      rules.unique({ table: 'users', column: 'email' })
    ]),
    password: schema.string({}, [
      rules.minLength(8)
    ])
    // Note: is_admin is NOT in schema — rejected if sent
  })

  const payload = await request.validate({ schema: userSchema })
  const user = await User.create(payload)
  return response.created(user)
}

This ensures only email and password are processed. The validator strips unknown fields by default when using schema.create() without allowUnknowns. For updates, use similar validation:

async update ({ params, request, response }) {
  const user = await User.findOrFail(params.id)

  const updateSchema = schema.create({
    email: schema.string.optional({}, [rules.email()]),
    password: schema.string.optional({}, [rules.minLength(8)])
  })

  const payload = await request.validate({ schema: updateSchema })
  user.merge(payload)
  await user.save()

  return response.ok(user)
}

middleBrick validates fixes by rescanning the endpoint and confirming that previously accepted sensitive fields (like is_admin) are now ignored or rejected. It reports remediation success when the API no longer reflects unauthorized changes in responses or behavior. This approach prevents OWASP API6 issues by enforcing strict input boundaries at the model and validation layers.

Frequently Asked Questions

Does enabling $fillable in Adonisjs models automatically prevent all mass assignment attacks?
No. While $fillable restricts which attributes can be mass-assigned, it does not validate input format or presence. Attackers could still send valid-looking data for fillable fields (e.g., a malicious email). Always combine $fillable/$guarded with Adonisjs validator for schema-based input validation to ensure both field safety and data integrity.
Can middleBrick detect mass assignment in Adonisjs if the API uses custom setter methods instead of direct model binding?
Yes. middleBrick detects mass assignment by observing whether unexpected parameters influence the API’s behavior or response, regardless of internal implementation. If a setter like setIsAdminValue() is called via mass assignment (e.g., through request.all() merging into a model), and it alters state or output, middleBrick’s active probing will identify the abnormal effect and report it as a finding.