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.
JwtGuardcommented out atapps/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:
getBetInfoSettledatbet.repository.ts(no userId predicate; ties to SR-005, SR-006). - Repro.
- 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: RequestExtat:35, plumbreq.user.idinto the service, and addWHERE userId = :userIdingetBetInfoSettled. 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.tsreview checklist for guard presence. - Related advisories. None external; internal ID
SR-001.
SR-002 (legacy SF-013) — Vault transfer overdraft¶
- Title.
POST /accounting/to-vaultallows overdraft of primary balance — repository UPDATE has noWHERE amount >= Nclause. - 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.amountlacksCHECK (amount >= 0)(vault side has it). - Repro.
- 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:Addwhere: { amount: { gte: new Prisma.Decimal(N) } } // map rowcount=0 → ACCOUNTING_BALANCE_INSUFFICIENTALTER 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 confirmsCHECKconstraint exists in prod. - Related advisories. Internal-only.
SR-003 (legacy FM-C-1) — Promo claim endpoint unregistered¶
- Title.
PromoController.claimPromoCodehas guards/throttle/swagger but no@Post()decorator atapps/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.
- PoC. N/A.
- Mitigation. Add
@Post('public/:code')at:99. Pinned bytests-e2e/tests/dropbet-challenges.spec.ts404 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:24short-circuitsRole.SuperAdmintotrueBEFORE theif (!user.mfaSecret)check at:40— SuperAdmin withoutmfaSecretbypasses 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:24and:40. - Repro. Sign in as
admin@admin.com(seeded SuperAdmin, nomfaSecret). CallPOST /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
mfaSecretreceives 403 withMFA_REQUIREDon protected endpoints. - Related advisories. Internal-only.
SR-005 (legacy SF-009) — BetInfoQuery.userId is a dead field¶
- Title. Even with auth restored (SR-001),
getBetInfoSettledignoresuserId; 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 atbet.dto.tsexposes the deaduserId. - 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)withWHERE 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.
@Cacheableon bet-info path keys bybetIdonly; 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@Cacheabledecorator. - Repro. Coordinated requests in TTL window after ban.
- PoC. N/A.
- Mitigation. Include
userIdin 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) onP2002instead ofBET_DUPLICATE. - CVSS. N/A — error-mapping defect.
- CWE. CWE-755 (Improper Handling of Exceptional Conditions).
- Affected files.
apps/api/src/bet/bet.service.tsplaceBet; Prisma schemaBet@@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 toBET_DUPLICATE. Consider advisory lock on(roundId, userId)before INSERT. - Owner. {{TBD: bet-pipeline team}}, due Q3-2026.
- Verification. E2E asserts 409 +
BET_DUPLICATEpayload on concurrent duplicate bets. - Related advisories. None.
SR-008 (legacy SF-005) — Fairness seed race¶
- Title.
@PlaceBetLockRedis-backed lock — if TTL expires mid-handler, two handlers reuse the same nonce; identicalrandomValuefor 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.
popUserSeedin seed/RNG service;@PlaceBetLockdecorator. - Repro. Force lock-TTL expiry under contention.
- PoC. Theoretical; no observed exploit in production.
- Mitigation. Conditional
nonce++insidepopUserSeed, 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.amountlacksCHECK (amount >= 0); only the bet-placeWHEREclause 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_balancetable. - 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/betswithAuthorization: Bearer <player-token>→ 5026 ms → 500. - PoC. 100 concurrent calls hold 100
ClientProxysockets 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-168flips 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_SECRETsigns 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_attracking. - 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.tsresetUserPassword. - Repro. Issue reset → reset password → reuse same token within TTL → second reset succeeds.
- PoC.
tests-e2e/tests/dropbet-password-reset.spec.tssteps 2 & 4. - Mitigation.
consumed_attimestamp / 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:
resetUserPassworddid not invalidateauth-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.tsresetUserPassword. - 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_tokencookie returns 401 after reset.
Medium¶
SR-014 (legacy SF-001) — Email-enumeration timing oracle¶
- Title.
POST /auth/sign-inreturns ~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.compareagainst 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) deletesattemptsKeywhen 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→ waitUSER_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-upreferrerlack@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)onmfaCode,@MaxLength(2048)onreferrer. 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-315handleServerEventiteratesclientSockets.forEachperBalanceUpdated. - 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.
BetHttpControllerforwards toPrivate.BetFindManyover Redis transport, but no@MessagePatternsubscriber exists;timeout(5000)inevents.gateway.ts:83always 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-160adjusts to 404 / 401.
SR-019 (legacy FM-R-4) — Email enumeration via reset-cooldown¶
- Title.
resendPasswordResetEmailcooldown 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.tsresendPasswordResetEmail. - 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.
RecaptchaServiceaccepts'pass'whenNODE_ENV=local. - Affected files.
recaptcha.service.ts:28. - Status. Accepted. Production env never reaches this code path;
NODE_ENVis 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.tshas 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:36calls.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:23concurrency: 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, nopropagateContextUrls, 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 zrem — Accepted¶
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