diff --git a/FUTURE.md b/FUTURE.md index 0f021fe..10c422c 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -3,7 +3,7 @@ **This document tracks potential future enhancements for Bill Tracker.** **Last Updated:** 2026-05-10 -**Current Version:** v0.23.1 +**Current Version:** v0.23.2 ## How to Use This Document @@ -34,21 +34,8 @@ Items are grouped under their priority section heading (`## 🔴 CRITICAL`, `## ### 🔴 CRITICAL -### 🔴 Notification Runner Leaks Bill Details Across Users — CRITICAL -**Priority:** CRITICAL -**Added:** 2026-05-10 by Prime (code review) -**Type:** SECURITY - -**Description:** -`services/notificationService.js` loads every active bill globally (`SELECT * FROM bills WHERE active = 1`) and then loops every notification recipient. In per-user notification mode, this can email one user's bill names, due dates, and amounts to every other opted-in user because the recipient is never matched to `bill.user_id`. - -**Affected Files:** -- `services/notificationService.js:180-233` - -**Potential Fix:** -Fetch bills per recipient with `WHERE user_id = ?`, or group active bills by `user_id` and only send each bill to its owner. Add a regression test with two users to prove cross-user notifications cannot occur. - -**Severity:** CRITICAL +### ~~🔴 Notification Runner Leaks Bill Details Across Users — CRITICAL~~ ✅ FIXED (v0.23.2) +**Moved to HISTORY.md** ### 🟠 HIGH @@ -305,23 +292,8 @@ Add explicit UI copy warning that exports may contain account metadata, and cons **Severity:** LOW -### 🔵 Duplicate Local Login Route Increases Auth Drift Risk — LOW -**Priority:** LOW -**Added:** 2026-05-10 by Prime (code review) -**Type:** TECH_DEBT / SECURITY - -**Description:** -Local username/password login logic exists in both `routes/auth.js` and `routes/authLogin.js`. Only `routes/auth.js` appears mounted in `server.js`, while `authLogin.js` is a near-duplicate public login handler. Duplicate auth code can silently drift on rate limiting, audit logging, CSRF exemptions, and error handling. - -**Affected Files:** -- `routes/auth.js:17-44` -- `routes/authLogin.js:1-39` -- `server.js:73-75` - -**Potential Fix:** -Delete the unused route module or refactor both route registrations to call one shared login handler. Add a startup/router inventory test so orphan auth routes do not linger. - -**Severity:** LOW +### ~~🔵 Duplicate Local Login Route Increases Auth Drift Risk — LOW~~ ✅ FIXED (v0.23.2) +**Moved to HISTORY.md** ### Add comprehensive unit and integration tests diff --git a/HISTORY.md b/HISTORY.md index dc36717..52bec73 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,19 @@ # Bill Tracker — Changelog +## v0.23.2 + +### Security +- **CRITICAL: Notification privacy leak fix** — In per-user notification mode, bills were sent to all opted-in recipients regardless of ownership. Added ownership filter (`bill.user_id !== recipient.id`) and orphaned bill guard. Security audit by Private_Hudson confirmed the fix is airtight. +- **Duplicate login route removed** — Deleted `routes/authLogin.js`, consolidating login logic into `routes/auth.js` only. + +### Changed +- `services/notificationService.js`: Added per-user ownership filter and null `user_id` guard in notification runner +- `routes/authLogin.js`: Removed (consolidated into `routes/auth.js`) +- `docs/Engineering_Reference_Manual.md`: Removed stale `authLogin.js` duplicate route note, updated version to 0.23.2 +- `README.md`: Updated to reflect current features, env vars, security notes, project structure, and known limitations + +--- + ## v0.23.1 ### Added diff --git a/README.md b/README.md index 4ae339f..b853fc2 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,14 @@ # BillTracker -

- Tracker logo +

+ Tracker logo

BillTracker is a self-hosted app for tracking recurring bills, monthly payments, due dates, categories, and personal bill history. It runs as a Node/Express server with a React frontend and stores data in SQLite. This product was produced with the assistance of AI.

-Demo Server -https://t1.scheller.ltd/ -
-Username: guest -
-Password: guest123 +Demo Server: https://t1.scheller.ltd/
+Username: guest · Password: guest123

## Screenshots @@ -27,15 +23,16 @@ Password: guest123 ## What Is BillTracker? - BillTracker helps a household or small self-hosted setup keep bill data in one place: -- recurring bill records with due day, expected amount, category, notes, autopay details, and optional APR +- recurring bill records with due day, expected amount, category, notes, autopay details, optional APR, and flexible billing cycles (monthly, weekly, biweekly, quarterly, annual) +- bill history ranges for tracking which months a bill was active - monthly tracker with payments, skipped bills, actual monthly amounts, and notes +- monthly income tracking and starting cash amounts (1st/15th/other) - calendar view for due dates and payments - analytics for monthly spending, expected vs actual totals, category spend, and payment history - categories, profile, display name, notification preferences, password changes, and data tools -- admin user management, authentication settings, backups, cleanup, and status checks +- admin user management, authentication settings, auth-mode/OIDC configuration, backups, scheduled backups, cleanup, migration rollback, audit logging, and status checks ## Features @@ -46,11 +43,12 @@ BillTracker helps a household or small self-hosted setup keep bill data in one p - User-owned categories - Settings for theme, currency, date format, and grace period - Profile page with display name, notification preferences, password change, imports, exports, and import history -- User exports to Excel workbook or BillTracker user SQLite export +- User exports to Excel workbook or BillTracker user SQLite export (note: exports currently omit `cycle_type`, `cycle_day`, and `bill_history_ranges`) - XLSX spreadsheet import with preview and import decisions - User SQLite import from exports created by this app -- Admin users, role management, password resets, full database backups/restores, scheduled backups, cleanup, auth settings, and status page +- Admin users, role management, password resets, full database backups/restores, scheduled backups, cleanup, auth-mode/OIDC configuration, migration rollback, audit logging, and status page - Local username/password login and optional authentik/OIDC login +- CSRF protection using double-submit cookie pattern with per-request nonces ## Quick Start @@ -107,7 +105,16 @@ INIT_ADMIN_USER=admin INIT_ADMIN_PASS=change-this-password ``` -Remove or change those first-run values after the initial admin account exists. +To also seed a regular (non-admin) user: + +```bash +INIT_REGULAR_USER=regularuser +INIT_REGULAR_PASS=changeme123 +``` + +The regular user password must be at least 8 characters. Seeded users skip the first-login and password-change gates. + +Remove or change those first-run values after the initial accounts exist. ## Configuration @@ -122,9 +129,16 @@ DB_PATH=/path/to/bills.db BACKUP_PATH=/path/to/backups INIT_ADMIN_USER=admin INIT_ADMIN_PASS=change-this-password +INIT_REGULAR_USER=regularuser +INIT_REGULAR_PASS=changeme123 +SESSION_CLEANUP_INTERVAL_MS=86400000 HTTPS=true COOKIE_SECURE=true CORS_ORIGIN=https://bills.example.com +CSRF_HTTP_ONLY=true +CSRF_SAME_SITE=strict +CSRF_SECURE=true +CSRF_COOKIE_NAME=bt_csrf_token ``` OIDC environment fallback variables are supported when the matching Admin database setting is blank: @@ -158,7 +172,9 @@ Optional authentik/OIDC login can be enabled in Admin. OIDC uses authorization c Admin role is never granted by default through OIDC. Set an authentik admin group in BillTracker; only users whose OIDC `groups` claim includes that configured group become app admins. -BillTracker includes lockout checks so local login cannot be disabled unless OIDC is configured, enabled, and mapped to an admin group. +BillTracker enforces lockout checks: local login cannot be disabled unless OIDC is configured, enabled, and mapped to an admin group. This prevents accidental lockout where no login method is available. + +The default admin account (created by `INIT_ADMIN_USER`/`INIT_ADMIN_PASS`) is restricted to admin routes only. It cannot access user tracker routes (bills, payments, calendar, etc.). Regular users and promoted admins have full access. ## authentik Setup @@ -217,11 +233,15 @@ Backups and exports contain sensitive financial data. The code writes SQLite bac - Auth is required for user data routes. - Admin routes require an admin session. -- User-owned bill, category, payment, import, and export routes derive ownership from the authenticated session. +- The default admin account cannot access user tracker routes. +- User-owned bill, category, payment, import, and export routes derive ownership from the authenticated session (`req.user.id` in SQL). +- CSRF protection uses a double-submit cookie pattern: a `bt_csrf_token` cookie is set on responses, and mutating requests must include a matching `x-csrf-token` header. Defaults are `httpOnly`, `sameSite=strict`, and `secure` (overridable via env vars). - Local login, password change, import, export, admin actions, and OIDC routes have per-IP in-memory rate limits. - CORS is disabled unless `CORS_ORIGIN` is set. -- Baseline security headers are sent; HSTS is sent only when `HTTPS=true`. +- Security headers include Content-Security-Policy with per-request nonces, plus standard hardening headers. HSTS is sent only when `HTTPS=true`. - Session cookies are `httpOnly`, `sameSite=strict`, and marked secure when `COOKIE_SECURE=true`, `HTTPS=true`, or the request appears to be HTTPS. +- Password changes rotate the current session ID and invalidate all other sessions. +- Audit logging records security-sensitive events: login, logout, password changes, role changes, CSRF failures, and migration operations. - OIDC validation is handled through `openid-client` using discovered provider metadata and JWKS. - Protect database files, backups, and exports as sensitive financial records. @@ -236,15 +256,15 @@ Set `CORS_ORIGIN` only when the frontend and backend are served from different o ```text client/ React app, pages, layout, UI components db/ SQLite connection, schema, startup migrations -middleware/ auth checks, rate limits, security headers +middleware/ auth checks, CSRF, rate limits, security headers routes/ Express API routes -services/ auth, OIDC, backups, imports, cleanup, status, notifications -workers/ daily background tasks -setup/ first-run admin setup -scripts/ migrations and smoke/import tests -public/ legacy static assets -img/ app/runtime images and source screenshots -docs/images/ README images +services/ auth, OIDC, backups, scheduler, imports, cleanup, status, notifications, audit +workers/ daily background tasks (notifications, cleanup) +setup/ first-run admin setup +scripts/ DB migrations, seed data, and smoke/import tests +public/ legacy static assets +img/ app-runtime images and source screenshots +docs/ Engineering Reference Manual, CSRF guide, Authentik integration ``` ## Upgrading @@ -266,9 +286,9 @@ For Docker, pull/rebuild the image, recreate the container, and keep the `/data` - Admin backups and user exports are not encrypted by the app. - OIDC single logout is not implemented. -- Content-Security-Policy is intentionally deferred. +- User exports (Excel and SQLite) currently omit `cycle_type`, `cycle_day`, and `bill_history_ranges` data. - Rate limiting is in-memory, so counters reset on restart and are not shared across multiple app instances. -- authentik live login must be tested in your deployment with your authentik provider. +- Authentik live login must be tested in your deployment with your authentik provider. - The XLSX parser dependency has known upstream security advisories; the import route is authenticated, file-size limited, and parses cells as data. ## License diff --git a/docs/Engineering_Reference_Manual.md b/docs/Engineering_Reference_Manual.md index cba0999..529bc09 100644 --- a/docs/Engineering_Reference_Manual.md +++ b/docs/Engineering_Reference_Manual.md @@ -2,7 +2,7 @@ **Status:** Current code reference **Last Updated:** 2026-05-10 -**Version:** 0.23.1 +**Version:** 0.23.2 **Primary stack:** Node.js + Express, React + Vite, SQLite via `better-sqlite3` This manual reflects the current application code in `server.js`, `routes/`, `services/`, `middleware/`, `db/`, `client/`, `package.json`, `Dockerfile`, and `docker-compose.yml`. It is written as a current-state reference, not a changelog. @@ -236,7 +236,6 @@ Mounted under `/api/auth`. - Body: `{username, password}`. - Validation: both required; local login must be enabled. - Response: sets `bt_session`; `{user}`. - - Errors: 401 invalid credentials, 403 disabled login. - `POST /auth/logout` - Auth: required. @@ -285,8 +284,6 @@ Mounted under `/api/auth`. - Validation: username min 3, password min 8, unique username. - Response: created safe user. -Server also mounts `routes/authLogin.js` at `/api/auth/login`; that router defines `POST /login`, creating an effective compatibility path `/api/auth/login/login` with the same local-login behavior. The frontend uses `/api/auth/login`. - ### 5.2 OIDC Auth Mounted under `/api/auth/oidc`; OIDC rate limiter applies. @@ -1151,7 +1148,7 @@ These use TanStack Query keys and cache server data for common pages. ### `package.json` -Version: `0.23.1`. +Version: `0.23.2`. Scripts: