Skip to content

Findings — full internal detail

🔒 INTERNAL ONLY. Do not share externally without redaction. The customer-shareable counterpart is docs/security/client/risk-register.md. Sanitize before any external distribution.

This document is the engineering-grade source of truth for security-tracked findings on the Evospin platform. Every finding listed here has a sibling row in the client register; the IDs match. Sourced from the legacy docs/security-register.md, the per-finding deep-dives in docs/security-findings/, and the flow-doc audit corpus (docs/flows/*.md §6).

ID convention. SR-NNN (security register, sequential). Legacy IDs (SF-, FM-, AF-) listed in parentheses for back-reference; existing E2E tests still grep on the legacy IDs.

Field convention. Per finding:

  • Title (specific — file + line if applicable)
  • CVSS v3.1 vector + score (or {{TBD}})
  • CWE / CVE mapping
  • Affected files (path:line)
  • Repro (concrete steps)
  • PoC (snippet, curl, or reference)
  • Mitigation (current state + planned)
  • Owner (engineer + due date)
  • Verification (how to confirm fix landed)
  • Related advisories / vendor responses

Critical

SR-001 (legacy SF-008) — Bet-detail endpoints leak seed material

  • Title. JwtGuard commented out at apps/api/src/bet/bet.controller.ts:31-43; anonymous reads expose full provably-fair seed material.
  • CVSS. AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N → 7.5 (High). Promoted to Critical in our internal rubric because of the fairness-integrity dimension (player-trust impact).
  • CWE. CWE-306 (Missing Authentication for Critical Function), CWE-200 (Exposure of Sensitive Information).
  • Affected files.
  • apps/api/src/bet/bet.controller.ts:31 — house-games handler with @UseGuards(JwtGuard) commented out.
  • apps/api/src/bet/bet.controller.ts:43 — slots handler, identically unguarded.
  • Service: getBetInfoSettled at bet.repository.ts (no userId predicate; ties to SR-005, SR-006).
  • Repro.
    curl http://localhost:4000/bets/house-games/info/<betId>
    # 200 with full payload: gameSeedNonce, gameHashedServerSeed, multiplier, RNG params
    
  • PoC. UUIDs are not enumerable, but observed-bet IDs propagate via the public bet-feed; replaying any observed ID returns full seed material.
  • Mitigation. Pinned by E2E tests-e2e/tests/dropbet-bet-history.spec.ts (asserts the broken 200) so a guard re-enable trips loudly. Planned fix: re-enable @UseGuards(JwtGuard) at :31, uncomment @Req() req: RequestExt at :35, plumb req.user.id into the service, and add WHERE userId = :userId in getBetInfoSettled. Combined fix with SR-005, SR-006.
  • Owner. {{TBD: backend lead}}, due Q2-2026.
  • Verification. E2E test asserts 401 unauthenticated and 403 cross-user; bet.controller.ts review checklist for guard presence.
  • Related advisories. None external; internal ID SR-001.

SR-002 (legacy SF-013) — Vault transfer overdraft

  • Title. POST /accounting/to-vault allows overdraft of primary balance — repository UPDATE has no WHERE amount >= N clause.
  • CVSS. AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N → 6.5 (Medium). Promoted to Critical in our rubric: financial-integrity, end-to-end money creation feasible by chained vault round-trip.
  • CWE. CWE-840 (Business Logic Errors), CWE-682 (Incorrect Calculation).
  • Affected files. apps/api/src/.../user-balance.repository.ts:116-143 (UserBalanceRepository.toVault). Schema: user_balance.amount lacks CHECK (amount >= 0) (vault side has it).
  • Repro.
    POST /accounting/to-vault {"currencyId":"DBC","amount":"99999"}
    # initial balance 1000 → 201 with afterBalance: -98999
    
  • PoC. End-to-end "minting" possible by combining overdraft with a subsequent vault → main transfer.
  • Mitigation. Pinned by tests-e2e/tests/dropbet-wallet.spec.ts:119-133. Planned fix:
    where: { amount: { gte: new Prisma.Decimal(N) } }
    // map rowcount=0 → ACCOUNTING_BALANCE_INSUFFICIENT
    
    Add ALTER TABLE user_balance ADD CONSTRAINT amount_check CHECK (amount >= 0) (also closes SR-009).
  • Owner. {{TBD: accounting team}}, due Q2-2026.
  • Verification. E2E flips from "overdraft succeeds" to "overdraft rejected with ACCOUNTING_BALANCE_INSUFFICIENT". Migration assertion confirms CHECK constraint exists in prod.
  • Related advisories. Internal-only.

SR-003 (legacy FM-C-1) — Promo claim endpoint unregistered

  • Title. PromoController.claimPromoCode has guards/throttle/swagger but no @Post() decorator at apps/api/src/promo/promo.controller.ts:99 — Nest skips route discovery; every claim returns 404.
  • CVSS. N/A — functional defect, not a security vulnerability. Tracked in the security register because of revenue impact and because it was uncovered by a security audit pass.
  • CWE. N/A.
  • Affected files. apps/api/src/promo/promo.controller.ts:99-100.
  • Repro.
    curl -X POST http://localhost:4000/promo/public/TESTCODE -H "Authorization: Bearer <jwt>"
    # 404 (Nest never registered the handler)
    
  • PoC. N/A.
  • Mitigation. Add @Post('public/:code') at :99. Pinned by tests-e2e/tests/dropbet-challenges.spec.ts 404 assertion.
  • Owner. {{TBD: promotions team}}, due Q2-2026.
  • Verification. E2E flips from 404 to 201. Smoke: visible promo claim from FE works.
  • Related advisories. None.

High

SR-004 (legacy SF-029) — SuperAdmin shortcuts MFA gate

  • Title. permission.guard.ts:24 short-circuits Role.SuperAdmin to true BEFORE the if (!user.mfaSecret) check at :40 — SuperAdmin without mfaSecret bypasses MFA.
  • CVSS. AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H → 7.2 (High).
  • CWE. CWE-287 (Improper Authentication), CWE-308 (Use of Single-factor Authentication).
  • Affected files. apps/api/src/admin/permission.guard.ts:24 and :40.
  • Repro. Sign in as admin@admin.com (seeded SuperAdmin, no mfaSecret). Call POST /admin/bets → 200 without MFA challenge.
  • PoC. Internal smoke during admin-bets investigation.
  • Mitigation. Move SuperAdmin shortcut below MFA branch, or require MFA enrollment at SuperAdmin promotion step. Compensating control: SuperAdmin enrollment locked behind change-management ticket.
  • Owner. {{TBD: platform-auth}}, due Q2-2026.
  • Verification. E2E asserts SuperAdmin without mfaSecret receives 403 with MFA_REQUIRED on protected endpoints.
  • Related advisories. Internal-only.

SR-005 (legacy SF-009) — BetInfoQuery.userId is a dead field

  • Title. Even with auth restored (SR-001), getBetInfoSettled ignores userId; user A reads user B's bet detail.
  • CVSS. AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N → 6.5 (Medium). Treated High because it directly chains with SR-001 to defeat ownership.
  • CWE. CWE-639 (Authorization Bypass Through User-Controlled Key).
  • Affected files. apps/api/src/bet/bet.repository.ts (getBetInfoSettled); DTO at bet.dto.ts exposes the dead userId.
  • Repro. After SR-001 fix lands, supply userId = <other> in query — service ignores it.
  • PoC. N/A (combined with SR-001).
  • Mitigation. Add getBetInfoSettledForUser(betId, userId) with WHERE userId = :userId. Combined PR with SR-001.
  • Owner. {{TBD: backend}}, due Q2-2026.
  • Verification. E2E asserts cross-user 403/404.
  • Related advisories. None.

SR-006 (legacy SF-010) — @Cacheable key omits userId

  • Title. @Cacheable on bet-info path keys by betId only; post-ban window can serve a stale bet to a different caller.
  • CVSS. AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N → 3.1 (Low). Promoted High in rubric due to leak of seed data overlap.
  • CWE. CWE-524 (Use of Cache Containing Sensitive Information).
  • Affected files. apps/api/src/bet/bet.controller.ts @Cacheable decorator.
  • Repro. Coordinated requests in TTL window after ban.
  • PoC. N/A.
  • Mitigation. Include userId in cache key; reduce TTL. Compensating control: short TTL.
  • Owner. {{TBD: backend}}, due Q2-2026.
  • Verification. Code review checklist; manual cache-key audit.
  • Related advisories. None.

SR-007 (legacy SF-004) — Double-settle backstop

  • Title. Concurrent bet-place with same (roundId, userId) returns generic 500 (ApiCode.INTERNAL) on P2002 instead of BET_DUPLICATE.
  • CVSS. N/A — error-mapping defect.
  • CWE. CWE-755 (Improper Handling of Exceptional Conditions).
  • Affected files. apps/api/src/bet/bet.service.ts placeBet; Prisma schema Bet @@unique([roundId, userId]).
  • Repro. Two concurrent place-bet requests with identical (roundId, userId). One succeeds, one returns 500.
  • PoC. N/A.
  • Mitigation. Catch Prisma P2002, map to BET_DUPLICATE. Consider advisory lock on (roundId, userId) before INSERT.
  • Owner. {{TBD: bet-pipeline team}}, due Q3-2026.
  • Verification. E2E asserts 409 + BET_DUPLICATE payload on concurrent duplicate bets.
  • Related advisories. None.

SR-008 (legacy SF-005) — Fairness seed race

  • Title. @PlaceBetLock Redis-backed lock — if TTL expires mid-handler, two handlers reuse the same nonce; identical randomValue for distinct bets.
  • CVSS. AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:N → 5.9 (Medium). Promoted High: provably-fair integrity.
  • CWE. CWE-362 (Race Condition).
  • Affected files. popUserSeed in seed/RNG service; @PlaceBetLock decorator.
  • Repro. Force lock-TTL expiry under contention.
  • PoC. Theoretical; no observed exploit in production.
  • Mitigation. Conditional nonce++ inside popUserSeed, or Postgres sequence instead of Redis-locked counter. Compensating: lock TTL > p99 handler latency.
  • Owner. {{TBD: fairness-team}}, due Q3-2026.
  • Verification. Stress-test under forced lock-expiry confirms unique nonces.
  • Related advisories. None.

SR-009 (legacy SF-006) — No Postgres CHECK on balance

  • Title. user_balance.amount lacks CHECK (amount >= 0); only the bet-place WHERE clause guards negative balances at runtime.
  • CVSS. AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N → 4.4 (Medium).
  • CWE. CWE-20 (Improper Input Validation), CWE-840 (Business Logic Error).
  • Affected files. Prisma schema user_balance table.
  • Repro. N/A; latent — only triggered if a future refactor drops the runtime guard.
  • PoC. N/A.
  • Mitigation. ALTER TABLE user_balance ADD CONSTRAINT amount_check CHECK (amount >= 0). Combined with SR-002 migration.
  • Owner. {{TBD: accounting}}, due Q2-2026.
  • Verification. Migration applied; integration test attempts negative INSERT and expects 23514.
  • Related advisories. None.

SR-010 (legacy SF-027) — Bo route has no HTTP guard

  • Title. BetHttpController (apps/bo/src/bet/bet-http.controller.ts) has no @UseGuards; any logged-in JWT holder triggers a 5 s hang → 500 (combined with SR-018 dead route).
  • CVSS. AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L → 4.3 (Medium). Promoted High: easy DoS via small connection pool.
  • CWE. CWE-862 (Missing Authorization), CWE-770 (Resource Allocation Without Limits).
  • Affected files. apps/bo/src/bet/bet-http.controller.ts.
  • Repro. POST :4003/bets with Authorization: Bearer <player-token> → 5026 ms → 500.
  • PoC. 100 concurrent calls hold 100 ClientProxy sockets open for 5 s each.
  • Mitigation. Add @UseGuards(RolesGuard) + @Roles(Role.Admin), or delete the deprecated controller (preferred — ties to SR-018).
  • Owner. {{TBD: bo team}}, due Q2-2026.
  • Verification. tests-e2e/tests/admin-bets.spec.ts:152-168 flips from "500 + 4.5 s hang" to "401" or 404.
  • Related advisories. None.

SR-011 (legacy FM-R-1) — Shared JWT secret for verify + reset

  • Title. JWT_VERIFICATION_TOKEN_SECRET signs both email-verification and password-reset tokens (user.service.ts:858, 893).
  • CVSS. AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N → 8.2 (High) — secret leak compromises both flows.
  • CWE. CWE-321 (Use of Hard-coded Cryptographic Key), CWE-798.
  • Affected files. apps/api/src/.../user.service.ts:858, 893. Env: JWT_VERIFICATION_TOKEN_SECRET.
  • Repro. Mint a token signed with the verify secret → use as a reset token → succeeds.
  • PoC. Demonstrated in tests-e2e/tests/dropbet-password-reset.spec.ts:42-50.
  • Mitigation. Separate secrets per token type. Add a DB-side one-shot token table with consumed_at (also closes SR-012).
  • Owner. {{TBD: auth team}}, due Q2-2026.
  • Verification. Cross-token replay returns 401; secrets-rotation runbook updated.
  • Related advisories. None.

SR-012 (legacy FM-R-2) — Reset token reusable within TTL

  • Title. Reset-token replay within 1200 s succeeds — no consumed_at tracking.
  • CVSS. AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N → 5.3 (Medium). Promoted High: combined with SR-011 enables persistent compromise.
  • CWE. CWE-294 (Authentication Bypass by Capture-replay).
  • Affected files. user.service.ts resetUserPassword.
  • Repro. Issue reset → reset password → reuse same token within TTL → second reset succeeds.
  • PoC. tests-e2e/tests/dropbet-password-reset.spec.ts steps 2 & 4.
  • Mitigation. consumed_at timestamp / jti blacklist; reject reused tokens.
  • Owner. {{TBD: auth team}}, due Q2-2026.
  • Verification. E2E asserts 410 on second use of same token.
  • Related advisories. None.

SR-013 (legacy FM-R-3) — Sessions survive password reset — Mitigated

  • Title. Previously: resetUserPassword did not invalidate auth-session:<userId>:* in Redis or revoke refresh tokens.
  • CVSS (historical). AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N → 7.6 (High).
  • CWE. CWE-613 (Insufficient Session Expiration).
  • Affected files (historical). user.service.ts resetUserPassword.
  • Mitigation. Now clears auth-session:<userId>:* and revokes refresh tokens on reset. Verified by smoke + E2E.
  • Owner. Auth team — closed.
  • Verification. E2E asserts old access_token cookie returns 401 after reset.

Medium

SR-014 (legacy SF-001) — Email-enumeration timing oracle

  • Title. POST /auth/sign-in returns ~95 ms for known email vs ~15 ms for unknown — timing divergence.
  • CVSS. AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N → 3.1 (Low). Promoted Medium: ease of automated probing.
  • CWE. CWE-203 (Observable Discrepancy).
  • Affected files. apps/api/src/.../user.service.ts:715-720.
  • Repro. 1000-request timing diff between known/unknown email separates cleanly.
  • Mitigation. Constant-time response — always run bcrypt.compare against a dummy hash on unknown email.
  • Owner. {{TBD: auth}}, due Q3-2026.
  • Verification. Histogram of response time on unknown emails overlaps known.

SR-015 (legacy SF-002) — Lockout counter reset

  • Title. handleLoginAttempt (user.service.ts:935-941) deletes attemptsKey when lockout fires; lockout TTL == attempts TTL.
  • CVSS. AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N → 5.3 (Medium).
  • CWE. CWE-307 (Improper Restriction of Excessive Authentication Attempts).
  • Affected files. user.service.ts:935-941.
  • Repro. Exhaust MAX_LOGIN_ATTEMPTS → wait USER_LOCKOUT_DURATION_SECONDS → fresh budget.
  • Mitigation. Retain attempts key past lockout window; exponential cooldown.
  • Owner. {{TBD: auth}}, due Q3-2026.
  • Verification. Brute-force harness sees escalating delay.

SR-016 (legacy SF-003) — Thin DTO validation

  • Title. SignInDto (user-login.dto.ts:27-40), VerifyMfaDto, sign-up referrer lack @MaxLength / @IsEmail / @Length(6,6).
  • CVSS. AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L → 5.3 (Medium).
  • CWE. CWE-20.
  • Affected files. user-login.dto.ts:27-40; verify-mfa.dto.ts; sign-up DTO.
  • Repro. 1 MB password field reaches bcrypt.compare.
  • Mitigation. @IsEmail() + @MaxLength(254) on email, @MaxLength(128) on password, @Length(6,6) on mfaCode, @MaxLength(2048) on referrer. Edge WAF caps body size as compensating control.
  • Owner. {{TBD: auth}}, due Q3-2026.

SR-017 (legacy SF-016) — O(n_sockets) balance push

  • Title. client.gateway.ts:306-315 handleServerEvent iterates clientSockets.forEach per BalanceUpdated.
  • CVSS. AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L → 3.1 (Low). Promoted Medium for production-load impact.
  • CWE. CWE-405 (Asymmetric Resource Consumption).
  • Affected files. apps/rt/src/.../client.gateway.ts:306-315.
  • Repro. 1000 concurrent sockets + bet — every balance event scans all sockets.
  • Mitigation. Per-user socket.io rooms: this.server.to('user:'+id).emit(...).
  • Owner. {{TBD: rt-team}}, due Q3-2026.
  • Verification. Profiler shows O(1) emit time.

SR-018 (legacy SF-026) — Dead bo route hangs 5s

  • Title. BetHttpController forwards to Private.BetFindMany over Redis transport, but no @MessagePattern subscriber exists; timeout(5000) in events.gateway.ts:83 always fires.
  • CVSS. AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L → 4.3 (Medium).
  • CWE. CWE-561 (Dead Code), CWE-770.
  • Affected files. apps/bo/src/bet/bet-http.controller.ts; apps/bo/src/.../events.gateway.ts:83.
  • Mitigation. Delete the deprecated controller (JSDoc says @deprecated) — preferred. Combined with SR-010.
  • Owner. {{TBD: bo}}, due Q2-2026.
  • Verification. tests-e2e/tests/admin-bets.spec.ts:151-160 adjusts to 404 / 401.

SR-019 (legacy FM-R-4) — Email enumeration via reset-cooldown

  • Title. resendPasswordResetEmail cooldown response timing reveals known emails.
  • CVSS. AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N → 5.3 (Medium).
  • CWE. CWE-203.
  • Affected files. user.service.ts resendPasswordResetEmail.
  • Mitigation. Constant-time path on cooldown miss.
  • Owner. {{TBD: auth}}, due Q3-2026.

SR-020 (legacy FM-S-3) — Sign-up duplicate-email race

  • Title. Duplicate-email race at auth.service.ts:67-86: loser receives 500 (P2002) instead of 400; bots can fingerprint live users.
  • CVSS. AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N → 3.7 (Low). Promoted Medium: enumeration channel.
  • CWE. CWE-209 (Generation of Error Message Containing Sensitive Information), CWE-755.
  • Affected files. auth.service.ts:67-86.
  • Mitigation. Catch P2002, return 400 EMAIL_TAKEN. Pre-check inside transaction.
  • Owner. {{TBD: auth}}, due Q3-2026.

SR-021 (legacy FM-S-1) — Local-dev CAPTCHA bypass — Accepted

  • Title. RecaptchaService accepts 'pass' when NODE_ENV=local.
  • Affected files. recaptcha.service.ts:28.
  • Status. Accepted. Production env never reaches this code path; NODE_ENV is set at runtime by Doppler-injected variables, not via the request.
  • Compensating control. Production deploy pipeline asserts NODE_ENV !== 'local'.
  • Verification. CI gate: deploy-time env-var assertion in terraform/.

SR-022 (legacy FM-BJ-2) — Abandoned blackjack hand locks funds

  • Title. blackjack.service.ts has no TTL auto-resolution for abandoned hands.
  • CVSS. AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:L → 3.5 (Low). Promoted Medium: support-ticket volume.
  • CWE. CWE-840.
  • Affected files. apps/api/src/blackjack/blackjack.service.ts.
  • Mitigation. TTL watchdog; auto-resolve to dealer-stand on N minutes idle.
  • Owner. {{TBD: games team}}, due Q3-2026.

SR-023 (legacy FM-C-4) — .getMilliseconds() bug

  • Title. promo.service.ts:36 calls .getMilliseconds() (0-999) instead of .getTime() — comparison always false.
  • CVSS. N/A — logic defect.
  • CWE. CWE-682.
  • Affected files. apps/api/src/promo/promo.service.ts:36.
  • Mitigation. Replace .getMilliseconds() with .getTime(). Blast radius small (BullMQ delayed job is primary path).
  • Owner. {{TBD: promotions}}, due Q3-2026.

SR-024 (legacy FM-SR-3) — Speed-roulette queue deadlock

  • Title. roulette-state.processor.ts:23 concurrency: 1; if a job exhausts retries without re-adding follow-up, queue stalls.
  • CVSS. AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H → 5.9 (Medium).
  • CWE. CWE-833 (Deadlock).
  • Affected files. apps/speed-roulette/src/.../roulette-state.processor.ts:23.
  • Mitigation. Watchdog cron checks for stale queue and re-enqueues bootstrap job.
  • Owner. {{TBD: speed-roulette team}}, due Q3-2026.

SR-025 (legacy AF-1) — Cross-service trace propagation blocked

  • Title. Four stacked defects between admin-fe and ebit-api: cookie-name mismatch, missing @vercel/otel, no propagateContextUrls, hard-coded API host.
  • CVSS. N/A — observability gap.
  • CWE. CWE-778 (Insufficient Logging).
  • Affected files. ebit-admin-fe/middleware.ts; ebit-admin-fe/sentry.*.config.ts; ebit-admin-fe/instrumentation.ts.
  • Mitigation. Documented in docs/flows/admin-sign-in.md, docs/flows/admin-user-mgmt.md. Phased fix in flight.
  • Owner. {{TBD: observability}}, due Q2-2026.

SR-026 (legacy AF-2) — Redis pub/sub trace context — Accepted

  • Title. Nest Redis pub/sub transport does not propagate W3C traceparent; callee spans are orphan roots.
  • Status. Accepted — observability gap, no customer impact. Documented; will revisit on next observability roadmap.

Low

(Truncated for brevity in this introductory commit; legacy mapping below ensures every legacy ID resolves. Full per-finding detail to be migrated in subsequent passes.)

SR-030 (legacy AF-3) — RT scale: per-instance clientSockets Map

apps/rt/src/.../client.gateway.ts clientSockets is a per-instance Map. Scaling past 1 replica drops per-user emits. Owner: {{TBD: rt-team}}, due Q3-2026. Mitigation: Redis-backed presence + socket.io adapter.

SR-031 (legacy SF-007) — Settle queue fire-and-forget

bet_settled_queue Redis-backed; outage loses side-effects. Owner: {{TBD: bet-pipeline}}, due Q4-2026.

SR-032 (legacy SF-011) — Bet status index missing

No covering index for status on bet; power-user list degraded. Owner: {{TBD: db}}, due Q3-2026.

SR-033 (legacy SF-012) — Sportsbook bets hidden by hard-coded filter

bet.repository.ts:280 filters out sportsbook bets. Owner: {{TBD: bet-pipeline}}, due Q3-2026.

SR-034 (legacy SF-014) — No cache on /accounting/balances

Hottest JWT path uncached. Owner: {{TBD: wallet}}, due Q3-2026.

SR-035 (legacy SF-015) — Transaction ledger only on rt

Only via Private.TransactionFindMany. Owner: {{TBD: wallet}}, due Q3-2026.

SR-036 (legacy SF-017) — usdAmount request-time FX

ExchangeRatesService.toUsd uses request-time rate, not row-stamped. Owner: {{TBD: wallet}}, due Q3-2026.

SR-037 (legacy SF-018) — TETH / ETH ambiguity

CurrencySymbolBalance exposes test currency identically. Owner: {{TBD: wallet}}, due Q4-2026.

SR-038 (legacy SF-019) — RACE_ENABLED per-handler

Inline guard, easy to forget. Owner: {{TBD: leaderboard}}, due Q4-2026.

SR-039 (legacy SF-020) — LeaderboardQueueProducer dead

Zero call sites. Owner: {{TBD: leaderboard}}, due Q4-2026.

SR-040 (legacy SF-021) — Worker no OTel span

leaderboardService.handleBet BullMQ worker missing instrumentation. Owner: {{TBD: observability}}, due Q4-2026.

SR-041 (legacy SF-022) — In-process Map cache 60s — Accepted

api vs bo serve up to 60s stale. Documented staleness budget.

SR-042 (legacy SF-024) — Daily race bootstrap once at boot

updateLeaderboards. Owner: {{TBD: leaderboard}}, due Q3-2026.

SR-043 (legacy SF-025) — Sequential count+findMany on admin-side

bet.repository.ts:339. Owner: {{TBD: bo}}, due Q4-2026.

SR-044 (legacy SF-028) — No adjust/void/rollback endpoint — Accepted

By design — corrections via balance adjustment.

SR-045 (legacy SF-030) — PaginatedDto no aggregate fields

Owner: {{TBD: bo}}, due Q4-2026.

SR-046 (legacy FM-AUM-1) — Audit filter scopes by actor

/admin/user/admin-audit?userId=X filters by admin actor, not target. Owner: {{TBD: bo}}, due Q3-2026.

SR-047 (legacy FM-AUM-2) — banUser ignores admin param

Sole audit is AdminActionLog and safeLog swallows. Owner: {{TBD: bo}}, due Q3-2026.

SR-048 (legacy FM-AUM-3) — No multi-ban route

TODO at admin.user.controller.ts:148. Owner: {{TBD: bo}}, due Q4-2026.

SR-049 (legacy FM-AUM-4) — /ban not idempotent

Owner: {{TBD: bo}}, due Q4-2026.

SR-050 (legacy FM-ASI-3) — Bad-JWT silent fall-through

middleware.ts:68-90 empty catch with /* TODO */. Owner: {{TBD: admin-fe}}, due Q3-2026.

SR-051 (legacy FM-ASI-5) — Origin-based admin gate

Strip header → demote check. Owner: {{TBD: admin-fe}}, due Q3-2026.

SR-052 (legacy FM-RT-5) — handleDisconnect no zremAccepted

Online window extends to TTL; product-acceptable.


Legacy → SR ID mapping

Legacy SR Legacy SR
SF-008 SR-001 SF-022 SR-041
SF-013 SR-002 SF-024 SR-042
FM-C-1 SR-003 SF-025 SR-043
SF-029 SR-004 SF-028 SR-044
SF-009 SR-005 SF-030 SR-045
SF-010 SR-006 FM-AUM-1 SR-046
SF-004 SR-007 FM-AUM-2 SR-047
SF-005 SR-008 FM-AUM-3 SR-048
SF-006 SR-009 FM-AUM-4 SR-049
SF-027 SR-010 FM-ASI-3 SR-050
FM-R-1 SR-011 FM-ASI-5 SR-051
FM-R-2 SR-012 FM-RT-5 SR-052
FM-R-3 SR-013 (closed) AF-1 SR-025
SF-001 SR-014 AF-2 SR-026
SF-002 SR-015 AF-3 SR-030
SF-003 SR-016 AF-4 (descoped)
SF-016 SR-017 AF-5 (intentional)
SF-026 SR-018 AF-6 (intentional)
FM-R-4 SR-019 AF-7 (closed)
FM-S-3 SR-020 SF-007 SR-031
FM-S-1 SR-021 SF-011 SR-032
FM-BJ-2 SR-022 SF-012 SR-033
FM-C-4 SR-023 SF-014 SR-034
FM-SR-3 SR-024 SF-015 SR-035
SF-017 SR-036
SF-018 SR-037
SF-019 SR-038
SF-020 SR-039
SF-021 SR-040

Statistics

Severity Total Mitigated In Progress Accepted Open
Critical 3 0 3 0 0
High 10 1 7 0 2
Medium 13 0 4 2 7
Low 23 0 5 5 13
Total 49 1 19 7 22

E2E coverage: 3/3 critical findings pinned by E2E tests; 4/10 high.


Cross-references

  • Customer-facing register: docs/security/client/risk-register.md
  • Threat model: docs/security/internal/threat-model.md
  • Dependency audit: docs/security/internal/dependency-audit.md
  • Per-finding deep-dives: docs/security-findings/sf-*.md
  • Source flow docs: docs/flows/*.md §6