HIGH type confusionfeathersjs

Type Confusion in Feathersjs

How Type Confusion Manifests in Feathersjs

Type confusion in Feathersjs occurs when the framework or application incorrectly interprets data types, allowing attackers to bypass validation, authorization, or business logic. This vulnerability is particularly dangerous in Feathersjs because of its flexible schema handling and dynamic type coercion.

The most common manifestation appears in Feathersjs service methods where parameters are dynamically typed. Consider this vulnerable pattern:

class UsersService {
  async find(params) {
    const query = params.query;
    
    // Vulnerable: No type validation
    const limit = query.$limit;
    
    // If $limit is a string like "5; DROP TABLE users", SQL injection possible
    // If $limit is an object, unexpected behavior occurs
    return this._find(query, params);
  }
}

Feathersjs's loose typing allows several attack vectors:

  • Query Parameter Injection: An attacker sends { $limit: { $ne: 0 } } instead of a number, causing the query to return all records instead of a limited set
  • Boolean Confusion: Sending isActive: "false" (string) vs isActive: false (boolean) can bypass filters that expect strict boolean types
  • Object Injection: Parameters expecting primitives receive objects, leading to prototype pollution or unexpected behavior in service methods

A concrete Feathersjs example:

class OrdersService {
  async get(id, params) {
    // Vulnerable: id could be an object with malicious properties
    const orderId = params.query.id;
    
    // If orderId is { $in: [1,2,3] }, this might return multiple orders
    return this._get(orderId, params);
  }
}

The framework's dynamic nature means type confusion can also occur in:

  • Hook chains where data types change between hooks
  • Event listeners expecting specific payload structures
  • Feathersjs client applications where TypeScript types aren't enforced at runtime

Another critical area is Feathersjs's authentication system. Type confusion in JWT verification can lead to authentication bypass:

const authService = {
  async verifyJWT(token) {
    // Vulnerable: No strict type checking on decoded payload
    const decoded = jwt.decode(token);
    
    // If decoded.sub is an object instead of string/number, authorization fails
    return decoded.sub;
  }
};

Feathersjs-Specific Detection

Detecting type confusion in Feathersjs requires both static analysis and runtime testing. The framework's dynamic nature makes traditional type checking insufficient.

Static Analysis Patterns:

// Vulnerable pattern: No type validation
function processPayment(amount) {
  // If amount is { $gt: 0 }, unexpected behavior occurs
  return this._process(amount);
}

// Safe pattern: Strict type checking
function processPayment(amount) {
  if (typeof amount !== 'number' || isNaN(amount)) {
    throw new Error('Invalid amount type');
  }
  return this._process(amount);
}

Runtime Testing with middleBrick:

middleBrick's black-box scanning approach is particularly effective for Feathersjs type confusion detection. The scanner tests unauthenticated endpoints by sending malformed type payloads:

{
  "endpoint": "/api/users",
  "test_cases": [
    { "name": "Number confusion", "payload": { "$limit": {"$ne": 0} } },
    { "name": "Boolean confusion", "payload": { "isActive": "false" } },
    { "name": "Object injection", "payload": { "id": {"$in": [1,2,3]} } }
  ]
}

middleBrick specifically checks for:

  • Unexpected response codes when sending malformed types
  • Database query behavior changes with injected operators
  • Authentication bypass through type confusion in JWT verification
  • Prototype pollution via object injection in query parameters

Custom Feathersjs Detection:

class TypeSafeService {
  constructor() {
    this.typeValidators = {
      id: (value) => typeof value === 'string' || typeof value === 'number',
      limit: (value) => typeof value === 'number' && value > 0 && value < 100,
      isActive: (value) => typeof value === 'boolean'
    };
  }

  validateType(field, value) {
    if (!this.typeValidators[field]) return true;
    return this.typeValidators[field](value);
  }

  async find(params) {
    const query = params.query;
    
    // Validate all query parameters
    Object.keys(query).forEach(field => {
      if (!this.validateType(field, query[field])) {
        throw new Error(`Invalid type for ${field}`);
      }
    });

    return this._find(query, params);
  }
}

middleBrick's OpenAPI analysis also helps detect type confusion by comparing your service definitions with actual runtime behavior, identifying mismatches between documented and implemented types.

Feathersjs-Specific Remediation

Remediating type confusion in Feathersjs requires a defense-in-depth approach combining strict typing, validation, and secure coding practices.

1. Strict Type Validation Middleware:

const typeValidationHook = context => {
  const { method, params } = context;
  const query = params.query || {};
  
  // Define expected types
  const typeSchema = {
    id: 'string|number',
    limit: 'number',
    skip: 'number',
    isActive: 'boolean',
    sort: 'object'
  };

  Object.keys(query).forEach(field => {
    const expectedType = typeSchema[field];
    if (!expectedType) return; // Allow unknown fields
    
    const value = query[field];
    const actualType = typeof value;
    
    // Check type matches
    if (!expectedType.split('|').includes(actualType)) {
      throw new Error(`Invalid type for ${field}: expected ${expectedType}, got ${actualType}`);
    }
    
    // Additional object validation
    if (actualType === 'object') {
      // Prevent MongoDB operator injection
      if (Object.keys(value).some(key => key.startsWith('$'))) {
        throw new Error('Object injection detected');
      }
    }
  });

  return context;
};

2. Feathersjs Service Type Safety:

class SecureUsersService {
  async find(params) {
    const query = params.query || {};
    
    // Strict type conversion
    const limit = this.safeParseInt(query.$limit, 10);
    const skip = this.safeParseInt(query.$skip, 0);
    const isActive = this.safeParseBoolean(query.isActive);
    
    // Only allow expected query parameters
    const allowedFields = ['$limit', '$skip', 'isActive', 'email'];
    const sanitizedQuery = {};
    
    allowedFields.forEach(field => {
      if (query[field] !== undefined) {
        sanitizedQuery[field] = query[field];
      }
    });

    return this._find(sanitizedQuery, params);
  }

  safeParseInt(value, defaultValue) {
    const num = parseInt(value, 10);
    return isNaN(num) ? defaultValue : num;
  }

  safeParseBoolean(value) {
    if (typeof value === 'boolean') return value;
    if (typeof value === 'string') {
      return value.toLowerCase() === 'true';
    }
    return !!value;
  }
}

3. TypeScript Integration:

interface UserQuery {
  $limit?: number;
  $skip?: number;
  email?: string;
  isActive?: boolean;
  sort?: Record;
}

class TypeSafeUsersService {
  async find(params: { query: UserQuery }) {
    const { query } = params;
    
    // TypeScript ensures compile-time type safety
    if (query.$limit !== undefined) {
      if (typeof query.$limit !== 'number') {
        throw new Error('$limit must be a number');
      }
    }

    return this._find(query, params);
  }
}

4. Input Sanitization Hook:

const sanitizeInputHook = context => {
  const { params } = context;
  const query = params.query || {};
  
  // Remove potentially dangerous operators
  const dangerousOperators = ['$where', '$eval', '$mapReduce', '$function'];
  
  dangerousOperators.forEach(op => {
    if (query[op]) {
      delete query[op];
      console.warn(`Removed dangerous operator: ${op}`);
    }
  });
  
  // Prevent prototype pollution
  if (query.__proto__ || query.constructor) {
    delete query.__proto__;
    delete query.constructor;
  }

  return context;
};

5. Comprehensive Testing:

describe('Type Safety', () => {
  it('should reject object injection', async () => {
    const service = new SecureUsersService();
    
    await expect(
      service.find({ query: { id: { $in: [1,2,3] } } })
    ).rejects.toThrow('Invalid type');
  });

  it('should handle boolean confusion', async () => {
    const service = new SecureUsersService();
    
    const result = await service.find({ 
      query: { isActive: 'true' } 
    });
    
    expect(result).toBeDefined();
  });
});

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 attacks in Feathersjs?
Type confusion exploits type coercion and loose typing rather than syntax injection. While SQL injection manipulates query structure, type confusion manipulates data types to bypass validation or trigger unexpected behavior. For example, sending { $limit: { $ne: 0 } } as an object instead of a number can cause Feathersjs to return all records instead of a limited set, without any SQL syntax involved.
Can middleBrick detect type confusion in my Feathersjs API?
Yes, middleBrick's black-box scanning specifically tests for type confusion by sending malformed type payloads to your Feathersjs endpoints. It checks for unexpected responses when sending objects instead of primitives, boolean string confusion, and operator injection attempts. The scanner runs 12 security checks in parallel and provides specific findings with severity levels and remediation guidance for type confusion vulnerabilities.