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) vsisActive: 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 ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |