Information Disclosure in Adonisjs
How Information Disclosure Manifests in Adonisjs
Adonisjs, like any Node.js framework, can leak sensitive data when error handling, configuration, or model serialization is not carefully managed. One common vector is the default error renderer that returns full stack traces and request payloads when the DEBUG environment variable is set to true. An attacker who triggers a validation error or a failed database query can receive a response that includes file paths, line numbers, and even snippets of the application source code.
Another frequent source is the automatic serialization of Lucid models. When a controller returns a model instance directly (e.g., return await User.find(id)), Adonisjs will JSON‑serialize the entire object, including hidden attributes such as password_hash, token, or any custom getters that expose internal state. If the model defines a toJSON method that does not filter fields, those values end up in the API response.
Misconfigured environment variables also pose a risk. Adonisjs loads variables from .env into process.env and makes them accessible via the Env service. If a route mistakenly returns Env.get('APP_KEY') or Env.get('DB_PASSWORD'), the secret is exposed to anyone who can call that endpoint. This pattern has been observed in several open‑source Adonisjs projects and has led to credential leakage incidents tracked as CVEs in the broader Node.js ecosystem (e.g., CVE-2020-7659, which, while a prototype‑pollution issue, often results in information disclosure when exploited).
Finally, debug‑level logging can write request bodies, headers, or query strings to files or stdout that are later served staticly or collected by log‑aggregation tools. If those logs are accessible via a misconfigured static route, an attacker can harvest API keys, session tokens, or personally identifiable information (PII). These behaviors map to OWASP API Security Top 10 items A3:2019 (Excessive Data Exposure) and A6:2019 (Security Misconfiguration).
Adonisjs-Specific Detection
Detecting information disclosure in an Adonisjs API starts with observing what the service returns under error conditions. You can manually probe endpoints with invalid parameters, malformed JSON, or non‑existent IDs and look for stack traces, file paths, or unexpected data fields in the response body.
For a repeatable, automated check, middleBrick’s black‑box scanner can be pointed at the base URL of your Adonisjs service. The tool performs the following relevant checks without any agents or credentials:
- Sends a variety of malformed requests to trigger error responses and scans the returned bodies for stack trace patterns,
Errorobjects, orprocess.envleakage. - Examines successful responses for over‑exposed model attributes by comparing field names against a list of known sensitive identifiers (e.g.,
password,secret,token,key). - Looks for debug‑mode indicators such as the
X-Powered-By: AdonisJsheader combined with verbose error messages. - Checks for exposed configuration endpoints (e.g., routes that inadvertently return
Env.getvalues) by probing common paths like/config,/env, or/debug.
You can run a scan from the terminal with the middleBrick CLI:
# Install the CLI once
npm i -g middlebrick
# Scan an Adonisjs API (replace with your URL)
middlebrick scan https://api.example.com
The output includes a severity‑graded finding for each information‑disclosure issue, a short description, and remediation guidance. In CI/CD pipelines, the GitHub Action can be configured to fail a build if the score drops below a chosen threshold:
# .github/workflows/api-security.yml
name: API Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick scan
uses: middlebrick/action@v1
with:
api-url: https://staging.example.com
fail-below: B # fail if score is B or lower
Because middleBrick works unauthenticated, it evaluates the public attack surface exactly as an external attacker would see it.
Adonisjs-Specific Remediation
The first line of defense is to disable debug mode in production. Set DEBUG=false in your .env file or override it via the hosting platform’s environment variables. This prevents Adonisjs from rendering detailed error pages.
Next, implement a centralized exception handler that returns a generic error message. In start/exceptions.js you can override the default renderer:
// start/exceptions.js
const { HttpContext } = require('@adonisjs/core/build/standalone')
class CustomExceptionHandler {
async handle(error, { response }) {
// Log the error internally for debugging
this.logger.error(error)
// Return a safe payload to the client
return response.status(error.status || 500).json({
error: 'Something went wrong. Please try again later.'
})
}
}
module.exports = CustomExceptionHandler
To avoid over‑exposing model data, never return a Lucid model directly from a controller. Instead, map the instance to a plain object that contains only the fields you intend to share. Adonisjs provides a helpful $visible array on the model, but the most explicit approach is to use a Data Transfer Object (DTO) or a simple pick:
// app/Controllers/Http/UserController.js
const User = use('App/Models/User')
class UserController {
async show ({ params, response }) {
const user = await User.findOrFail(params.id)
// Select only safe attributes
const safe = user.$toJSON()
delete safe.password_hash
delete safe.token
delete safe.remember_token
return response.json(safe)
}
}
module.exports = UserController
If you prefer to keep the model clean, define a toJSON method that filters out sensitive fields:
// app/Models/User.js
const Model = use('Model')
class User extends Model {
static get hidden () {
return ['password_hash', 'token', 'remember_token']
}
}
module.exports = User
Finally, audit all routes that might inadvertently expose environment variables. Remove any Env.get calls from controller return values, and if you need to expose non‑secret configuration, create a dedicated, whitelisted endpoint that returns only the allowed keys:
// app/Controllers/Http/ConfigController.js
const Env = use('Env')
class ConfigController {
public async show ({ response }) {
const allowed = {
app_name: Env.get('APP_NAME'),
version: Env.get('APP_VERSION', '1.0.0')
}
return response.json(allowed)
}
}
module.exports = ConfigController
After applying these changes, run another middleBrick scan to verify that the information‑disclosure findings have been resolved. Continuous monitoring (available on the Pro plan) will automatically rescan your APIs on a schedule and alert you if any new exposure appears.
Frequently Asked Questions
Does middleBrick need any credentials or agents to scan my Adonisjs API?
How can I make sure my Adonisjs API does not leak stack traces in production?
DEBUG=false in your environment, replace the default exception handler with a custom one that logs errors internally and returns a generic JSON error message to the client, and verify the change with a middleBrick scan.