Study/audit-results/CONFIRMED_FINDINGS.md

9.6 KiB

Confirmed Findings

Severity: Critical / High / Medium / Low / Observation. Confidence: Confirmed / High-confidence inference / Unverified risk. Full structured detail for every finding (files, functions, scenario, expected/actual, root cause, impact, evidence, fix direction, complexity, regression test, runtime-confirmation flag) is in FINDINGS.json (validated). Source IDs from specialist auditors are in parentheses.

ID Severity Confidence Title Area
FIN-01 (DBAUD-01, GRP-01) Critical Confirmed Schema/migration drift: MaterialGroup table + groupId columns never migrated — fresh deployments' databases are incompatible with the app (verified: no such column: Deck.groupId, no such table: MaterialGroup) DB/migrations
FIN-02 (AUTH-01, OPS-03) Critical High-confidence inference Hardcoded fallback SESSION_SECRET ("dev-session-secret-change-in-production-must-be-32-chars") in src/lib/auth.ts:15 + src/proxy.ts:8-9; empty env var in shipped compose → forgeable session cookie → full auth bypass Auth/deploy
FIN-03 (QUIZ-01, ADV-01) High Confirmed In-viewer "Retake Missed" never sets retakeIds prop → persisted as a full attempt with a wrong, lower score; pollutes SEQUENTIAL progress Quiz
FIN-04 (CARD-01, IMPT-01, QUIZ-06) High Confirmed Stale card ids in saved progress never filtered on resume (filterAndClampOrder dead code) → deleting a card mid-session bricks the flashcard study session Flashcards
FIN-05 (OPS-01) High Confirmed Port mismatch: container listens on 3726, compose publishes 3000:3000 → production deployment unreachable Deploy
FIN-06 (OPS-06) High High-confidence inference No .dockerignore: Windows-host builds inject win32 native modules (better-sqlite3, argon2) into the Linux image Deploy
FIN-07 (OPS-08) High Confirmed docker-compose.override.yml auto-merges on plain docker compose up → production command silently runs dev mode Deploy
FIN-08 (AUTH-04, ADV-09) Medium High-confidence inference First-login provisioning takeover; ADMIN_PASSWORD_HASH env is dead config Auth
FIN-09 (AUTH-02) Medium High-confidence inference Proxy auth bypass for any path containing . (latent today; would expose any future non-UUID-id route) Auth
FIN-10 (AUTH-03) Medium High-confidence inference Password-reset abuse: token overwrite DoS, spoofable x-forwarded-for rate limits, unthrottled complete Auth
FIN-11 (QUIZ-02) Medium High-confidence inference Attempt route stores unvalidated answersJson; duplicate option ids inflate SATA credit; malformed shapes → 500 Quiz
FIN-12 (QUIZ-03, FE-04) Medium Confirmed Quiz Finish double-submit creates duplicate attempts; failure path silent Quiz/FE
FIN-13 (QUIZ-04) Medium High-confidence inference Progress PATCH can land after Finish's DELETE → stale "Continue" row for a completed quiz Quiz
FIN-14 (CARD-02, FE-05, QUIZ-09) Medium High-confidence inference Fire-and-forget, unsequenced progress autosaves: out-of-order PATCHes regress resume state; multi-tab last-write-wins Flashcards/Quiz
FIN-15 (CARD-03, ADV-07) Medium Confirmed Completed flashcard session never persisted as complete → resume re-shows the last graded card Flashcards
FIN-16 (CARD-04) Medium Confirmed SRS review endpoint enforces membership only — no due-date or newCardsPerDay enforcement server-side SRS
FIN-17 (CARD-05, ADV-02) Medium Confirmed Unbounded DB scans: whole StudyActivity table per fetch; all SRS card states per set; Navbar polls every 60 s Perf/DB
FIN-18 (FE-01, GRP-03, FE-02, GRP-05) Medium Confirmed Library optimistic mutations are fire-and-forget (no res.ok, no rollback); cross-group reorder omits origin group from payload FE/Groups
FIN-19 (GRP-02, CARD-07, AUTH-06) Medium Confirmed Reorder/create endpoints trust client groupId/sortOrder — no class/type/existence validation; cross-class group membership can leak content into another class's share link Groups
FIN-20 (FE-03) Medium Confirmed Shared quiz session resets and options reshuffle on any parent re-render (unstable inline quiz prop) FE
FIN-21 (IMPT-02, FE-08, QUIZ-05, ADV-10) Medium Confirmed /api/progress PATCH completely unvalidated; unguarded client JSON.parse of persisted progress crashes pages Quiz/FE
FIN-22 (OPS-02) Medium High-confidence inference Entrypoint migrate-deploy chain fragile: crash loop on conflicting DB state; runner prisma CLI install unverified/unpinned Deploy
FIN-23 (OPS-05) Medium Confirmed Non-reproducible installs: npm install (not ci); unpinned prisma@^7.8.0 re-install re-resolves the whole tree in the runner Deploy
FIN-24 (OPS-07) Medium Confirmed No healthcheck, no backup, no documented recovery Deploy
FIN-25 (OPS-10, TEST-10) Medium Confirmed CI builds/pushes without tests, lint, prisma validate, or container smoke test — deployment-breaking defects ship green CI
FIN-26 (QUIZ-07) Low Confirmed SATA scoring divides by correctIds.length with no zero-guard → NaN Quiz
FIN-27 (QUIZ-08) Low Confirmed Historical review/category breakdown recompute scores from current content, not stored attempt Quiz
FIN-28 (IMPT-03) Low Confirmed Card add/edit endpoints bypass Zod: empty strings storable; CreateTab silently drops incomplete cards Imports
FIN-29 (IMPT-04) Low Confirmed No payload size/string-length/array-length caps on import schemas/routes; whitespace-only names accepted Imports
FIN-30 (IMPT-05) Low Confirmed SATA constraint mismatch: instructions say ≥2 correct, schema enforces ≥1 Imports
FIN-31 (IMPT-06) Low Confirmed Unvalidated name/groupId override fields on import POST routes → 500; PATCH routes mask failures as 404 Imports
FIN-32 (GRP-04) Low Confirmed Group deletion leaves duplicate sortOrder values in Uncategorized; no unique constraint/tie-break Groups
FIN-33 (GRP-06) Low Confirmed Keyboard users cannot move items between groups (empty handleDragOver) Groups/a11y
FIN-34 (CARD-08) Low High-confidence inference Restart deletes progress fire-and-forget; slow DELETE can remove the fresh session's progress row Flashcards
FIN-35 (CARD-09) Low Confirmed Concurrent first review of same new card → P2002 → generic 500 instead of 409 SRS
FIN-36 (CARD-10) Low High-confidence inference "Previous card" during the 350 ms grade animation races the pending timeout Flashcards
FIN-37 (CARD-11) Low Confirmed SRS set page never refreshes on focus → stale membership after deck deletion in another tab SRS/FE
FIN-38 (CARD-06) Low Confirmed Day boundary hardcoded to Arizona (UTC-7): "today"/new-card limit/streak roll over at 07:00 UTC SRS
FIN-39 (AUTH-05) Low Confirmed Proxy destroy() cookie-clear lost on redirect; layout check ignores sessionGeneration Auth
FIN-40 (AUTH-08) Low Confirmed Session cookie Secure flag off in shipped compose (plain HTTP, no TLS story) Auth/deploy
FIN-41 (DBAUD-02, AUTH-07) Low Confirmed /api/share accepts arbitrary targetType → repeatable junk all-NULL ShareLink rows Auth/DB
FIN-42 (DBAUD-03) Low High-confidence inference dev.db committed in git history (8 commits), remains in blobs on the LAN remote Repo hygiene
FIN-43 (OPS-09) Low Confirmed .gitignore gaps (data/, study.db*); scratch files out.css/temp.css/test.css tracked Repo hygiene
FIN-44 (FE-07, ADV-04) Low Confirmed Dashboard fetchClasses no .catch/res.ok → unhandled rejection, misleading empty state (or render crash) FE
FIN-45 (FE-09) Low Confirmed Effect fetches without .catch in GenerateTab and ShareMenu FE
FIN-46 (FE-10) Low High-confidence inference Cross-class navigation fetch race renders wrong-class data; module cache never invalidated FE
FIN-47 (ADV-03) Low Confirmed Logout has no error handling — failed logout strands the user FE
FIN-48 (ADV-05) Low Confirmed slugify can yield an empty slug (unreachable class); renames never update the URL slug Classes
FIN-49 (ADV-08) Low High-confidence inference Shared-viewer localStorage keys item-scoped, not token-scoped → session bleed across tokens Sharing/FE
FIN-50 (GRP-07) Observation Confirmed Group sortOrder uses inverted desc convention with unvalidated PATCH — latent trap Groups
FIN-51 (FE-11) Observation Confirmed Collapsed-groups state read in effect → one-frame expand flash FE
FIN-52 (AUTH-10) Observation Confirmed Shared quiz links ship the full answer key to anonymous viewers — by design, worth a warning Sharing
FIN-53 (TEST-01) Low Confirmed False-confidence test: "distinct valid state for every rating" never asserts distinctness Tests

Notes on consolidation

  • Duplicate findings across specialist reports were merged under one root cause (see sourceIds).
  • FE-06 ("all /api/* routes unauthenticated") was refuted by the adversarial review — src/proxy.ts IS the compiled middleware and enforces session checks; the residual truth is FIN-09 (dot-bypass). See REJECTED_FINDINGS.md.
  • OPS-02's fresh-volume crash-loop framing was weakened by the adversarial review (on a fresh volume migrate deploy succeeds; the app then fails per FIN-01) — reframed as FIN-22.
  • FIN-18 severity downgraded from the frontend auditor's High to Medium after adversarial review (no visible break for a single user; silent divergence self-heals on reload).