docs: update README.md, ERM, FUTURE.md, HISTORY.md

README.md updates:
- Added billing cycles (weekly/biweekly/quarterly/annual), history ranges,
  monthly income/starting amounts, migration rollback, audit logging,
  auth-mode/OIDC config, CSRF protection details
- Added INIT_REGULAR_USER/PASS and SESSION_CLEANUP_INTERVAL_MS env vars
- Added CSRF env vars (CSRF_HTTP_ONLY, CSRF_SAME_SITE, CSRF_SECURE,
  CSRF_COOKIE_NAME)
- Noted export limitation: cycle_type, cycle_day, history_ranges omitted
- Fixed: CSP is now implemented with per-request nonces (was 'deferred')
- Added: default admin restricted from tracker routes, session rotation
  on password change, audit logging
- Cleaned up demo server formatting, project structure listing, scripts
- Removed authLogin.js from project structure (file was deleted in v0.23.2)

Engineering_Reference_Manual.md:
- Removed stale authLogin.js duplicate route note (file no longer exists)
- Removed 401/403 error detail from login endpoint (simplified)
- Updated version to 0.23.2

FUTURE.md:
- Marked notification privacy leak (CRITICAL) as FIXED v0.23.2
- Marked duplicate login route (LOW) as FIXED v0.23.2
- Updated current version to v0.23.2

HISTORY.md:
- Added v0.23.2 entry with security fix and route consolidation details
This commit is contained in:
null 2026-05-10 12:42:45 -05:00
parent 6b1ef7dcfa
commit 7c3cfd1715
4 changed files with 68 additions and 65 deletions

View File

@ -3,7 +3,7 @@
**This document tracks potential future enhancements for Bill Tracker.** **This document tracks potential future enhancements for Bill Tracker.**
**Last Updated:** 2026-05-10 **Last Updated:** 2026-05-10
**Current Version:** v0.23.1 **Current Version:** v0.23.2
## How to Use This Document ## How to Use This Document
@ -34,21 +34,8 @@ Items are grouped under their priority section heading (`## 🔴 CRITICAL`, `##
### 🔴 CRITICAL ### 🔴 CRITICAL
### 🔴 Notification Runner Leaks Bill Details Across Users — CRITICAL ### ~~🔴 Notification Runner Leaks Bill Details Across Users — CRITICAL~~ ✅ FIXED (v0.23.2)
**Priority:** CRITICAL **Moved to HISTORY.md**
**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
### 🟠 HIGH ### 🟠 HIGH
@ -305,23 +292,8 @@ Add explicit UI copy warning that exports may contain account metadata, and cons
**Severity:** LOW **Severity:** LOW
### 🔵 Duplicate Local Login Route Increases Auth Drift Risk — LOW ### ~~🔵 Duplicate Local Login Route Increases Auth Drift Risk — LOW~~ ✅ FIXED (v0.23.2)
**Priority:** LOW **Moved to HISTORY.md**
**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
### Add comprehensive unit and integration tests ### Add comprehensive unit and integration tests

View File

@ -1,5 +1,19 @@
# Bill Tracker — Changelog # 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 ## v0.23.1
### Added ### Added

View File

@ -1,18 +1,14 @@
# BillTracker # BillTracker
<p align="center">
<img src="docs/images/logo_cut.png" alt="Tracker logo">
<p align="center">
<img src="docs/images/logo_cut.png" alt="Tracker logo">
</p> </p>
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. 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.
<p align="center"> <p align="center">
Demo Server Demo Server: https://t1.scheller.ltd/<br>
https://t1.scheller.ltd/ Username: guest &middot; Password: guest123
<br>
Username: guest
<br>
Password: guest123
</p> </p>
## Screenshots ## Screenshots
@ -27,15 +23,16 @@ Password: guest123
## What Is BillTracker? ## What Is BillTracker?
BillTracker helps a household or small self-hosted setup keep bill data in one place: 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 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 - calendar view for due dates and payments
- analytics for monthly spending, expected vs actual totals, category spend, and payment history - analytics for monthly spending, expected vs actual totals, category spend, and payment history
- categories, profile, display name, notification preferences, password changes, and data tools - 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 ## Features
@ -46,11 +43,12 @@ BillTracker helps a household or small self-hosted setup keep bill data in one p
- User-owned categories - User-owned categories
- Settings for theme, currency, date format, and grace period - Settings for theme, currency, date format, and grace period
- Profile page with display name, notification preferences, password change, imports, exports, and import history - 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 - XLSX spreadsheet import with preview and import decisions
- User SQLite import from exports created by this app - 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 - Local username/password login and optional authentik/OIDC login
- CSRF protection using double-submit cookie pattern with per-request nonces
## Quick Start ## Quick Start
@ -107,7 +105,16 @@ INIT_ADMIN_USER=admin
INIT_ADMIN_PASS=change-this-password 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 ## Configuration
@ -122,9 +129,16 @@ DB_PATH=/path/to/bills.db
BACKUP_PATH=/path/to/backups BACKUP_PATH=/path/to/backups
INIT_ADMIN_USER=admin INIT_ADMIN_USER=admin
INIT_ADMIN_PASS=change-this-password INIT_ADMIN_PASS=change-this-password
INIT_REGULAR_USER=regularuser
INIT_REGULAR_PASS=changeme123
SESSION_CLEANUP_INTERVAL_MS=86400000
HTTPS=true HTTPS=true
COOKIE_SECURE=true COOKIE_SECURE=true
CORS_ORIGIN=https://bills.example.com 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: 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. 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 ## 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. - Auth is required for user data routes.
- Admin routes require an admin session. - 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. - 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. - 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. - 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. - OIDC validation is handled through `openid-client` using discovered provider metadata and JWKS.
- Protect database files, backups, and exports as sensitive financial records. - 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 ```text
client/ React app, pages, layout, UI components client/ React app, pages, layout, UI components
db/ SQLite connection, schema, startup migrations 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 routes/ Express API routes
services/ auth, OIDC, backups, imports, cleanup, status, notifications services/ auth, OIDC, backups, scheduler, imports, cleanup, status, notifications, audit
workers/ daily background tasks workers/ daily background tasks (notifications, cleanup)
setup/ first-run admin setup setup/ first-run admin setup
scripts/ migrations and smoke/import tests scripts/ DB migrations, seed data, and smoke/import tests
public/ legacy static assets public/ legacy static assets
img/ app/runtime images and source screenshots img/ app-runtime images and source screenshots
docs/images/ README images docs/ Engineering Reference Manual, CSRF guide, Authentik integration
``` ```
## Upgrading ## 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. - Admin backups and user exports are not encrypted by the app.
- OIDC single logout is not implemented. - 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. - 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. - The XLSX parser dependency has known upstream security advisories; the import route is authenticated, file-size limited, and parses cells as data.
## License ## License

View File

@ -2,7 +2,7 @@
**Status:** Current code reference **Status:** Current code reference
**Last Updated:** 2026-05-10 **Last Updated:** 2026-05-10
**Version:** 0.23.1 **Version:** 0.23.2
**Primary stack:** Node.js + Express, React + Vite, SQLite via `better-sqlite3` **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. 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}`. - Body: `{username, password}`.
- Validation: both required; local login must be enabled. - Validation: both required; local login must be enabled.
- Response: sets `bt_session`; `{user}`. - Response: sets `bt_session`; `{user}`.
- Errors: 401 invalid credentials, 403 disabled login.
- `POST /auth/logout` - `POST /auth/logout`
- Auth: required. - Auth: required.
@ -285,8 +284,6 @@ Mounted under `/api/auth`.
- Validation: username min 3, password min 8, unique username. - Validation: username min 3, password min 8, unique username.
- Response: created safe user. - 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 ### 5.2 OIDC Auth
Mounted under `/api/auth/oidc`; OIDC rate limiter applies. 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` ### `package.json`
Version: `0.23.1`. Version: `0.23.2`.
Scripts: Scripts: