Skip to content

Fix security, stability, and performance quick wins - #191

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

Fix security, stability, and performance quick wins#191
Sparksx wants to merge 1 commit into
mainfrom
claude/focused-curie-l7gdu1

Conversation

@Sparksx

@Sparksx Sparksx commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Comprehensive analysis of the codebase across security, stability, performance, and functionality. This PR implements the highest-priority quick wins (low difficulty, high impact).

Security

  • Server-side gold validation: The PUT /api/game/state endpoint now rejects client gold increases exceeding 500 above the server-known balance, logging anomalies. Prevents players from setting arbitrary gold via modified clients.
  • Rate-limit refresh token endpoint: /api/auth/refresh now uses the same authLimiter as login/register (5 req/min), preventing brute-force token rotation.
  • Atomic username change: Removed the check-then-act race in POST /api/auth/change-username — now relies on the DB unique constraint and catches P2002 directly.

Stability

  • Remove competing SIGTERM handler: server/lib/prisma.js had its own SIGTERM/SIGINT handler that could call process.exit(0) before server/index.js finishes draining HTTP connections. Removed the duplicate.
  • Guard null user after OAuth retry: If all 3 username-collision retries fail on Discord/Google signup, the code now returns a 409 instead of crashing on null.id.
  • try/catch on chat:share-combat: The socket handler had no error handling — an async resolveChannel failure became an unhandled rejection. Now caught and reported to the client.
  • Fix event listener leak: home.js registered a CHAT_UPDATED listener on every render() call without removing the old one. Now does off before on.
  • Health check verifies DB: /api/health now runs SELECT 1 and returns 503 if the database is unreachable, so Railway won't route traffic to a node with a dead DB.

Performance

  • Cache-Control headers: Added public, max-age=3600 to monster templates, sprites, and payment packs endpoints (matching the existing equipment/player template endpoints).
  • Static asset caching: Vite hashed assets (/assets/*) now served with max-age=30d, immutable; other static files get max-age=1h.
  • Missing DB indexes: Added indexes on User.isGuest, Clan.ownerId, and a unique constraint on Purchase.stripePaymentId (used by refund webhook lookups).

Observability

  • Save failure toast: When the debounced server save fails, a SAVE_FAILED event now fires and shows a user-facing toast ("Progress save failed — retrying…") instead of silently logging to console.

Test plan

  • All 524 tests pass (npm test)
  • Lint clean — 0 errors (npm run lint)
  • Production build succeeds (npm run build)
  • Verify gold validation: attempt to set gold > current + 500 via modified client → should be rejected
  • Verify health check: stop DB → GET /api/health returns 503
  • Verify OAuth signup: simulate P2002 exhaustion → returns 409

Generated by Claude Code

Security:
- Server-side gold validation: reject suspiciously large client gold increases (>500 delta)
- Rate-limit the /api/auth/refresh endpoint to prevent abuse
- Make username change atomic (use P2002 catch instead of check-then-act)

Stability:
- Remove competing SIGTERM handler in prisma.js (server/index.js already handles shutdown)
- Guard against null user after OAuth retry exhaustion (Discord & Google)
- Add try/catch to chat:share-combat socket handler (was unhandled rejection)
- Fix event listener accumulation in home screen (off before on)
- Health check now verifies DB connectivity (returns 503 if unreachable)

Performance:
- Add Cache-Control headers to monster templates, sprites, and payment packs endpoints
- Set long-term immutable caching for Vite hashed assets, 1h for other static files
- Add missing DB indexes: User.isGuest, Clan.ownerId, Purchase.stripePaymentId

Observability:
- Emit SAVE_FAILED event + show user toast when server save fails

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFGbMG4n9XhxzVZyyxmMye
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