BillTracker/SECURITY.md

2.6 KiB

Security

Bill Tracker is a self-hosted app that stores financial records, bank connections, and encryption keys. Security is a first-class concern.

Reporting a vulnerability

Please report suspected vulnerabilities privately to the maintainer (do not open a public issue). Include steps to reproduce and impact. We aim to acknowledge within a few days.

Controls in place

  • Authentication: bcrypt passwords, hashed + rotating session tokens, optional TOTP 2FA and WebAuthn/FIDO2, OIDC. Failed-login tracking + login history.
  • Authorization: requireAuth / requireUser / requireAdmin on every non-public route; all user data is scoped by user_id (isolation is covered by tests).
  • CSRF: double-submit token (csrfMiddleware) on all authenticated mutating routes.
  • Rate limiting: on auth, admin, import/export, backup, and password-change surfaces.
  • Encryption at rest: AES-256-GCM with HKDF key derivation for stored secrets (SimpleFIN token, SMTP/OIDC secrets, login metadata); a non-reversible key fingerprint lets operators verify the active key. Set TOKEN_ENCRYPTION_KEY in production so the key lives outside the database.
  • Transport/headers: security headers + CSP (securityHeaders), Secure/HttpOnly/SameSite cookies, opt-in CORS allowlist.
  • Audit: security-sensitive actions recorded to audit_log.
  • Automated in CI: secret scanning (gitleaks), dependency audit (npm audit), lint + typecheck + tests on every push.

Known dependency-audit exceptions (assessed)

npm audit reports two HIGH advisories that are not currently actionable via npm and are assessed as follows:

  • nodemailer — raw message option file-read/SSRF. Not exploitable here: the app builds messages via the normal API and never passes a user-controlled raw option. The npm fix requires nodemailer 9 (breaking); deferred until a maintenance window with SMTP re-test.
  • xlsx (SheetJS) — prototype pollution. Confined to services/spreadsheetImportService.cts (import only), which parses with raw: false behind a 10 MB size cap and content-type allowlist. SheetJS publishes fixes only via its own CDN (not npm); migrating to the CDN build (or a maintained alternative) is tracked as follow-up.

The CI dependency gate fails on critical; highs are reviewed here.

Operational hardening

  • Set TOKEN_ENCRYPTION_KEY (outside the DB) in production.
  • Run behind HTTPS with TRUST_PROXY set appropriately.
  • Turn off verbose/debug logging (e.g. simplefin_debug_logging) in production.
  • Back up data/db encrypted; never commit databases or keys (gitignored + gitleaks).