MEDIUM information disclosureadonisjs

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, Error objects, or process.env leakage.
  • 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: AdonisJs header combined with verbose error messages.
  • Checks for exposed configuration endpoints (e.g., routes that inadvertently return Env.get values) 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?
No. middleBrick performs unauthenticated, black‑box testing; you only need to provide the public URL of the endpoint. No agents, configuration files, or credentials are required.
How can I make sure my Adonisjs API does not leak stack traces in production?
Set 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.