HIGH type confusionadonisjs

Type Confusion in Adonisjs

How Type Confusion Manifests in Adonisjs

Type confusion in Adonisjs occurs when the framework's type coercion and dynamic property access mechanisms allow attackers to manipulate input data types, causing the application to misinterpret values and execute unintended logic paths. This vulnerability is particularly dangerous in Adonisjs because of its flexible request handling and schema validation system.

The most common Adonisjs-specific manifestation involves the framework's request body parsing and schema validation. When Adonisjs processes incoming requests, it automatically parses JSON payloads and assigns them to the request object. If an endpoint expects a specific type but receives a different one, the framework's coercion rules can lead to unexpected behavior.

class UserController {
  async update({ request, response }) {
    const data = request.post()
    
    // Expects a number but receives a string
    const userId = data.id
    const user = await User.find(userId)
    
    // Type confusion: string '0' becomes falsy, skipping authorization
    if (!user || user.id === 0) {
      return response.notFound()
    }
    
    // Further processing with potentially wrong user object
  }
}

Another Adonisjs-specific scenario involves Lucid model operations where type confusion can lead to BOLA (Broken Object Level Authorization) vulnerabilities. When using dynamic where clauses with user-provided values, incorrect type handling can bypass authorization checks.

class ProfileController {
  async updateProfile({ request, response, auth }) {
    const data = request.post()
    const profileId = data.profile_id
    
    // Type confusion vulnerability
    const profile = await Profile.query()
      .where('id', profileId)
      .where('user_id', auth.user.id)
      .first()
    
    // If profileId is '0' or falsy string, query returns first profile
    // regardless of user_id, bypassing authorization
    if (!profile) {
      return response.notFound()
    }
    
    await profile.merge(data).save()
    return response.ok(profile)
  }
}

Adonisjs's schema validation system can also introduce type confusion when using loose validation rules. The framework's ability to coerce types during validation can lead to situations where validated data doesn't match the expected runtime types.

class UpdateUserValidator {
  get rules() {
    return {
      age: 'number|min:0', // Accepts numeric strings
      email: 'email',
      isAdmin: 'boolean' // Accepts 'true'/'false' strings
    }
  }
}

// Validator usage
class UserController {
  async update({ request, response }) {
    const data = await request.validate(UpdateUserValidator)
    
    // data.age is now a number, but what if validation failed?
    // Type confusion can occur if validation rules are too permissive
    const user = await User.find(data.id)
    
    // isAdmin might be a string 'true' instead of boolean true
    if (data.isAdmin === true) {
      user.role = 'admin'
    }
    
    await user.save()
    return response.ok(user)
  }
}

Adonisjs-Specific Detection

Detecting type confusion in Adonisjs applications requires examining both the request handling patterns and the validation logic. middleBrick's API security scanner includes specific checks for Adonisjs applications that analyze runtime behavior and schema validation patterns.

middleBrick scans Adonisjs endpoints by sending crafted payloads with type variations to identify where the application's type coercion creates security gaps. The scanner tests for common Adonisjs patterns like numeric string injection, boolean string manipulation, and falsy value exploitation.

# Using middleBrick CLI to scan Adonisjs API
npm install -g middlebrick
middlebrick scan https://api.example.com/users --target adonisjs

# Or integrate into Adonisjs project
middlebrick scan http://localhost:3333 --watch

The scanner specifically looks for Adonisjs-specific vulnerabilities including:

  • Request body type coercion in route handlers
  • Loose schema validation rules that accept multiple types
  • Dynamic query building with user-controlled parameters
  • Model finder methods with type-ambiguous inputs

middleBrick's LLM security features also detect if your Adonisjs application serves AI endpoints that might be vulnerable to prompt injection through type confusion in AI model parameters.

For manual detection in Adonisjs applications, examine these patterns:

// Check for loose validation rules
class LooseValidator {
  get rules() {
    return {
      id: 'number',           // Should be 'integer' or 'range'
      status: 'boolean',      // Should validate boolean specifically
      amount: 'number'        // Missing min/max constraints
    }
  }
}

// Check for unsafe dynamic queries
class UnsafeController {
  async find({ request }) {
    const id = request.input('id', 0)
    
    // Type confusion risk: id could be string, array, or object
    const user = await User.find(id)
    
    return user
  }
}

middleBrick's continuous monitoring (Pro plan) can automatically rescan your Adonisjs APIs on a schedule, alerting you when new type confusion vulnerabilities are introduced through code changes.

Adonisjs-Specific Remediation

Remediating type confusion in Adonisjs requires strict type validation and careful handling of user inputs. Adonisjs provides several native features to prevent type confusion vulnerabilities.

First, use strict validation rules in your validators. Adonisjs's schema validation system allows you to specify exact types and reject ambiguous inputs.

class StrictValidator {
  get rules() {
    return {
      id: 'integer|required',           // Rejects numeric strings
      age: 'integer|min:0|max:150',     // Strict integer validation
      email: 'email|required',
      isActive: 'boolean|required'      // Only true/false accepted
    }
  }
  
  // Custom sanitization for additional safety
  get sanitizationRules() {
    return {
      id: 'toInt',
      age: 'toInt'
    }
  }
}

// Usage in controller
class UserController {
  async update({ request, response }) {
    try {
      const data = await request.validate(StrictValidator)
      
      // Now data.id is guaranteed to be an integer
      const user = await User.find(data.id)
      
      if (!user) {
        return response.notFound()
      }
      
      await user.merge(data).save()
      return response.ok(user)
    } catch (error) {
      return response.badRequest({
        message: 'Invalid input data',
        details: error.messages
      })
    }
  }
}

Second, implement explicit type checking in your route handlers. Adonisjs's TypeScript support helps catch many type issues at compile time, but runtime validation is still necessary.

class SafeController {
  async show({ request, response }) {
    const id = request.input('id')
    
    // Explicit type validation
    if (typeof id !== 'number' && !Number.isInteger(Number(id))) {
      return response.badRequest({ error: 'ID must be an integer' })
    }
    
    const userId = Number(id)
    const user = await User.find(userId)
    
    if (!user) {
      return response.notFound()
    }
    
    return response.ok(user)
  }
  
  async create({ request, response }) {
    const data = request.post()
    
    // Validate object structure before processing
    if (!data || typeof data !== 'object') {
      return response.badRequest({ error: 'Invalid payload' })
    }
    
    // Check for unexpected properties
    const allowedKeys = ['name', 'email', 'age']
    const unknownKeys = Object.keys(data).filter(
      key => !allowedKeys.includes(key)
    )
    
    if (unknownKeys.length > 0) {
      return response.badRequest({
        error: 'Unexpected properties',
        keys: unknownKeys
      })
    }
    
    // Safe to process validated data
    const user = await User.create(data)
    return response.created(user)
  }
}

Third, use Adonisjs's query builder safely by avoiding dynamic type coercion in where clauses.

class SecureProfileController {
  async updateProfile({ request, response, auth }) {
    const data = request.post()
    
    // Explicit validation of profile ID
    const profileId = data.profile_id
    if (typeof profileId !== 'number' && !Number.isInteger(Number(profileId))) {
      return response.badRequest({ error: 'Invalid profile ID' })
    }
    
    const userId = auth.user.id
    
    // Use explicit type conversion and validation
    const profile = await Profile.query()
      .where('id', Number(profileId))
      .where('user_id', userId)
      .first()
    
    if (!profile) {
      return response.notFound()
    }
    
    await profile.merge(data).save()
    return response.ok(profile)
  }
}

// For boolean parameters, use explicit parsing
class FeatureController {
  async toggleFeature({ request, response }) {
    const featureId = request.input('feature_id')
    const enabled = request.input('enabled')
    
    // Strict boolean parsing
    const isEnabled = enabled === 'true' || enabled === true || enabled === '1'
    const isDisabled = enabled === 'false' || enabled === false || enabled === '0'
    
    if (!isEnabled && !isDisabled) {
      return response.badRequest({ error: 'Enabled must be boolean' })
    }
    
    const feature = await Feature.find(featureId)
    feature.enabled = isEnabled
    await feature.save()
    
    return response.ok(feature)
  }
}

Finally, implement comprehensive error handling to prevent type confusion from causing information disclosure or unexpected application states.

// Global error handler for type-related issues
class TypeConfusionHandler {
  async handle(error, { response }) {
    if (error.code === 'E_VALIDATION_FAILURE') {
      return response.badRequest({
        message: 'Validation failed',
        errors: error.messages
      })
    }
    
    if (error instanceof TypeError) {
      // Log the error but don't expose internal details
      console.error('Type error:', error)
      return response.internalServerError({
        message: 'Internal server error'
      })
    }
    
    return response.internalServerError()
  }
}

// Register in start/events.js
Event.on('error', async (error, { response }) => {
  const handler = new TypeConfusionHandler()
  await handler.handle(error, { response })
})

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How does type confusion differ from traditional injection vulnerabilities in Adonisjs?
Type confusion in Adonisjs specifically exploits the framework's type coercion and dynamic property access mechanisms, whereas traditional injection vulnerabilities typically involve malicious data being interpreted as code. Type confusion tricks the application into misinterpreting data types, leading to authorization bypasses, data exposure, or logic manipulation. For example, a string '0' being treated as falsy can bypass authorization checks, while traditional injection would involve SQL injection or XSS payloads.
Can middleBrick detect type confusion vulnerabilities in Adonisjs applications?
Yes, middleBrick's API security scanner includes specific checks for Adonisjs applications that test for type confusion vulnerabilities. The scanner sends crafted payloads with type variations to identify where Adonisjs's type coercion creates security gaps. It examines request body parsing, schema validation patterns, and dynamic query building to detect vulnerabilities. The Pro plan also offers continuous monitoring that automatically rescans your APIs on a schedule to catch new type confusion issues introduced through code changes.