Security & Performance Audits

Full codebase audits conducted March 2026 — 47 total findings across security and performance

Audit Overview

9
Critical
17
High
16
Medium
23
Fixed

Two security audit phases + one performance audit. 23 of 47 findings resolved.

Security Audit — Phase 1 (Post-Migration)

SeverityFoundFixedResolution
Critical55
High86
Medium98
Low53

22 of 27 issues resolved. Key fixes: cart isolation per user, order ownership checks, dev bypass removed, impersonation logging, security headers.

Security Audit — Phase 2 (March 4)

Critical Payment Status Manipulation
Customers can mark own orders as "paid" via /api/orders/[id]/payment-status without actual payment.
Critical Discount Code Enumeration
/api/cart/validate-discount requires no auth. Anyone can brute-force valid codes.
Critical Payment Amount Not Validated
CyberSource 3DS and PayPal capture trust client-submitted amounts. Server does not recalculate.
High UUID vs Numeric ID Mismatch
Systemic issue. Driver access checks compare UUID to numeric ID — always fails. Affects driver routes.
IssueSeverityStatus
Mass assignment on restaurant creationHighOpen
Driver/customer PII exposed without maskingHighOpen
No rate limiting on auth endpointsHighOpen
Admin endpoints trust client roleHighOpen
Missing CSRF protectionHighOpen
Insecure direct object referencesHighOpen
RLS blocks unauthenticated menu browsingMediumOpen
No input sanitization on user contentMediumOpen
Sessions not invalidated on password changeMediumOpen

Performance Audit (March 4)

Critical Cart Page God Component
2,677 lines, 27 useState hooks. Every keystroke re-renders entire page. Severe input lag on mobile.
Critical Sequential Dashboard Queries
Manager dashboard makes 8 sequential API calls (one per order status). 2-4s load time.
Critical 2 DB Roundtrips Per Auth
Every authenticated request: JWT verify + user lookup. Adds 50-100ms to every API call.
Critical Image Optimization Disabled
unoptimized: true in next.config. Menu pages load 5-10MB of images on mobile.

Optimizations Implemented

1
Firebase dead code removal — Deleted unused firebase.ts and converters.ts (leftover from migration)
2
CartContext memoization — 10 handlers wrapped in useCallback, context value in useMemo. Prevents cascading re-renders across 7 consumers.
3
Visibility-aware polling — New useVisibilityAwareInterval hook pauses intervals when tab backgrounded. Applied to 6 polling components.
4
Blob URL memory leaks — Image uploads now revoke old blob URLs before creating new ones.
5
Rogue fetcher replaced — Raw fetch() in restaurants page replaced with shared apiClient fetcher.

Known Issue — Auth Race Condition

Data fetches fire before Supabase session is ready on initial page load. Causes:

"Failed to load restaurants"
First load shows error, works after refresh. SWR fires before auth token available.
Blank menus / skeleton loading
RLS requires auth.uid() IS NOT NULL. Unauthenticated requests return empty arrays.
Menu images failing
Same root cause — image URLs from protected storage fail when auth not ready.
Fix approach
Gate SWR fetches behind auth.loading === false. Add skeleton states that wait for session.
Detailed docs: Security Audit Details | Performance Audit Details
Back to Ricoya