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:
parent
6b1ef7dcfa
commit
7c3cfd1715
38
FUTURE.md
38
FUTURE.md
|
|
@ -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
|
||||||
|
|
|
||||||
14
HISTORY.md
14
HISTORY.md
|
|
@ -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
|
||||||
|
|
|
||||||
74
README.md
74
README.md
|
|
@ -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 · 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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue