Fix auth security issues, add rate limiting, write Phase 2 audit
Security fixes: - Register route validates company exists before creating user - Rate limiting on auth routes (10 per 15min per IP) - Dev auth plugin guards against production use - Main.ts throws if JWT_SECRET missing in production Added Phase 2 audit doc (22) covering: - Built vs planning doc comparison - Security review with fixes applied - Duplicate code patterns identified - Standard POS feature gap analysis - Music-specific feature gaps 33 tests passing.
This commit is contained in:
143
planning/22_Audit_Phase2_Review.md
Normal file
143
planning/22_Audit_Phase2_Review.md
Normal file
@@ -0,0 +1,143 @@
|
||||
Forte — Music Store Management Platform
|
||||
|
||||
Phase 2 Audit: Code Review, Security, and Feature Gap Analysis
|
||||
|
||||
Date: 2026-03-27
|
||||
|
||||
|
||||
|
||||
# 1. Built vs Planning Docs
|
||||
|
||||
## 1.1 Phase 1 — Complete
|
||||
|
||||
All deliverables built: monorepo config, Docker Compose (Postgres 16 + Valkey 8), Fastify server with Pino logging and request ID tracing, Drizzle ORM, health endpoint, seed script, test setup.
|
||||
|
||||
## 1.2 Phase 2 — Mostly Complete
|
||||
|
||||
Area | Status | Notes
|
||||
Auth (JWT + bcrypt) | Complete | Self-issued JWTs, role enum, register/login routes
|
||||
User table | Complete | company_id FK, unique email, 5 roles
|
||||
Account table | Complete | Added is_active (not in spec — good defensive addition)
|
||||
Member table | Complete | Added updated_at (not in spec — good practice)
|
||||
Account processor link | Complete | Processor-agnostic payment linking
|
||||
Account payment method | NOT BUILT | Specified in doc 02 but not implemented
|
||||
Category table | Complete | Added parent_id for hierarchy, sort_order, is_active
|
||||
Supplier table | Complete | All columns present
|
||||
Product table | Complete | Cost removed — moved to stock_receipt (FIFO)
|
||||
Inventory unit table | Complete | Condition enum, status enum, serial number
|
||||
Shared Zod schemas | Complete | Auth, account, member, inventory schemas
|
||||
|
||||
## 1.3 Additions Not in Original Planning
|
||||
|
||||
Table | Purpose | Notes
|
||||
stock_receipt | FIFO cost tracking per purchase event | Replaces product.cost column
|
||||
price_history | Logs every retail price change | Auto-logged on product update
|
||||
consignment_detail | Consignment product linking | Links product to consignor account with commission %
|
||||
product_supplier | Many-to-many product ↔ supplier | Tracks supplier SKU and preferred supplier
|
||||
|
||||
## 1.4 Planning Doc Inconsistencies
|
||||
|
||||
Issue | Details | Resolution
|
||||
product.cost column | Planning doc 03 specifies cost on product table | Intentionally removed — cost now tracked per stock_receipt for FIFO accuracy. Doc 03 needs updating.
|
||||
stripe_customer_id on account | Doc 02 originally had this | Replaced by account_processor_link table. Doc updated.
|
||||
student entity | Docs originally used "student" | Renamed to "member" across all docs. Code uses member.
|
||||
|
||||
|
||||
|
||||
# 2. Security Audit
|
||||
|
||||
## 2.1 Issues Found and Fixed
|
||||
|
||||
Issue | Severity | Fix Applied
|
||||
Auth routes trusted x-company-id header blindly | HIGH | Register now validates company exists in DB before creating user. Login derives company from user record (no header needed).
|
||||
No rate limiting on auth routes | HIGH | Added @fastify/rate-limit — 10 attempts per 15 minutes per IP on register and login.
|
||||
Dev auth had no production guard | HIGH | Dev auth plugin throws error if NODE_ENV is not development/test. Main.ts throws error if JWT_SECRET missing in production.
|
||||
|
||||
## 2.2 Issues Found — Not Yet Fixed (Lower Priority)
|
||||
|
||||
Issue | Severity | Notes
|
||||
CORS in production is false | Medium | Blocks all cross-origin requests. Needs origin whitelist via CORS_ORIGINS env var. Fix when building frontend.
|
||||
No pagination on list endpoints | Medium | list() returns .limit(100) with no offset. Add cursor/offset pagination when data volumes grow.
|
||||
Path parameters not validated as UUIDs | Low | Cast with `as { id: string }` but not validated. Drizzle handles gracefully (returns null). Add Zod param validation later.
|
||||
Login schema accepts min(1) password | Low | Should match register's min(8) for consistency. Not a real security risk since bcrypt compare handles it.
|
||||
No JWT secret strength validation | Low | Only checks if set, not length/entropy. Add min 32 char check.
|
||||
Price conversion to string is scattered | Low | .toString() on every numeric field. Extract helper function.
|
||||
Inconsistent search validation | Low | Supplier search uses manual check, others use Zod schema. Standardize.
|
||||
|
||||
## 2.3 Security — No Issues Found
|
||||
|
||||
Area | Status
|
||||
SQL injection | SECURE — all queries via Drizzle ORM parameterized queries
|
||||
Password storage | SECURE — bcrypt with 10 salt rounds, password never returned in API responses
|
||||
Error information leakage | SECURE — stack traces only in development mode
|
||||
JWT payload | SECURE — contains only id, companyId, role (no sensitive data)
|
||||
Soft delete | SECURE — financial records use soft delete, never hard delete
|
||||
|
||||
|
||||
|
||||
# 3. Duplicate Code Patterns
|
||||
|
||||
## 3.1 Identified Patterns
|
||||
|
||||
Pattern | Occurrences | Recommendation
|
||||
Zod validation + 400 response | 20+ routes | Extract validateBody(schema) helper
|
||||
404 not-found response | 16+ locations | Extract notFound(reply, entity) helper
|
||||
Service getById pattern | 4 services | Consider base service or utility function
|
||||
Service softDelete pattern | 4 services | Same as getById
|
||||
Search with ilike pattern | 3 services | Standardize search utility
|
||||
Numeric .toString() conversion | Multiple services | Extract toDbNumeric() helper
|
||||
|
||||
## 3.2 Assessment
|
||||
|
||||
The duplication is structural — the same patterns repeated across different domains. Not urgent to fix now. When Phase 3 (POS) adds more routes, the repetition will grow. Good time to extract helpers is before Phase 3 starts.
|
||||
|
||||
|
||||
|
||||
# 4. Standard POS Feature Gap Analysis
|
||||
|
||||
## 4.1 Well Covered
|
||||
|
||||
Feature | Score | Notes
|
||||
Transaction processing | 9/10 | Multiple payment methods, Stripe Terminal, processor abstraction
|
||||
Inventory management | 8/10 | Serialized + non-serialized, FIFO costing, stock receipts
|
||||
Customer management | 8/10 | Account/member model, family billing, search
|
||||
Instrument rentals | 9/10 | 4 rental types, RTO equity, billing consolidation
|
||||
Lessons | 9/10 | Scheduling, attendance, curriculum, grading, parent portal
|
||||
Repairs | 9/10 | Full lifecycle, parts inventory, flat-rate billing, bulk/batch
|
||||
Consignment | 9/10 | Commission tracking, settlement workflow, accounting
|
||||
Sales commission | 8/10 | Rate hierarchy, per-employee, category overrides
|
||||
Personnel | 8/10 | Time clock, scheduling, time off, payroll export
|
||||
|
||||
## 4.2 Missing — High Priority (Add Before Launch)
|
||||
|
||||
Feature | Impact | Notes
|
||||
Gift cards / store credits | High | Common customer request. Needs gift_card table, balance tracking, POS redemption.
|
||||
Trade-in workflow | High | Huge in music retail. Customer brings used instrument, gets credit toward purchase. Needs appraisal + credit + inventory intake flow.
|
||||
Tax exemptions | High | Schools, churches, resellers are core customers. Need tax_exempt flag on account + resale certificate tracking.
|
||||
Inventory cycle counts | High | Physical inventory reconciliation. Need count sessions, variance tracking, adjustment audit trail.
|
||||
Returns/exchanges workflow | High | Structured beyond just refund transactions. Need RMA, reason codes, return window policies, restocking.
|
||||
Purchase orders | Medium | PO generation, receiving against PO, three-way match. stock_receipt is a start but needs formal PO workflow.
|
||||
|
||||
## 4.3 Missing — Medium Priority
|
||||
|
||||
Feature | Notes
|
||||
Layaway / payment plans | Hold item with partial payment, release on full payment
|
||||
Product bundles / kits | Instrument + case + accessories at bundle price
|
||||
Barcode label printing | Bulk label printing for inventory
|
||||
Backorders | Customer order queue for out-of-stock items
|
||||
Warranty tracking | Warranty period, claim tracking, extended warranty options
|
||||
|
||||
## 4.4 Missing — Lower Priority / Future
|
||||
|
||||
Feature | Notes
|
||||
Customer loyalty / rewards | Point accumulation and redemption
|
||||
E-commerce / online store | Product catalog, cart, online payments
|
||||
Shipping integration | Carrier APIs, label printing, tracking
|
||||
Multi-currency | Exchange rates, currency-specific pricing
|
||||
|
||||
## 4.5 Music-Specific Gaps
|
||||
|
||||
Feature | Notes
|
||||
Instrument sizing | 1/4, 1/2, 3/4, full — critical for string instrument rentals. Need size field on product/inventory_unit.
|
||||
Rental insurance certificates | Certificate generation for rental instruments. Common parent request.
|
||||
Instrument maintenance schedules | Preventive maintenance recommendations and reminders per instrument.
|
||||
Reference in New Issue
Block a user