Skip to content

Security, performance & stability quick wins - #170

Open
Sparksx wants to merge 1 commit into
mainfrom
claude/focused-curie-plu1c2
Open

Security, performance & stability quick wins#170
Sparksx wants to merge 1 commit into
mainfrom
claude/focused-curie-plu1c2

Conversation

@Sparksx

@Sparksx Sparksx commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Comprehensive analysis of the codebase across 4 axes (security, performance, stability, functionality) followed by implementation of the highest-priority quick wins — changes with high impact and low risk.

  • Security: Enable CSP, enforce ban checks on REST API, rate-limit token refresh, whitelist settings keys, fix non-atomic refund clawback, use distinct dev JWT secrets
  • Performance: Add HTTP compression, fix N+1 clan listing query, add missing DB indexes, set immutable cache headers for hashed assets, fix npm audit vulnerabilities
  • Stability: Wrap clan leave in a single transaction, remove competing shutdown handler, clean up dead code

Changes

Security (6 fixes)

  • Content Security Policy enabled — was fully disabled (contentSecurityPolicy: false); now configured with proper directives for Google/Discord OAuth, Stripe checkout, and WebSocket connections
  • Ban check on REST API — banned users were only blocked on Socket.io connections; requireAuth middleware now checks active bans on all authenticated API routes
  • Rate-limit /api/auth/refresh — endpoint was unprotected, allowing rapid token rotation with a stolen refresh token; now uses the same authLimiter as login/register
  • Settings key whitelistPUT /api/auth/settings accepted arbitrary keys (prototype pollution vector); now rejects unknown keys
  • Atomic refund clawback — refund webhook's gold deduction was a non-atomic read-modify-write that could race with client saves; now wrapped in a Prisma $transaction
  • Distinct dev JWT secrets — access and refresh token secrets shared the same fallback value in dev, collapsing the two-token security boundary

Performance (5 fixes)

  • HTTP compression — added compression middleware; JSON responses compress 70-90% (clan listings, PvP fight logs, chat history)
  • Clan list N+1 fixGET /api/clans was loading all members + their full gameState for every clan in the list, then discarding it; now uses a lightweight _count-only include
  • Immutable asset caching — Vite's hashed /assets/ files now served with max-age=1y, immutable headers
  • Missing DB indexes — added User.updatedAt (PvP matchmaking ORDER BY) and Purchase.stripePaymentId (refund webhook lookup)
  • npm audit — fixed all 18 vulnerabilities (7 moderate, 10 high, 1 critical) → 0 remaining

Stability (3 fixes)

  • Clan leave transaction — the leave handler deleted the member, then checked ownership, then transferred — all in separate queries with a race condition window; now a single atomic $transaction
  • Graceful shutdown — removed competing SIGTERM/SIGINT handler in prisma.js that could call process.exit(0) before the HTTP server finished draining in-flight requests
  • Dead code cleanup — removed unused shopState destructuring from game state save route

Misc

  • Fixed admin panel forge level prompt showing hardcoded "1–12" instead of the actual MAX_FORGE_LEVEL (35)

Test plan

  • npm test — 308 tests passing
  • npm run lint — 0 errors (only pre-existing warnings)
  • npm run build — builds successfully
  • Verify CSP does not block Google/Discord OAuth or Stripe checkout in production
  • Verify banned user gets 403 on REST API calls (not just socket disconnect)
  • Verify clan list page loads faster (no full member data fetched)

🤖 Generated with Claude Code

https://claude.ai/code/session_01PHyAxehXQVsp2JoJTvdjAj


Generated by Claude Code

Security:
- Enable Content Security Policy (was disabled) with proper directives
  for Google/Discord OAuth, Stripe, and WebSocket connections
- Use distinct JWT secret fallbacks in dev (access vs refresh were identical)
- Add ban check to REST API middleware (was only on Socket.io)
- Rate-limit /api/auth/refresh endpoint (was unprotected)
- Whitelist allowed keys in PUT /api/auth/settings (prevented arbitrary key injection)
- Wrap refund gold clawback in a Prisma transaction (was a non-atomic read-modify-write)

Performance:
- Add HTTP response compression middleware (compression package)
- Fix N+1 query: clan list no longer fetches all members+gameState, uses lightweight include
- Add immutable cache headers for hashed /assets/ (1 year max-age)
- Add database indexes: User.updatedAt (PvP matchmaking), Purchase.stripePaymentId (refund lookup)
- Fix 18 npm audit vulnerabilities (0 remaining)

Stability:
- Wrap clan leave in a single transaction (was split across multiple queries, race condition)
- Remove competing SIGTERM/SIGINT handler in prisma.js (raced with server/index.js shutdown)
- Remove dead shopState destructuring from game state save route

Misc:
- Fix admin panel forge level prompt showing "1-12" instead of dynamic MAX_FORGE_LEVEL (35)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PHyAxehXQVsp2JoJTvdjAj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants