Files
GameList/REVIEW.md

7.5 KiB

Review - Pick'n'Play (State-of-the-Art Assessment)

Date: 2026-02-18

Scope

  • Evaluated backend (Program.cs, Endpoints/*, Infrastructure/*, Data/*), frontend (wwwroot/*), and CI/deployment scripts.
  • Focused on risks in maintainability, extensibility, scalability, and security.

Executive summary

The app is solid for a small private group, but it still has several architectural bottlenecks compared to current best practice for long-term product growth. The largest risks are read amplification from client polling, hard-coded workflow/permission modeling, and security hardening gaps (CSRF posture and crypto modernization).

Findings

1) High - Scalability - Single-node SQLite bottleneck

Evidence:

  • SQLite is the primary DB (Program.cs:42).

Risk:

  • SQLite is excellent for small single-node deployments, but write concurrency and horizontal scale are limited for larger or bursty usage.

Alternative:

  • Keep SQLite for local/dev and migrate production to PostgreSQL/SQL Server with provider-specific migrations and connection pooling.

2) High - Scalability - Polling causes read amplification

Evidence:

  • Frontend polling runs continuously with 3s-20s cadence (wwwroot/app.js:30, wwwroot/app.js:58).
  • Each refresh can hit multiple endpoints (wwwroot/js/data.js:21, wwwroot/js/data.js:109).
  • /api/state also executes multiple aggregate counts each time (Endpoints/StateWorkflowService.cs:14).

Risk:

  • As concurrent users increase, backend read load grows quickly and mostly serves unchanged data.

Alternative:

  • Move to event-driven updates (SSE/WebSocket) plus conditional GET (ETag/If-None-Match) and/or a consolidated bootstrap endpoint.

3) High - Security - CSRF protection is implicit, not explicit

Evidence:

  • Cookie authentication is used for API auth (Program.cs:100, Program.cs:104).
  • Many state-changing endpoints rely on cookie auth (Endpoints/SuggestEndpoints.cs:24, Endpoints/VoteEndpoints.cs:24, Endpoints/AdminEndpoints.cs:14).
  • No antiforgery middleware/tokens are configured in startup.

Risk:

  • SameSite=Strict helps (Program.cs:104) but is not a full long-term CSRF strategy across browser/proxy edge cases.

Alternative:

  • Add explicit anti-forgery tokens for mutating requests (or move to bearer tokens for API calls) and verify origin headers server-side.

4) High - Extensibility - Workflow is hard-coded across backend and frontend

Evidence:

  • Phase behavior is encoded via enum and many explicit checks/switches (Domain/Phase.cs:3, Endpoints/StateWorkflowService.cs:70, Endpoints/EndpointHelpers.cs:97).
  • Frontend also hard-codes phase logic in many files (wwwroot/app.js:99, wwwroot/js/data.js:47, wwwroot/js/votes-ui.js:167).

Risk:

  • Adding a new phase or changing transitions requires touching many scattered branches, increasing regression risk.

Alternative:

  • Introduce a shared workflow/state-machine model (transition table) and consume it in both backend and frontend.

5) High - Extensibility - Role model is fixed to booleans

Evidence:

  • Role/state flags are booleans on player (Domain/Player.cs:22, Domain/Player.cs:23).
  • Admin checks are tightly coupled to that model (Infrastructure/AdminOnlyFilter.cs:13, Endpoints/AdminWorkflowService.cs:93).

Risk:

  • Future needs (moderator, read-only admin, per-feature permissions) require schema and logic rewrites instead of additive changes.

Alternative:

  • Move to role/permission tables (or claims-based capability model) and policy-based authorization.

6) Medium - Maintainability - Frontend is string-template heavy with global mutable state

Evidence:

  • Single global state object (wwwroot/js/state.js:1).
  • Heavy innerHTML rendering across modules (wwwroot/js/suggestions-ui.js:115, wwwroot/js/votes-ui.js:35, wwwroot/js/results-ui.js:72).

Risk:

  • Harder refactoring, weaker static guarantees, and easy XSS regressions when new contributors add templates.

Alternative:

  • Incrementally move to TypeScript + componentized rendering (or at minimum typed JSDoc + stricter lint rules + centralized safe render helpers).

7) Medium - Scalability/Security - In-memory dictionaries are unbounded

Evidence:

  • Auth attempt monitor stores failures in unbounded ConcurrentDictionary (Infrastructure/AuthAttemptMonitor.cs:14).
  • Image reachability cache is a static dictionary without size limits (Endpoints/SuggestionValidator.cs:7, Endpoints/SuggestionValidator.cs:42).

Risk:

  • High-cardinality traffic can grow memory and become a denial-of-service vector.

Alternative:

  • Replace with bounded MemoryCache (size limits + eviction) or distributed cache (Redis) with TTL and cardinality controls.

8) Medium - Scalability - Linking/results workflows load full sets into memory

Evidence:

  • Link/unlink loads all suggestions then computes roots in memory (Endpoints/AdminWorkflowService.cs:136, Endpoints/AdminWorkflowService.cs:188).
  • Results projection includes full vote lists per suggestion (Endpoints/ResultsWorkflowService.cs:23, Endpoints/ResultsWorkflowService.cs:34, Endpoints/ResultsWorkflowService.cs:50).

Risk:

  • Memory and query cost rise non-linearly with larger datasets.

Alternative:

  • Introduce persisted link-group IDs and push aggregation to SQL; add pagination/windowing for large result sets.

9) Medium - Security - Crypto is good but not state-of-the-art

Evidence:

  • Password hashing uses PBKDF2-SHA256 (Infrastructure/PasswordHasher.cs:33) with fixed iteration count (Infrastructure/PasswordHasher.cs:10).

Risk:

  • PBKDF2 remains acceptable, but modern guidance favors memory-hard KDFs (Argon2id/scrypt) against GPU/ASIC attacks.

Alternative:

  • Add versioned password hashes and migrate to Argon2id on login/re-hash.

10) Medium - Security - CSP remains permissive for inline style and mixed image origins

Evidence:

  • CSP allows style-src 'unsafe-inline' and img-src ... https: http: (Program.cs:138).

Risk:

  • Wider policy surface than needed, especially for long-term hardening.

Alternative:

  • Remove inline style dependence (e.g., CSS classes or nonce/hash), and restrict image sources to https and/or a media proxy allowlist.

11) Medium - Maintainability/Extensibility - API contract sync is manual

Evidence:

  • Frontend endpoints are hard-coded in JS (wwwroot/js/api.js:37, wwwroot/js/api.js:70).
  • Contract is also maintained manually in markdown (API.md).

Risk:

  • Contract drift between backend DTOs and frontend consumers over time.

Alternative:

  • Generate OpenAPI from endpoints and produce typed client code for frontend consumption.

12) Medium - Scalability/Resilience - External image validation is synchronous on write path

Evidence:

  • Suggestion validation calls network reachability checks (Endpoints/SuggestionValidator.cs:40).
  • Validation does outbound DNS/connect/HEAD/GET in request path (Endpoints/EndpointHelpers.cs:196, Endpoints/EndpointHelpers.cs:240, Endpoints/EndpointHelpers.cs:270).

Risk:

  • User write latency is coupled to third-party host responsiveness.

Alternative:

  • Accept URL quickly, then validate asynchronously (background job + status flag), optionally with trusted media proxying.
  1. Reduce read amplification: event-driven updates + state endpoint optimization.
  2. Harden security baseline: explicit CSRF, CSP tightening, and password-hash migration plan.
  3. Decouple growth hotspots: workflow state machine + role/permission model.
  4. Improve operational scale: replace unbounded in-memory structures and large in-memory link/result operations.
  5. Introduce contract tooling: OpenAPI + generated frontend client to reduce drift.