Skip to content

Known weaknesses — aggregated register

This is the canonical scoreboard for every known weakness in Evospin / dropbet. Architectural blind spots, security findings, flow-doc failure modes, and workspace-level gaps all roll up here.

ID prefixes: - SF-* — security findings; lives in security-findings/ or a flow doc's §6. - FM-* — flow-doc-local failure modes; not yet SF-promoted. - AF-* — architectural blind spots that touch multiple flows. - WK-* — workspace-level gaps.

Updated alongside the flow docs and security-findings/. Re-paginate ID assignment after each ten-finding addition.

Architectural blind spots

ID Scope Weakness Source
AF-1 admin-fe ↔ ebit-api Four stacked bugs block every admin-fe→api cross-service trace: (a) cookie-name mismatch — api sets access_token, middleware reads jwt_access_token (middleware.ts:59-60, src/types/Auth.ts:36-40); (b) instrumentation.ts is Sentry-only, no @vercel/otel fallback → no traceparent injection; (c) propagateContextUrls absent; (d) hard-coded API host. User-directive: do not edit admin-fe source. All admin specs therefore drive ebit-api directly. flows/admin-sign-in.md, flows/admin-user-mgmt.md, flows/admin-bets.md
AF-2 Inter-app RPC @ExternalControllerClient / @GatewayMethod / @MessagePattern use Nest Redis pub/sub transport — W3C traceparent is NOT propagated. Callee (api when called from rt, or speed-roulette when called from api) emits orphan root traces. Cross-service latency breakdown requires Loki pivot on userId + timestamp. flows/rt-websocket.md §6 #1, flows/dropbet-speed-roulette.md §6 #1
AF-3 ebit-rt scale ClientGateway.clientSockets is a per-instance Map. Scaling rt past one replica without sticky-routing OR @socket.io/redis-adapter silently drops message.user-targeted emits and breaks per-room joins. flows/rt-websocket.md §6 #2, flows/dropbet-wallet.md SF-016
AF-4 Orphan ebit-bj apps/bj/ runs on 4002 with a full blackjack + EVO-wallet RPC, but the dropbet client goes through ebit-api's /casino/games/house/blackjack/* exclusively. Compose exposes the port; the service receives zero in-repo traffic. Delete-or-wire decision pending. flows/dropbet-blackjack.md §6 #4
AF-5 Online-count inflation apps/api/src/user/online-tracker.service.ts:30-46 broadcasts zcard(ONLINE_USERS_KEY) + fakeUserOnline every 10 s via Server.UsersOnlineUpdated. fakeUserOnline init 500, drifts ±5, floor 180. UI counters and any dashboard reading this number are reading a padded figure — not the real one. Only getOnlineCount() exposes the raw zcard; no HTTP endpoint. flows/rt-websocket.md §6 #3
AF-6 RabbitMQ stubbed apps/api/src/fast-track/rabbitmq/fast-track.rmq.module.ts:8 returns disabled = true. The broker runs in compose (vhost ft) but receives zero traffic. 11 producer call sites (bet.service.ts ×4 settlement paths + promo-effect.service.ts ×7 reward handlers) fire into a no-op stub — every settled bet and every promo effect silently drops its Fast Track event. Flipping disabled=false requires real FASTTRACK_JWT_PRIVATE_KEY/FASTTRACK_JWT_PUBLIC_KEY + a reachable Fast Track CRM sandbox (out of scope for local dev). Decision deferred: either wire sandbox credentials (option b) or rip the producer classes + 11 call sites entirely (option a). flows/dropbet-challenges.md §6 #3, docker-compose.yml comment block
AF-7 ~~Loki coverage gap~~ fixed EvoLogger (winston) records now reach Loki via the filelog/docker receiver added to observability/otel-collector.yml. The receiver scrapes /var/lib/docker/containers/*/*.log and tags each record with source: docker_filelog so they are distinguishable from OTLP-bridged pino records. Query: {source="docker_filelog"} \|= "EvoLogger". observability/otel-collector.yml, docker-compose.yml (volume mount)

Security-tracked findings (SF-*)

Flow-doc SF numbering is continuous across all docs — each SF is a single line in the respective §6 with source pointer. Three promoted to security-findings/:

ID Title Flow Severity signal
SF-001 Sign-in email-enumeration timing oracle (user.service.ts:715-720 returns before bcrypt on unknown email) flows/dropbet-sign-in.md Info-disclosure; combines with SF-003
SF-002 Lockout counter reset defeats escalating rate limit (handleLoginAttempt deletes attemptsKey when lockout arms; both share TTL) flows/dropbet-sign-in.md Auth-control bypass
SF-003 SignInDto + VerifyMfaDto + referrer lack @MaxLength / @IsEmail / @Length(6,6) flows/dropbet-sign-in.md, flows/admin-sign-in.md, flows/dropbet-sign-up.md Request-validation gap
SF-004 Double-settle backstop is only @@unique([roundId, userId]); generic ApiCode.INTERNAL on violation flows/dropbet-bet-place.md Correctness
SF-005 Fairness seed race if @PlaceBetLock key TTL-expires mid-handler flows/dropbet-bet-place.md Fairness
SF-006 Insufficient-funds guard relies on WHERE amount >= betAmount rowcount; no pg CHECK flows/dropbet-bet-place.md Correctness
SF-007 bet_settled_queue is fire-and-forget; Redis outage loses side-effects after removeOnFail.age flows/dropbet-bet-place.md Data loss
SF-008 Bet-detail endpoints have JwtGuard commented out — anon reads full seed material flows/dropbet-bet-history.md High — info disclosure
SF-009 BetInfoQuery.userId is dead — service ignores ownership filter flows/dropbet-bet-history.md Access control
SF-010 Bet-detail cache key is betId-only; post-ban window leaks data flows/dropbet-bet-history.md Info disclosure
SF-011 No covering index for bet list status filter; power-user degradation flows/dropbet-bet-history.md Performance
SF-012 Sportsbook bets silently invisible in /bets/my/* (hard-coded != SPORTSBOOK) flows/dropbet-bet-history.md UX gap
SF-013 UserBalanceRepository.toVault has no overdraft guard — balance goes negative (amount=1000 + vault=10003 → -9003) flows/dropbet-wallet.md High — correctness / financial
SF-014 Zero caching on GET /accounting/balances; hot JWT path flows/dropbet-wallet.md Performance
SF-015 Transaction ledger has no HTTP endpoint (rt Private.TransactionFindMany only) flows/dropbet-wallet.md Debuggability
SF-016 ClientGateway.handleServerEvent is O(n_sockets) per BalanceUpdated flows/dropbet-wallet.md Scale
SF-017 usdAmount is request-time FX, not row-stamped; two reads can disagree flows/dropbet-wallet.md Consistency
SF-018 TETH indistinguishable from ETH at API layer flows/dropbet-wallet.md UX
SF-019 RACE_ENABLED gates each leaderboard handler inline — easy to forget on new routes flows/dropbet-leaderboard.md Maintainability
SF-020 LeaderboardQueueProducer has zero call sites; @Processor subscribes to a queue nobody writes flows/dropbet-leaderboard.md Dead code
SF-021 leaderboardService.handleBet runs in BullMQ worker with no OTel span flows/dropbet-leaderboard.md Observability
SF-022 @Cacheable is in-process (node-local Map); api vs bo serve stale to each other up to 60 s flows/dropbet-leaderboard.md Cache coherence
SF-023 leaderboard_user_position_view recomputes ROW_NUMBER per query — full-board pagination scans partition per page flows/dropbet-leaderboard.md Performance
SF-024 updateLeaderboards runs only at boot — DAILY race stays ACTIVE past midnight until restart flows/dropbet-leaderboard.md Correctness
SF-025 Admin bet list runs count + findMany sequentially where player list uses Promise.all flows/admin-bets.md Performance
SF-026 bo POST /bets proxies to Private.BetFindMany — no subscriber; every call 5 s hangs → 500 flows/admin-bets.md Dead route + DOS
SF-027 BetHttpController has no @UseGuards — any JWT holder can trigger the 5 s hang flows/admin-bets.md Access control / DOS
SF-028 No admin "adjust/void/rollback bet" endpoint despite task title — corrections go via balance adjustment + ad-hoc DB write flows/admin-bets.md Capability gap
SF-029 permission.guard.ts:24 — SuperAdmin role shortcuts BEFORE the MFA check at line 40; seeded admin@admin.com (no mfaSecret) bypasses MFA flows/admin-bets.md High — auth control
SF-030 Admin response has no aggregates — total-wagered / GGR needs a separate endpoint or client-side reduction flows/admin-bets.md UX

Other flow-doc failure modes (FM — not yet SF-promoted)

  • FM-Sign-up-1 RecaptchaService bypasses on NODE_ENV=local && token === 'pass'; a mis-configured prod NODE_ENV=local disables reCAPTCHA entirely. flows/dropbet-sign-up.md
  • FM-Sign-up-2 User committed even if verification email fails (sendEmailVerificationLink is a floating promise). Same shape for sendUserWelcomeEmail.
  • FM-Sign-up-3 Duplicate email/username race — losing INSERT returns 500 (P2002) instead of AUTH_USERNAME_OR_EMAIL_TAKEN; bots can fingerprint live users via status-code divergence.
  • FM-Sign-up-4 First session has no UserSession row (isFromRegister=true skips BullMQ enqueue).
  • FM-Reset-1 JWT_VERIFICATION_TOKEN_SECRET signs both email-verify and password-reset tokens — leak mints reset tokens for any account.
  • FM-Reset-2 Reset token is not one-use within its TTL — observed two successful PATCHes with the same token in the E2E.
  • FM-Reset-3 Sessions are not invalidated after reset — phished access_token survives.
  • FM-Reset-4 Email enumeration via cooldown key (written only inside the user-exists branch).
  • FM-Admin-user-mgmt-1 /admin/user/admin-audit?userId=X filters by ADMIN ACTOR, not target — undocumented.
  • FM-Admin-user-mgmt-2 banUser/unBanUser take an admin parameter but don't persist it; the only "who banned whom" record is the admin_action_log row, and safeLog swallows DB errors.
  • FM-Admin-user-mgmt-3 No multi-ban route (admin.user.controller.ts:148 // TODO).
  • FM-Admin-user-mgmt-4 No idempotency on /ban — repeat PATCH re-UPDATEs + re-publishes to profile-notifier + writes another audit row + double-kicks the socket.
  • FM-Admin-signin-3 middleware.ts:68-90 — bad-JWT parseToken catch is try { … } catch { /* TODO */ } with the leaveFromAccount redirect commented out → silent dashboard fall-through → blank page.
  • FM-Admin-signin-5 Admin-only gate (auth.service.ts:145-156) is Origin-header-based; stripping the header demotes the check.
  • FM-House-game-3 Limbo randomMultiplier = 1.0 collapses genuine bust vs near-one crash; no explicit didWin in the response.
  • FM-House-game-4 Dice multiplier < 1.01 is a hard reject, not a clamp — UI must pre-validate.
  • FM-Challenges-1 POST /promo/public/:code is unregistered — missing @Post decorator at promo.controller.ts:99; every FE claim 404s.
  • FM-Challenges-2 Challenges have no user-triggered state change; rewards land silently when admin calls /admin/challenge/:id/award.
  • FM-Challenges-4 promo.service.ts:36 calls expiresAt.getMilliseconds() (0-999) where .getTime() was intended — that path always evaluates false.
  • FM-Blackjack-2 Abandoned hand locks player funds indefinitely — no TTL auto-resolution on is_finished=false rounds.
  • FM-Blackjack-3 BetService.createBet triggers 6+ side-load SELECTs per /init. Same pattern flagged in flows/dropbet-bet-place.md.
  • FM-Blackjack-5 payload is opaque JSONB — no covering index; analytics must parse per-row.
  • FM-Blackjack-6 No settlement push channel; /handleAction response body is the sole settlement signal.
  • FM-Speed-roulette-3 State processor is concurrency: 1, single-worker — any unhandled path that exhausts retries without a follow-up job deadlocks the queue.
  • FM-RT-4 Unknown client event is warn-log + undefined return — ack-callers hang to their own 5 s timeout with no error event back.
  • FM-RT-5 handleDisconnect does not zrem the user; "online" window extends to TTL.

Workspace-level gaps (WK-*)

  • WK-1 ebit-api/node_modules ends up root-owned after docker builds (pending task #23).
  • WK-2 First sign-up produces no UserSession row (see FM-Sign-up-4). Admin seeing a just-registered account finds only the one-shot RegistrationInfo row.
  • WK-3 FastTrack RabbitMQ broker runs in compose but is stubbed; disposition decision pending (task #21).