8fc88ffc14
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
2.5 KiB
2.5 KiB
Security design updates (Phases 0 & 3)
Summary of application-security design changes from the production-readiness program. Full finding table lives in docs/ops/RentalDriveGo_Production_Readiness_Plan.md §3.
Closed in code (S1–S15)
| ID | Design rule now enforced |
|---|---|
| S1 | Team list/invite responses use safe presenters — never return password hashes or raw reset tokens |
| S2 | Per-tenant Docker/Compose orchestration is out of production scope (fail-closed + ADR-001) |
| S3 | Invite tokens hashed at rest (SHA-256) |
| S4 | Employee email unique per company; login fails closed on ambiguity |
| S5 | Post-login redirects limited to safe relative paths |
| S6 | Authenticated payment/subscription return URLs allowlisted |
| S7 | Admin company slugs validated / slugified |
| S8 | Admin presenters strip reset/verification secrets |
| S9 | Forwarded headers scrubbed by default; TRUSTED_FORWARD_HEADERS=true only behind a scrubbing edge (docs/ops/proxy-trust.md) |
| S10 | Password reset token lookup is hash-only |
| S11 | reviewToken is never returned in reservation/review API JSON (still used server-side for email links) |
| S12 | Public booking access: read does not burn the token; payment init atomically consumes an unused token |
| S13 | Admin money / privileged mutations require 2FA proof newer than ADMIN_FRESH_2FA_MAX_AGE_MS |
| S14 | .gitignore present for secrets/build artifacts |
| S15 | npm run security:static in CI |
Public booking token (S12) flow
createBooking → mint publicAccessToken (hash stored)
│
├─ GET booking?token=… → validate (used or unused OK until expiry) — do not consume
│
└─ initPayment(token) → require usedAt IS NULL → updateMany set usedAt → proceed
(second payment attempt with same token → 404)
Fresh admin 2FA (S13)
requireFreshAdmin2FA rejects when:
- TOTP not enrolled, or
- JWT lacks
last2faAt, or now - last2faAt > ADMIN_FRESH_2FA_MAX_AGE_MS(default 30 minutes)
Wired on finance/support money and high-privilege admin mutations.
Tenant isolation design
- App-level: every company resource query includes
companyIdfrom the authenticated session - Regression suite:
apps/api/src/tests/integration/cross-tenant-isolation.test.ts - Postgres RLS: deferred (ADR-002) until the app-level suite and worker/migration roles are ready
Pen-test
Scope pack: docs/security/pen-test-scope.md. Reports belong under security-reports/ (evidence, not design).