Back to Blog
critical SEVERITY6 min read

How Exposed Debug Endpoints Happen in Express.js and How to Fix It

A critical security vulnerability in `routes.js` exposed a `/test` endpoint in production without any authentication or authorization checks, potentially allowing attackers to gather system information and reconnaissance data. The fix restricts this debugging endpoint to non-production environments only, preventing unauthorized access while preserving development functionality.

O
By Orbis AppSec
Published September 4, 2026Reviewed September 4, 2026

Answer Summary

This is an authentication/authorization bypass vulnerability (CWE-306) in Express.js where a debugging endpoint (`/test`) was exposed in production without authentication checks. The fix adds an environment-based access control that returns a 404 error when `NODE_ENV` is set to `production`, ensuring the endpoint is only accessible during development. This prevents attackers from using the endpoint for reconnaissance and system information gathering.

Vulnerability at a Glance

cweCWE-306 (Missing Authentication for Critical Function)
fixAdd environment-based access control to return 404 in production
riskAttackers can access debugging functionality, gather system information, and perform reconnaissance
languageJavaScript (Node.js/Express.js)
root causeDebug endpoint not restricted by environment or authentication checks in production
vulnerabilityExposed Debug Endpoint Without Authentication

How Exposed Debug Endpoints Happen in Express.js and How to Fix It

Introduction

In a production Express.js application, the routes.js file contained a /test endpoint designed for ISBN search testing—but it was accessible to anyone, anywhere, without any authentication or authorization checks. This wasn't a subtle bug; it was a critical security gap that could allow attackers to discover system capabilities, gather reconnaissance information, and potentially chain this information into more sophisticated attacks.

The vulnerability existed at line 56 in routes.js, where the route handler for /test was defined without any middleware to verify the requester's identity or authorization level. In a web service where remote attackers can reach every endpoint, this is a direct, exploitable vulnerability.

The Vulnerability Explained

What Went Wrong

Debug and test endpoints are common in development—they help developers verify functionality, test integrations, and troubleshoot issues. The problem occurs when these endpoints make it into production without proper access controls.

Here's the vulnerable code from routes.js:

/**
 * Runs through tests for the ISBN search
 * @param {Object} req The request to parse
 * @param {Object} res The response to send
 */
router.get('/test', async (req, res) => {
  res.send(await catalogues.testIsbnSearch())
})

The Problem: This endpoint accepts requests from anyone. There's no authentication middleware, no authorization check, no environment validation—nothing. An attacker can simply make a GET request to /test and receive whatever catalogues.testIsbnSearch() returns.

Why This Is Dangerous

Debug endpoints typically expose:
- System information: Node.js version, environment configuration, loaded modules
- Internal functionality: Test cases that reveal how the system works
- Catalog structure: In this case, the ISBN search implementation details
- Error messages: Stack traces and internal error handling that aid reconnaissance

An attacker could:
1. Reconnaissance: Call /test to understand the application's capabilities and structure
2. Vulnerability mapping: Learn which libraries and versions are in use
3. Logic discovery: Understand the ISBN search algorithm to find edge cases or bypasses
4. Chaining attacks: Use this information to craft more targeted attacks against authenticated endpoints

Attack Scenario

An attacker scanning your production domain discovers the /test endpoint through common enumeration techniques (directory fuzzing, GitHub repository analysis, etc.). They make a simple request:

curl https://production-api.example.com/test

The response exposes the ISBN search test results, potentially revealing:
- Database structure
- Search algorithm behavior
- Error handling patterns
- System configuration details

This information becomes the foundation for more sophisticated attacks.

The Fix

The fix is elegant and environment-aware. Here's the exact change made to routes.js:

 /**
  * Runs through tests for the ISBN search
+ * Restricted to non-production environments as it exposes internal
+ * debugging functionality with no authentication.
  * @param {Object} req The request to parse
  * @param {Object} res The response to send
  */
 router.get('/test', async (req, res) => {
+  if (process.env.NODE_ENV === 'production') {
+    return res.status(404).end()
+  }
   res.send(await catalogues.testIsbnSearch())
 })

What Changed

Lines 52-53: Added a comment explicitly documenting that this endpoint is restricted to non-production environments and exposes internal functionality.

Lines 59-61: Added an environment check that:
- Checks if NODE_ENV is set to 'production'
- Returns a 404 Not Found response if it is
- Allows the endpoint to function normally in development/testing environments

Why This Works

  1. Environment-based gating: The endpoint is only accessible when NODE_ENV !== 'production', which is the standard Node.js convention for environment detection
  2. 404 response: Returning 404 (Not Found) instead of 403 (Forbidden) provides no information to attackers—the endpoint appears to not exist
  3. Preserves development: Developers can still test the ISBN search functionality locally and in staging environments
  4. Zero false positives: The fix doesn't break legitimate development workflows

Security Improvement

Before: Any remote attacker could access /test and retrieve debugging information
After: The endpoint is invisible in production (returns 404) and only accessible in development environments where you control access

Prevention & Best Practices

1. Never Ship Debug Endpoints Without Guards

Always ask: "Could this endpoint expose sensitive information or system details?" If yes, add authentication and/or environment checks.

2. Use Middleware for Cross-Cutting Concerns

Instead of adding environment checks to every debug endpoint, create reusable middleware:

// middleware/devOnly.js
function devOnly(req, res, next) {
  if (process.env.NODE_ENV === 'production') {
    return res.status(404).end();
  }
  next();
}

// routes.js
router.get('/test', devOnly, async (req, res) => {
  res.send(await catalogues.testIsbnSearch())
});

router.get('/debug', devOnly, async (req, res) => {
  res.send(await getDebugInfo())
});

3. Separate Development and Production Routes

Keep debug routes in a separate file or router and only mount them in development:

// routes/debug.js
const debugRouter = express.Router();
debugRouter.get('/test', async (req, res) => { /* ... */ });
module.exports = debugRouter;

// app.js
if (process.env.NODE_ENV !== 'production') {
  app.use('/debug', require('./routes/debug'));
}

4. Use Static Analysis Tools

Tools like Semgrep can detect routes without authentication middleware:

# semgrep rule to find unprotected routes
- id: express-unprotected-route
  pattern-either:
    - pattern: router.get(...)
    - pattern: router.post(...)
  message: Route may lack authentication

5. Follow OWASP Guidelines

The OWASP Top 10 includes A01:2021 – Broken Access Control. Debug endpoints without access controls are a textbook example. Always verify:
- Is the endpoint supposed to be public?
- Does it require authentication?
- Does it require specific authorization levels?

6. Code Review Checklist

When reviewing routes, ask:
- [ ] Does this endpoint expose system information?
- [ ] Is it intended for development only?
- [ ] Are there authentication/authorization checks?
- [ ] Is the endpoint necessary in production?
- [ ] Could this aid in reconnaissance?

Key Takeaways

  • Debug endpoints are attack surfaces: The /test endpoint in routes.js exposed internal functionality without any access controls, creating a reconnaissance vector for attackers.

  • Environment-based gating is simple but effective: The fix uses NODE_ENV to prevent the endpoint from functioning in production, a pattern that's easy to implement and understand.

  • Return 404, not 403: Returning 404 provides no information to attackers, whereas 403 confirms the endpoint exists but is forbidden—use 404 for endpoints that shouldn't exist in certain contexts.

  • Reusable middleware prevents repetition: Creating a devOnly middleware function ensures consistent protection across all development-only endpoints.

  • Static analysis catches these issues: Security scanners can identify routes lacking authentication, preventing debug endpoints from reaching production in the first place.

How Orbis AppSec Detected This

Source: HTTP GET request to the /test endpoint in production environment

Sink: The route handler router.get('/test', ...) at line 56 in routes.js with no authentication or authorization middleware

Missing control: No environment-based access control, no authentication middleware, no authorization checks on the route handler

CWE: CWE-306 (Missing Authentication for Critical Function) and CWE-693 (Protection Mechanism Failure)

Fix: Added environment-based access control that returns 404 when NODE_ENV === 'production', restricting the debug endpoint to development environments only

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The /test endpoint vulnerability in routes.js demonstrates why debug functionality must never reach production without explicit access controls. What seems like a harmless testing endpoint can become a reconnaissance tool for attackers mapping your system's capabilities.

The fix—a simple environment check—is elegant and effective. It preserves development workflows while eliminating the attack surface in production. By adopting the practices outlined above—middleware-based access control, environment-based gating, and security-focused code review—you can prevent similar vulnerabilities in your Express.js applications.

Remember: In web services, every endpoint is reachable by potential attackers. Treat debug endpoints as security-critical and protect them accordingly.

References

Frequently Asked Questions

What is an exposed debug endpoint?

A debug endpoint is an HTTP route intended for development and testing that exposes internal system information, functionality, or diagnostics without proper authentication or authorization controls.

How do you prevent exposed debug endpoints in Express.js?

Always add authentication/authorization middleware to sensitive routes, use environment-based access controls, and never expose debug endpoints in production without explicit security controls.

What CWE is this vulnerability?

CWE-306: Missing Authentication for Critical Function, which covers cases where critical functionality lacks proper authentication checks.

Is removing the endpoint enough?

Removing it entirely is ideal, but if needed for development, environment-based gating (like this fix) is an acceptable compromise that prevents production exposure.

Can static analysis detect exposed debug endpoints?

Yes, static analysis tools can identify routes lacking authentication middleware or environment checks, especially when combined with security rules like the multi_agent_ai scanner that detected this issue.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #103

Related Articles

critical

How Rate Limiting Vulnerabilities Happen in Node.js OAuth Endpoints and How to Fix Them

A critical resource exhaustion vulnerability was discovered in the OAuth token endpoint at `server/routes/oauth.js`. Without rate limiting, attackers could flood the `/api/oauth/token` endpoint with requests, each triggering expensive bcrypt verification operations that would exhaust server CPU and memory. The fix implements per-IP rate limiting using `express-rate-limit` to cap requests at 20 per 15-minute window.

critical

How Missing Authentication on DELETE Endpoints Happens in Node.js Express and How to Fix It

A critical authentication bypass vulnerability was discovered in the skill-cabinet server where the DELETE /api/skills/:id endpoint allowed any unauthenticated user to delete arbitrary skills from the filesystem. The fix implements loopback origin validation to ensure only requests from localhost can perform destructive operations, while also consolidating delete functionality into a single, protected endpoint.

critical

How API Key Exposure and Unsafe Process Spawning Happens in Node.js Scripts and How to Fix It

A critical security vulnerability in the `scripts/close-issues.mjs` file exposed API key patterns in documentation and used unsafe `spawnSync` calls to execute curl commands. The fix replaces dangerous process spawning with native `fetch()` API calls and removes sensitive configuration examples from documentation, eliminating both credential exposure and command injection risks.

high

How CSRF and Missing Authentication Protection Happens in Node.js Express Routes and How to Fix It

A critical vulnerability in code-server's `/mint-key` endpoint allowed unauthenticated cross-origin requests to generate or retrieve VS Code web server authentication keys. By adding the `ensureAuthenticated` middleware to the POST handler, the fix ensures only authenticated users can mint new keys, eliminating the CSRF attack vector.

critical

How JWT Signature Bypass happens in Node.js and how to fix it

A critical authentication bypass vulnerability was discovered in `backend/services/auth-state.js` where the `tokenTtlSeconds()` function used `jwt.decode()` instead of `jwt.verify()`, allowing attackers to forge JWT tokens with arbitrary claims. Because `jwt.decode()` never validates the cryptographic signature, any attacker could craft a token with a manipulated expiration time or elevated privileges and have it accepted as legitimate. The fix replaces the insecure decode call with `jwt.verify(

critical

How hardcoded API key exposure happens in Node.js plugins and how to fix it

A critical hardcoded API key (`actor-studio-gpt-beta`) was discovered in the `src/plugins/llm/index.js` file of the Actor Studio application, granting anyone with source code access the ability to make unauthorized requests to the LLM service endpoints. The fix removes the default key from both the LLM class definition and the settings module, requiring the key to be explicitly configured through module settings instead.