Stack Overflow in Adonisjs
How Stack Overflow Manifests in Adonisjs
In Adonisjs, a stack overflow vulnerability usually appears when user‑supplied data is processed with a recursive algorithm that has no depth limit. Common places include custom validation rules, model hooks, or middleware that walk through nested JSON objects. If an attacker can supply a payload with extreme nesting (e.g., 10 000 levels), the recursive function exhausts the Node.js call stack and the process crashes, leading to a denial‑of‑service.
The following example shows a custom validator rule that recursively validates a nested data field without any depth check:
// start/routes.js
Route.post('/users', 'UsersController.store').middleware(['validator:storeUser'])
// app/Validators/StoreUserValidator.js
const { schema, rules } = require('@ioc:Adonis/Core/Validator')
class StoreUserValidator {
constructor () {
this.schema = schema.create({
profile: schema.object().members({
// Vulnerable recursive walk
data: schema.any({}, [
rules.custom((value, _, { errorReporter, tip }) => {
function walk (obj, depth) {
// No depth limit – will overflow with deep input
if (obj && typeof obj === 'object') {
for (const k in obj) {
walk(obj[k], depth + 1)
}
}
}
walk(value, 0)
})
])
})
})
}
}
module.exports = StoreUserValidator
When a request like the following is sent, the walk function calls itself for each nested level:
{
"profile": {
"data": {
"a": {
"b": {
"c": { … 10 000 levels deep … }
}
}
}
}
}
The Node.js process reaches the maximum call‑stack size, throws a RangeError: Maximum call stack size exceeded, and returns an empty or 500 response to the client.
Adonisjs-Specific Detection
middleBrick’s unauthenticated black‑box scan includes an Input Validation check that actively probes for stack‑overflow conditions. It sends a series of JSON payloads with increasing nesting depth (e.g., 10, 100, 1 000, 10 000 levels) to each discovered endpoint and measures the response.
If the scanner observes:
- A sudden increase in response time (indicating deep recursion)
- An empty response or HTTP 500 with a
RangeErrorin the body (when error details are exposed) - The connection timing out after the default 5‑15 second window
it flags the endpoint as a potential stack‑overflow vulnerability and adds a finding with severity, remediation guidance, and the exact payload that triggered the condition.
Example CLI usage:
middlebrick scan https://api.example.com/users
The output will contain a section similar to:
[INPUT VALIDATION] Potential stack overflow detected
Severity: Medium
Payload: {"profile":{"data":{"a":{"b":{ … 10 000 levels … }}}}}
Remediation: Replace recursive traversal with an iterative approach or enforce a maximum depth.
Because middleBrick does not need agents, credentials, or configuration, the check runs automatically as part of the 5‑15 second scan.
Adonisjs-Specific Remediation
The fix is to replace the recursive walk with an iterative algorithm that uses an explicit stack (or queue) and enforces a maximum depth. This prevents the call stack from growing unbounded while still allowing validation of reasonably nested objects.
Revised validator using an iterative walk:
// app/Validators/StoreUserValidator.js
const { schema, rules } = require('@ioc:Adonis/Core/Validator')
function walkIterative (obj, maxDepth) {
const stack = [{ obj, depth: 0 }]
while (stack.length) {
const { obj, depth } = stack.pop()
if (depth > maxDepth) continue
if (obj && typeof obj === 'object') {
for (const k in obj) {
stack.push({ obj: obj[k], depth: depth + 1 })
}
}
}
}
class StoreUserValidator {
constructor () {
this.schema = schema.create({
profile: schema.object().members({
data: schema.any({}, [
rules.custom((value, _, { errorReporter, tip }) => {
const MAX_DEPTH = 100 // adjust per business needs
walkIterative(value, MAX_DEPTH)
})
])
})
})
}
}
module.exports = StoreUserValidator
Alternative approaches that stay within Adonisjs’s ecosystem:
- Use the built‑in
schema.object()withschema.array()andschema.string()types, avoiding custom recursion altogether. - Leverage a third‑party validation library such as
ajv(which supportsmaxDepth) and call it from a custom rule. - If the data shape is known, define a strict schema with
schema.object().members()and let the validator reject unknown or excessively deep structures before any user code runs.
After deploying the fix, run middleBrick again to confirm that the finding disappears and the API returns a proper validation error (e.g., 422) instead of crashing.