Security review of the current cc-financial codebase after the security hardening pass.
src/app/admin/login/page.tsx now passes redirect targets through safeAdminRedirect(). Only /admin and /admin/... paths are accepted; absolute URLs, protocol-relative URLs, non-admin paths, backslash/newline payloads, and empty values fall back to /admin.
Login attempts are now rate-limited in memory by IP/email key via src/lib/security.ts. This is suitable as an application-level baseline, but production should still use reverse-proxy or platform-level rate limiting because in-memory counters reset on restart and do not coordinate across instances.
State-changing admin actions and POST routes now call assertSameOriginRequest() before mutating data. This covers the main server actions/routes for:
- login
- income create/edit/delete
- expense create/edit/delete
- giveaway create/edit/delete
- recurring expense create/edit/delete
- Twitch payment import
- Twitch disconnect
- EventSub registration
- logout
src/app/api/admin/profit-loss.csv/route.ts now uses csvSafeCell() from src/lib/security.ts, which neutralizes spreadsheet formula-like values beginning with =, +, -, @, tab, or carriage return.
The client and server now enforce limits for Twitch payment imports:
- client-side file size limit
- client-side row count limit
- server-side submitted CSV length limit
- server-side parsed row count limit
next.config.ts now sets baseline headers:
X-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originPermissions-Policy- HSTS
- CSP and
X-Frame-Options: DENYoutside overlay routes X-Robots-Tag: noindexon overlay routes
Overlay routes intentionally do not receive X-Frame-Options: DENY because they are meant to be embedded in OBS/browser sources.
src/lib/security.ts now rejects short and known-placeholder production secrets. The checks are used by session signing, overlay token validation, Twitch OAuth state signing, and Twitch EventSub secret validation.
docker-compose.yml now binds the development Postgres port to 127.0.0.1:5432:5432 instead of all host interfaces.
Public pages expose descriptions for expense rows and recurring expense rules that are marked public. This is now an intentional product feature, but it remains a data governance risk.
Impact: invoice numbers, vendor details, account references, or other sensitive text entered into a public row can become visible.
Recommended fix: add separate publicDescription fields for expenses and recurring expenses, keep private/admin descriptions separate, and render only publicDescription on public pages.
src/app/overlay/financial/page.tsx accepts ?token=... and validates it against PUBLIC_OVERLAY_TOKEN.
Impact: URL tokens can leak through logs, browser history, OBS/browser-source configuration, screenshots, referrers, and shared scene collections.
Recommended fix: use per-overlay random tokens stored hashed, support rotation, and avoid long-lived shared query-string secrets where possible.
npm audit --omit=dev reports a moderate production vulnerability:
postcss <8.5.10- Advisory:
GHSA-qx2v-qp2m-jg93 - Issue: XSS via unescaped
</style>in CSS stringify output - Path: bundled through
next/node_modules/next/node_modules/postcss - Current audit status: no direct fix available
Recommended fix: track and apply the Next.js release that updates its bundled PostCSS dependency, and keep production dependency audit in CI.
src/app/api/public/financial-summary/route.ts accepts arbitrary period=YYYY-MM. Public /financial/year also exposes yearly public summaries.
Impact: if historical public financial data should not be broadly queryable, this exposes more information than intended.
Recommended fix: document that historical public transparency is intentional, or restrict public periods to an explicit configured range.
/admin/profit-loss and /api/admin/profit-loss.csv include all admin ledger rows, including private rows and descriptions. This is intended for the owner, but there is no role separation.
Impact: if a future second admin/user role is added, it would receive full private financial/export access by default.
Recommended fix: document this as owner-only, or add role-based authorization before adding multi-user access.
Some Twitch admin flows include raw upstream error messages in query strings and render them on /admin/twitch.
Impact: this can expose provider error details, configuration hints, or overly long messages to the admin UI/logs.
Recommended fix: log detailed errors server-side and show short stable error codes/messages in the UI.
- Prisma is used for database access; reviewed paths do not show obvious SQL injection.
- React renders user-controlled text through normal JSX escaping; reviewed paths do not show obvious direct stored XSS.
- Twitch EventSub webhook verifies signatures and timestamp freshness before processing notifications.
- Edit-page and login redirects now use safe admin-only redirect handling.
.envis ignored by git, and only.env.exampleappears tracked.