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
•Technically reviewed by Anupam Mediratta•Published August 31, 2026•Reviewed 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

Discord Bot Auto-Role CWE-269: Privilege Escalation via role.position

A Discord.js bot's auto-role feature allowed privilege escalation where any user with ManageRoles permission could add Administrator roles to automatic assignment lists, regardless of their own role hierarchy. The vulnerability existed because role.position validation checked only the bot's permissions, not the invoking user's role hierarchy relative to the target role.

high

CVE-2026-54290: hono cors() Reflects Any Origin With Credentials

The `hono` CORS middleware, as resolved in this service at 4.12.8, reflected the caller's `Origin` header back in `Access-Control-Allow-Origin` while also emitting `Access-Control-Allow-Credentials: true` whenever the `origin` option was left at its `'*'` default. That combination makes any website a trusted origin for credentialed cross-origin reads. The dependency range was raised from `^4.7.1` to `^4.13.5`, moving the installed copy from 4.12.8 to 4.13.5.

critical

POST /api/generate Lacks Authentication, Allowing Unauthenticated

A resume generation endpoint in a Node.js backend accepted requests from any caller with network access, allowing attackers to consume OpenAI API quota without restriction. The vulnerability stemmed from missing authentication middleware on a cost-bearing endpoint. The fix adds mandatory API key validation via HTTP headers before processing any generation requests.

critical

SchedulePush.disableReminder Missing Authorization Check in push.js

The `disableReminder` method in the `SchedulePush` class allowed any user to disable push notification reminders for arbitrary user IDs by manipulating the `e.user_id` event parameter. The fix adds a `checkFriend()` authorization gate that verifies the requesting user has a valid friendship relationship with the bot before modifying subscription state.

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.