Sandbox Escape in Adonisjs
How Sandbox Escape Manifests in Adonisjs
Adonisjs, like other Node.js frameworks, provides a robust sandboxing environment through its Context (ctx) object and middleware system. However, sandbox escape vulnerabilities in Adonisjs typically manifest through improper validation of user-controlled data that bypasses the framework's security boundaries.
One common pattern occurs when developers use ctx.request.input() without proper type validation. Adonisjs's request.input() method automatically casts certain values, which can lead to unexpected type coercion. For example, passing an array where a string is expected might bypass validation logic:
// Vulnerable: Type coercion can bypass validation
const userId = ctx.request.input('user_id'); // Could be string, number, or array
const user = await User.find(userId); // Array might be converted to string '1,2,3'
Another manifestation involves the ctx.request.all() method, which merges query parameters, body, and route parameters. An attacker can exploit this by crafting requests that override expected values:
// Vulnerable: Parameter pollution
const { id } = ctx.request.all(); // Query param ?id=1 overrides route param :id
const user = await User.find(id); // Might return wrong user
Adonisjs's Lucid ORM, while powerful, can also be a vector for sandbox escape when dynamic queries are constructed without proper sanitization. The framework's query builder allows for dynamic field selection, which if not validated, can expose unintended data:
// Vulnerable: Dynamic field selection without validation
const fields = ctx.request.input('fields', 'id,name');
const user = await User.query().select(fields.split(','));
The framework's validate() middleware, while useful, can be bypassed if developers don't properly define schemas or if they rely on client-side validation. Adonisjs's validation rules are powerful but require explicit configuration:
// Vulnerable: Missing validation rules
const { username, role } = await ctx.request.validate({
username: 'required|string',
// Missing role validation allows any value
});
Adonisjs-Specific Detection
Detecting sandbox escape vulnerabilities in Adonisjs applications requires both static code analysis and dynamic runtime testing. The framework's structure provides several key areas to examine.
Static analysis should focus on route handlers that use ctx.request.input(), ctx.request.all(), and dynamic query building. Look for patterns where user input directly influences database queries, file paths, or object properties without validation:
# Check for vulnerable patterns in route files
grep -r "ctx.request.input" routes/ | grep -v "validate"
grep -r "query().select" app/Controllers/ | grep -v "validate"
Dynamic testing with middleBrick can identify these vulnerabilities by scanning your Adonisjs API endpoints. The scanner tests for common sandbox escape patterns specific to Node.js frameworks:
# Scan your Adonisjs API with middleBrick
middlebrick scan https://your-api.com/api/users
middleBrick's black-box scanning tests for parameter pollution, type coercion vulnerabilities, and dynamic query injection patterns. It specifically checks for:
- Parameter pollution attacks that might override route parameters
- Type coercion vulnerabilities in input handling
- Dynamic query building without proper validation
- Unvalidated field selection in database queries
- Missing authorization checks in route handlers
- Unsafe consumption of external data sources
The scanner also analyzes your OpenAPI/Swagger specification if available, comparing the documented API contract with actual runtime behavior to identify discrepancies that might indicate sandbox escape vulnerabilities.
Adonisjs-Specific Remediation
Remediating sandbox escape vulnerabilities in Adonisjs requires leveraging the framework's built-in security features and following secure coding practices. Here are Adonisjs-specific solutions:
First, always use the validate() middleware with explicit schemas. Adonisjs's validator provides powerful type checking and sanitization:
// Secure: Explicit validation with schema
const { validate } = use('Validator');
const schema = {
userId: 'required|integer|range:1,999999',
fields: 'optional|array|in:id,name,email,created_at'
};
const { userId, fields } = await validate(ctx.request.all(), schema);
const user = await User.query().select(fields).where('id', userId).first();
For dynamic queries, use Adonisjs's query builder with whitelisted fields rather than raw input:
// Secure: Whitelisted field selection
const allowedFields = ['id', 'name', 'email', 'created_at'];
const requestedFields = ctx.request.input('fields', 'id,name');
const fields = requestedFields.split(',').filter(field => allowedFields.includes(field));
const user = await User.query().select(fields).where('id', ctx.request.input('id')).first();
Use Adonisjs's parameter binding to prevent SQL injection and type coercion issues:
// Secure: Parameter binding
const userId = ctx.request.input('user_id', 'number'); // Explicit type casting
const user = await User.query().where('id', userId).first(); // Safe binding
Implement proper authorization checks using Adonisjs's auth middleware and policies:
// Secure: Authorization middleware
Route.get('/api/users/:id', 'UserController.show')
.middleware(['auth', 'can:view,user'])
.validator('GetUser');
Configure Adonisjs's security middleware to prevent common attack vectors:
// app/Http/kernel.js
const globalMiddleware = [
'Adonis/Middleware/AllowMethod',
'Adonis/Middleware/CORS',
'Adonis/Middleware/BodyParser',
'Adonis/Middleware/Session',
'Adonis/Middleware/Auth',
'App/Middleware/ValidateInput', // Custom validation middleware
];
For production deployments, integrate middleBrick's continuous monitoring into your CI/CD pipeline to catch sandbox escape vulnerabilities before deployment:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run middleBrick Scan
run: middlebrick scan https://staging.your-api.com --fail-below B
continue-on-error: true