# AUDIT H1, H14, H11, M16, M17 - COMPLETION REPORT

**Date:** 2026-02-07  
**Auditor:** Rivet (subagent)  
**Status:** ✅ COMPLETE

## Items Addressed

### 🔴 H1: Enable rate limiting on API routes (module exists but never imported) ✅

**Problem:** The rate limiting module existed at `src/lib/rate-limit.ts` but was never imported or used in any API routes.

**Solution:**
- Enhanced the rate limiting module with:
  - `getClientIP()` function to extract client IPs from requests
  - `createRateLimitResponse()` helper for consistent 429 responses
  - Per-route configurable limits
  - Proper TypeScript types
  
- Applied rate limiting to ALL API routes:
  | Route | Limit | Window | Reason |
  |-------|-------|--------|--------|
  | `/api/abn-lookup` | 10 req | 15 min | External API call (ABR) |
  | `/api/ai/generate-profile` | 5 req | 15 min | Expensive AI API call |
  | `/api/company-logo` | 30 req | 15 min | External API calls |
  | `/api/payments/create` | 10 req | 15 min | Sensitive financial op |
  | `/api/auth/verify-email` | 5 req | 15 min | Auth endpoint |

**Files Modified:**
- `src/lib/rate-limit.ts` - Enhanced with new features
- `src/app/api/abn-lookup/route.ts` - Added rate limiting
- `src/app/api/ai/generate-profile/route.ts` - Added rate limiting
- `src/app/api/company-logo/route.ts` - Added rate limiting
- `src/app/api/payments/create/route.ts` - Added rate limiting + user ID based limiting
- `src/app/api/auth/verify-email/route.ts` - Added rate limiting

---

### 🔴 H14: Fix in-memory rate limiter memory leak ✅

**Problem:** The rate limiter stored entries in a Map forever, causing memory to grow unbounded over time.

**Solution:**
- Added periodic cleanup interval (runs every 5 minutes)
- Removes expired entries (entries past their resetTime)
- Uses `unref()` to prevent keeping the process alive (serverless-friendly)
- Cleanup starts automatically when the module loads
- Exported `stopRateLimitCleanup()` for testing purposes

**Files Modified:**
- `src/lib/rate-limit.ts` - Added `startRateLimitCleanup()` and `stopRateLimitCleanup()`

---

### 🔴 H11: Add validation for missing SUPABASE_SERVICE_KEY ✅

**Problem:** The `supabaseAdmin` client was initialized at module load time, causing build failures when `SUPABASE_SERVICE_KEY` wasn't set. Also provided poor error messages.

**Solution:**
- Converted to lazy initialization with `getSupabaseAdmin()` function
- Added clear error messages explaining:
  - What's missing (SUPABASE_SERVICE_KEY)
  - How to fix it (add to .env.local)
  - Where to find more info (SUPABASE-KEY-ROTATION-GUIDE.md)
- Added validation for `NEXT_PUBLIC_SUPABASE_URL` as well
- Maintained backward compatibility via Proxy for `supabaseAdmin` export

**Files Modified:**
- `src/lib/supabase/server.ts` - Refactored for lazy initialization
- `src/app/api/payments/create/route.ts` - Updated to use `getSupabaseAdmin()`

---

### 🟡 M16: Delete test-signup.js from project root ✅

**Action:** Deleted `/home/ccuser/the-50-dollar-app/test-signup.js`

---

### 🟡 M17: Delete middleware.ts.backup from project root ✅

**Action:** Deleted `/home/ccuser/the-50-dollar-app/middleware.ts.backup`

---

### 🔴 H5: Fix CORS wildcard on company-logo endpoint ✅

**Problem:** The company-logo API endpoint used `Access-Control-Allow-Origin: *` wildcard, which is a security risk.

**Solution:**
- Created `ALLOWED_ORIGINS` array with specific allowed origins:
  - `process.env.NEXT_PUBLIC_APP_URL` (production)
  - `http://localhost:3000` (local dev)
  - `https://localhost:3000` (local dev HTTPS)
- Added `getCORSHeaders()` function that:
  - Checks the `Origin` header against allowlist
  - Returns the matched origin or first allowed origin
  - Adds `Vary: Origin` header for proper caching
- Applied CORS headers to all responses (GET, OPTIONS, and errors)

**Files Modified:**
- `src/app/api/company-logo/route.ts` - Replaced wildcard with allowlist-based CORS

---

## Build Status

✅ **TypeScript compilation passes** - `npx tsc --noEmit` completes without errors

⚠️ **Production build requires:**
- `SUPABASE_SERVICE_KEY` to be set in `.env.local` for payments functionality
- This is expected behavior - the app now validates configuration at runtime instead of crashing at build time

## Security Improvements

1. **Rate limiting** now protects against:
   - Abuse of expensive external APIs (ABR, OpenAI)
   - Brute force attacks on auth endpoints
   - Resource exhaustion attacks

2. **Memory leak fixed** - Rate limiter won't consume unbounded memory

3. **Better configuration validation** - Clear error messages when required env vars are missing

4. **CORS hardened** - Replaced wildcard with explicit allowlist of trusted origins

## Next Steps

The following HIGH priority items remain unaddressed:
- H2: Fix Supabase client singleton (stale auth on shared devices)
- H3: Sanitize AI prompt inputs (prompt injection possible)
- H4: Add error.tsx error boundaries (none exist anywhere)
- H6: Implement real messaging (currently mock data only)
- H9: Add not-found.tsx for dynamic routes
- H10: Fix verify-email page redirects to non-existent /contractor/dashboard
- H12: Handle invalid user type in handle_new_user() trigger
- H13: Add loading.tsx for async server component routes

**Recommended next priority:** H4 (error boundaries) - provides the biggest stability improvement by preventing complete page crashes when errors occur.
