# DropOps CRM Frontend Security Audit

**Date:** 2026-05-26  
**Auditor:** Cascade  
**Scope:** `src/` and root configuration files

---

## 1. Critical Findings

### CRIT-1: Hardcoded Demo Credentials in Source Code
**File:** `src/app/login/page.tsx`  
**Risk:** Credential disclosure, easy privilege escalation in demo environments  
**Details:** Two demo accounts (`owner@demo.com` / `password`, `admin@dropops.com` / `password`) are hardcoded in the frontend bundle. Anyone with access to the built app can read these credentials.
**Fix:** Remove hardcoded passwords; fetch demo credentials from a secure backend endpoint or environment variable. If demo mode is needed, use a one-time token flow.

### CRIT-2: JWT Token Stored in localStorage
**Files:** `src/hooks/useAuth.tsx`, `src/lib/api.ts`  
**Risk:** XSS token theft — any XSS vulnerability allows full account takeover  
**Details:** The auth token is stored in `localStorage`, which is accessible to any JavaScript running on the page (including injected XSS payloads). The API client reads the token from localStorage on every request.
**Fix:** Move to **httpOnly, Secure, SameSite=strict cookies** set by the backend. The frontend should not handle tokens at all. If cookies are not possible, store in a closure/memory and implement proper refresh-token rotation.

### CRIT-3: No CSRF Protection on State-Changing Requests
**File:** `src/lib/api.ts`  
**Risk:** Cross-Site Request Forgery (CSRF)  
**Details:** POST/PUT/DELETE requests carry the auth token but have no CSRF token or `X-Requested-With` header. If the backend relies only on the Authorization header (Bearer token), CSRF is mitigated, but if cookies are introduced, CSRF protection becomes mandatory.
**Fix:** If moving to cookie-based auth, implement Double Submit Cookie pattern or Synchronizer Token pattern. Add `X-Requested-With: XMLHttpRequest` header in the meantime.

---

## 2. High Findings

### HIGH-1: No Input Sanitisation Before API Submission
**Files:** All form pages (`support/create`, `returns/create`, `tracking/create`, etc.)  
**Risk:** XSS, injection attacks, data corruption  
**Details:** Form inputs are sent directly to the API without client-side sanitisation. While React's JSX escaping protects against DOM-based XSS, backend reflection or stored XSS could still occur if the API returns unsanitised data.
**Fix:** Add a `sanitizeInput()` utility using DOMPurify or a regex whitelist. Validate all inputs before submission.

### HIGH-2: No Security Headers
**File:** `next.config.js`  
**Risk:** Clickjacking, MIME sniffing, XSS via injected scripts  
**Details:** No Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, or Strict-Transport-Security headers are configured.
**Fix:** Add headers in `next.config.js` async headers() config.

### HIGH-3: document.write() with Potentially Untrusted Data
**File:** `src/app/despatch/page.tsx`  
**Risk:** DOM XSS via label data  
**Details:** `win.document.write()` is used to inject a base64 PDF into an iframe. While currently the data comes from an API, if that API is ever compromised or user-uploaded labels are supported, this becomes an XSS vector.
**Fix:** Use `URL.createObjectURL()` with a Blob instead of `document.write()`.

### HIGH-4: Missing Rate Limiting on Login
**File:** `src/app/login/page.tsx`  
**Risk:** Credential stuffing, brute-force attacks  
**Details:** No client-side rate limiting on login attempts. While backend rate limiting is the real defence, the frontend should also throttle repeated submissions.
**Fix:** Add a cooldown timer (e.g., 3 attempts → 30-second lockout) and display appropriate messaging.

---

## 3. Medium Findings

### MED-1: API Base URL Exposed via NEXT_PUBLIC_ prefix
**File:** `.env.example`  
**Risk:** Minor — the API URL is not a secret, but using `NEXT_PUBLIC_` means it is baked into the client bundle  
**Details:** `NEXT_PUBLIC_API_URL` is exposed to the client. This is acceptable for the API base URL but should not be used for secrets.
**Fix:** Ensure no secrets use the `NEXT_PUBLIC_` prefix. This is already the case, so this is informational only.

### MED-2: No Request Timeouts or Retry Logic
**File:** `src/lib/api.ts`  
**Risk:** Hanging requests, poor UX under network degradation  
**Details:** Axios instance has no timeout configured. Failed requests are not retried.
**Fix:** Add `timeout: 10000` and a simple retry interceptor for 5xx errors.

### MED-3: Error Messages Leak Internal Details
**File:** `src/app/login/page.tsx`  
**Risk:** Information disclosure for reconnaissance  
**Details:** Network error messages like "Cannot reach server. Is the backend running?" reveal internal architecture.
**Fix:** Use generic error messages for users; log detailed errors to console or error-tracking service.

---

## 4. Low / Informational

### LOW-1: No Subresource Integrity (SRI) on External Resources
**Risk:** If external scripts/CDNs are ever loaded, they could be compromised.  
**Fix:** Not currently applicable (no external scripts), but worth noting if adding analytics/CDNs.

### LOW-2: Missing `autocomplete` Attributes on Login Form
**File:** `src/app/login/page.tsx`  
**Risk:** Minor UX/security — password managers may not trigger properly  
**Fix:** Add `autoComplete="email"` and `autoComplete="current-password"`.

---

## 5. Remediation Priority

| Priority | Item | Effort |
|---|---|---|
| P0 | Remove hardcoded demo passwords | 30 min |
| P0 | Add security headers (next.config.js) | 15 min |
| P0 | Add input sanitisation utility | 30 min |
| P1 | Secure API client (timeout, retry, CSRF header) | 30 min |
| P1 | Fix document.write() in despatch | 15 min |
| P1 | Add login rate limiting | 20 min |
| P2 | Move auth to httpOnly cookies (requires backend) | 2-4 hours |
| P2 | Write unit + E2E tests | 2-3 hours |
