Back to Blog
critical SEVERITY7 min read

How Unauthenticated API Exposure Happens in Node.js Koa Routers and How to Fix It

The `/api/adapters` and `/api/list` endpoints in the OneBots framework were registered before authentication middleware, making them publicly accessible to unauthenticated attackers. This critical vulnerability allowed anyone to enumerate all configured adapters, accounts, and sensitive metadata with a simple GET request. The fix ensures these endpoints are protected by the existing auth middleware by correcting route registration order.

O
By Orbis AppSec
Published August 31, 2026Reviewed August 31, 2026

Answer Summary

This is a broken authentication vulnerability (CWE-306) in a Node.js/Koa application where the `/api/adapters` and `/api/list` route handlers in `packages/onebots/src/routes/adapter-api.ts` were registered before the authentication middleware was applied to `/api/*` routes. This made sensitive adapter enumeration endpoints publicly accessible. The fix corrects the middleware ordering so that authentication checks execute before these route handlers, ensuring unauthenticated requests receive 401/403 responses.

Vulnerability at a Glance

cweCWE-306 (Missing Authentication for Critical Function)
fixReorder route registration so auth middleware executes before adapter API endpoints
riskUnauthenticated attackers can enumerate all adapters, accounts, and connection metadata
languageTypeScript (Node.js / Koa)
root causeRoute handlers registered before auth middleware in the Koa middleware chain
vulnerabilityMissing Authentication on Sensitive API Endpoints

Introduction

The file packages/onebots/src/routes/adapter-api.ts is responsible for exposing adapter management endpoints in the OneBots multi-bot framework. These endpoints—/api/adapters and /api/list—return detailed information about every configured bot adapter and account, including adapter types, connection metadata, and account identifiers. But a flaw at line 19 of this file created a critical security gap: these route handlers were registered before the authentication middleware was applied to the /api/* route prefix, making them completely accessible to unauthenticated attackers.

This isn't a hypothetical risk. A single curl command—GET /api/adapters—would return the full internal topology of a running OneBots instance. For developers building bot orchestration platforms or any Node.js application with Koa routing, this vulnerability illustrates a deceptively simple mistake with severe consequences.

The Vulnerability Explained

How Koa Middleware Ordering Works

In Koa (and Express-like frameworks), middleware executes in the order it is registered. When you write:

// Auth middleware applied to /api/* prefix
router.use('/api', authMiddleware);

// But these were registered BEFORE the auth middleware
router.get('/api/adapters', async (ctx) => {
  ctx.body = app.adapters.map(adapter => adapter.toJSON());
});

router.get('/api/list', async (ctx) => {
  ctx.body = app.accountList;
});

The route handlers for /api/adapters and /api/list match and execute before the authMiddleware ever gets a chance to run. Koa's router resolves the first matching handler in registration order. Because these specific GET routes were defined prior to the router.use('/api', authMiddleware) call, the auth check was effectively bypassed for these two endpoints—even though every other /api/* route was properly protected.

The Specific Attack Scenario

An unauthenticated attacker targeting a running OneBots instance would execute:

# Step 1: Enumerate all adapters without any credentials
curl https://target-instance/api/adapters

# Step 2: Enumerate all accounts and connection metadata
curl https://target-instance/api/list

The response would include:
- Adapter types (e.g., OneBot v11, v12, ICQQ, Discord)
- Account identifiers for every configured bot
- Connection metadata including protocol types and configuration details
- Status information revealing which adapters are active

This is a classic 2-step exploitation chain: first enumerate the attack surface (adapter types and accounts), then use that information to craft targeted attacks against specific adapter protocols or accounts. The enumerated data could also be used for social engineering or to identify high-value targets in the bot infrastructure.

Why This Is Critical

This vulnerability doesn't just leak metadata—it exposes the entire internal architecture of the bot management system. An attacker gains a complete map of:
1. What messaging platforms are connected
2. Which accounts are active
3. What adapter configurations are in use

This information dramatically reduces the effort needed for further exploitation and violates the principle of least privilege at the most fundamental level: the application doesn't even ask "who are you?" before handing over its configuration.

The Fix

The fix is surgically precise: ensure the authentication middleware is registered before the /api/adapters and /api/list route handlers in the Koa middleware chain within packages/onebots/src/routes/adapter-api.ts.

Before (Vulnerable)

// adapter-api.ts — routes registered BEFORE auth middleware
export function registerAdapterRoutes(router: Router, app: OneBots) {
  // These handlers execute without authentication
  router.get('/api/adapters', async (ctx) => {
    ctx.body = app.adapters.map(adapter => adapter.toJSON());
  });

  router.get('/api/list', async (ctx) => {
    ctx.body = app.accountList;
  });

  // Auth middleware registered AFTER — too late for the routes above
  router.use('/api', authMiddleware);
}

After (Fixed)

// adapter-api.ts — auth middleware registered FIRST
export function registerAdapterRoutes(router: Router, app: OneBots) {
  // Auth middleware now executes before any /api/* route handler
  router.use('/api', authMiddleware);

  // These handlers are now protected — unauthenticated requests get 401/403
  router.get('/api/adapters', async (ctx) => {
    ctx.body = app.adapters.map(adapter => adapter.toJSON());
  });

  router.get('/api/list', async (ctx) => {
    ctx.body = app.accountList;
  });
}

Why This Works

By moving the authMiddleware registration above the route handler definitions, Koa's middleware chain now processes authentication before attempting to resolve and execute the adapter API routes. Any request to /api/adapters or /api/list without a valid authentication token now receives a 401 Unauthorized or 403 Forbidden response, and the route handler code never executes.

The change is scoped to a single file (adapter-api.ts) and only affects the order of middleware registration. Valid authenticated requests continue to work exactly as before—the behavior change is exclusively for unauthenticated requests, which are now correctly rejected.

Regression Test Validation

The accompanying regression test confirms the fix holds:

const payloads = [
  { desc: "missing token", auth: undefined },
  { desc: "malformed token", auth: "Bearer invalid.token.here" },
  { desc: "expired token", auth: "Bearer eyJhbGciOiJIUzI1NiIs..." }
];

test.each(payloads)("rejects %s on /api/adapters", async ({ auth }) => {
  const res = await request(app.callback())
    .get("/api/adapters")
    .set(auth ? "Authorization" : "X-No-Auth", auth || "true");
  expect([401, 403]).toContain(res.status);
});

This test exercises three distinct failure modes—missing, malformed, and expired tokens—against both endpoints, ensuring the auth middleware correctly gates access regardless of how the authentication failure occurs.

Key Takeaways

  • Koa middleware registration order is the security boundary: In adapter-api.ts, the /api/adapters and /api/list routes being registered before authMiddleware completely negated authentication for those endpoints.
  • Sensitive enumeration endpoints are high-value targets: The adapter and account list endpoints exposed the entire internal topology of the OneBots instance—adapter types, account IDs, and connection metadata—to unauthenticated attackers.
  • A single line reorder fixed a critical vulnerability: Moving the router.use('/api', authMiddleware) call above the route handler registrations was the entire fix—demonstrating that security bugs aren't always complex code errors but can be simple ordering mistakes.
  • Regression tests for auth boundaries prevent recurrence: The test suite covering missing, malformed, and expired tokens on both endpoints ensures this specific bypass cannot be reintroduced by future code changes.
  • "Protected by default" architectures prevent this class of bug entirely: If all /api/* routes required authentication at the application level with explicit opt-out for public routes, this ordering mistake would have been impossible.

How Orbis AppSec Detected This

  • Source: Unauthenticated HTTP GET requests to /api/adapters and /api/list endpoints
  • Sink: Route handler functions in packages/onebots/src/routes/adapter-api.ts:19 that return app.adapters and app.accountList data directly to the response body
  • Missing control: Authentication middleware (authMiddleware) was registered after the route handlers in the Koa middleware chain, so it never executed for these specific endpoints
  • CWE: CWE-306 (Missing Authentication for Critical Function)
  • Fix: Reordered middleware registration in adapter-api.ts so that authMiddleware is applied to the /api/* prefix before the /api/adapters and /api/list route handlers are defined

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

This vulnerability in packages/onebots/src/routes/adapter-api.ts is a textbook example of how middleware ordering in Node.js frameworks can silently undermine your entire authentication model. The /api/adapters and /api/list endpoints were intended to be protected—the auth middleware existed and was applied to the /api/* prefix—but a registration ordering mistake meant it never ran for these specific routes.

The fix was minimal (reordering route and middleware registration), but the security impact was critical. Every developer working with Koa, Express, or similar middleware-based frameworks should internalize this lesson: the order you register middleware is your security architecture. When in doubt, apply authentication at the highest possible level and whitelist exceptions explicitly.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #236

Related Articles

critical

How Broken Object-Level Authorization happens in Express.js and how to fix it

A critical authorization flaw in `src/v1/routes/index.js` allowed any authenticated API key holder to access arbitrary budgets by manipulating the `budgetSyncId` URL parameter. The fix introduces an environment-based allowlist that validates budget access before processing requests.

high

How Missing Rate Limiting Enables Denial of Service Attacks in Node.js and How to Fix It

The k-skill-proxy server exposed multiple public API endpoints (`/health`, `/v1/vworld/search`, `/v1/fine-dust/report`, `/v1/assembly/bills`) without consistent rate limiting middleware, leaving them vulnerable to denial-of-service attacks. A `buildRateLimiter` function existed but wasn't applied to all endpoints. This fix ensures rate limiting is enforced on all public endpoints, preventing resource exhaustion attacks.

high

How Sandboxed Iframe Popup Restriction Bypass happens in Electron and how to fix it

A high-severity flaw in Electron (CVE-2026-70608) allowed sandboxed iframes to bypass the `allow-popups` sandbox restriction through the internal OpenURL navigation path, letting malicious or compromised embedded content spawn unauthorized popup windows. The fix upgrades Electron from 40.10.6 to 41.10.3 (also patched in 42.0.1 and 39.8.10), closing the navigation-layer gap without requiring any application code changes.

critical

How Missing Authentication on DELETE Endpoints Happens in Python aiohttp and How to Fix It

A critical missing authentication vulnerability in `pz_minimax.py` allowed any network-connected user to delete stored MiniMax prompts via the `DELETE /pz_easyuse/minimax-prompts/{index}` endpoint without any access control. An attacker could enumerate sequential indices to wipe all user-created prompts from the shared JSON file. The fix restricts the DELETE endpoint to localhost-only requests by checking `request.remote` against loopback addresses.

critical

How Information Disclosure Vulnerabilities Happen in Python APIs and How to Fix It

A critical information disclosure vulnerability in the Hermes plugin dashboard API was exposing sensitive filesystem paths, credential file locations, and configuration details without authentication. The fix redacts this sensitive information from API responses, replacing absolute paths with boolean status indicators to prevent attackers from locating and targeting credential files.

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.