Skip to content

GET /api/cache/stats?detailed=true exposes raw Redis INFO: memory, client and command metrics are unauthenticated #826

Description

@nanaf6203-bit

GET /api/cache/stats?detailed=true exposes raw Redis INFO: memory, client and command metrics are unauthenticated

Labels / Complexity: Frontend · Medium Complexity — Medium

Problem

src/app/api/cache/stats/route.ts serves a GET handler with no authentication. When called as /api/cache/stats?detailed=true, it calls getRedisInfo() and returns raw INFO fields in the JSON response:

const redisMetrics = {
  usedMemory: redisInfo.used_memory_human,
  usedMemoryRss: redisInfo.used_memory_rss_human,
  usedMemoryPeak: redisInfo.used_memory_peak_human,
  connectedClients: redisInfo.connected_clients,
  totalCommandsProcessed: redisInfo.total_commands_processed,
  keyspaceHits: redisInfo.keyspace_hits,
  keyspaceMisses: redisInfo.keyspace_misses,
  uptimeInSeconds: redisInfo.uptime_in_seconds,
};

The only protected operation on this route is DELETE (wrapped in withCsrf); the GET — including the detailed path — is fully open. This discloses infrastructure internals (Redis version-dependent memory layout, client connections, command throughput, uptime) to anyone on the internet, which is exactly the reconnaissance data an attacker uses before targeting the cache layer, and it is inconsistent with the route's own CSRF protection on the write path. The detailed flag has no consumer in the UI (the only UI references are unrelated page text in src/pages/security/TransactionSecurity.tsx), so the leak exists for no product reason.

Root cause

src/app/api/cache/stats/route.ts line 13: export async function GET(request: NextRequest) with no auth or gate; the detailed=true branch (lines ~48-58) returns getRedisInfo() fields.

Why this is architecturally hard

  1. The fix needs a policy, not a flag. Options are dropping detailed entirely, gating it behind an admin check, or moving stats behind an internal-only route — the contributor must pick one and match how the app authenticates admins elsewhere (there is no shared admin guard in the API routes today).
  2. Monitoring wants the data. A naive removal breaks whatever operational use the detailed stats serve; the contributor must reconcile observability needs with exposure, e.g. by scoping the response rather than deleting the feature.

Acceptance criteria

  • An unauthenticated request to GET /api/cache/stats?detailed=true no longer returns Redis INFO fields (memory, clients, commands, uptime).
  • The non-sensitive stats summary (connected/healthy/latency) remains available or is explicitly removed with a documented replacement.
  • Any remaining privileged access is authenticated and tested (a test asserts unauthenticated access is denied).

Out of scope

Building an admin dashboard for cache metrics.

Getting started

Files: src/app/api/cache/stats/route.ts, src/lib/redis.ts (getRedisInfo). Commands: npm run lint, npm test. Good first files to read: src/app/api/cache/stats/route.ts, src/lib/redis.ts.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions