# OWASP ASVS 4.0.3 Level 2: review checklist

Status as of M6 (updated 31 Oct 2026). ✅ implemented and tested · ◐ partly / needs staging verification · ☐ open. Evidence points to code or tests.

| Area | Requirement (summary) | Status | Evidence / notes |
|---|---|---|---|
| V1 Architecture | Tenant isolation by DB and credentials | ✅ | Per-tenant MySQL user; `tenants:verify` isolation check; tests/Feature/Tenancy |
| V1 | Secrets out of code and env for tenants | ✅ | SecretStore (encrypted / AWS SM); TOKEN_ENCRYPTION_KEY separate from APP_KEY |
| V2 Authentication | Password policy, breached-password check | ✅ | `Password::defaults()`: in production min 12, mixed case, numbers, `uncompromised()` (AppServiceProvider) |
| V2 | MFA for privileged roles | ✅ | RequireTwoFactor; tests/Feature/Auth |
| V2 | Login and reset throttling | ✅ | Fortify limiters, ThrottlePasswordResetRequests |
| V3 Session | Host-only cookies, regenerate on login; the current workspace is a session value re-checked against membership every request | ✅ | SESSION_DOMAIN null; `session()->regenerate()` on login; `WorkspaceSession`, `IdentifyTenant`; `BindComponentToTenant` refuses Livewire updates from another workspace |
| V3 | Re-authentication for sensitive actions | ✅ | password confirm (15 min) for 2FA changes, data export, workspace deletion |
| V4 Access control | Server-side authorization on every action | ✅ | Policies/ClientAccess in actions; Livewire locked ids; tests/Feature/Authorization matrix |
| V4 | Clients hidden (404) from unassigned members | ✅ | ClientAccess::findVisible |
| V5 Validation | Output encoding (Blade escaping), no raw user HTML | ✅ | `{!! !!}` used only for server-built report cells (escaped with `e()`) and SVG built from numbers |
| V5 | File upload validation (type sniffing, AV) | ✅ | StoreMedia finfo + ClamAV/EICAR; logo upload rejects SVG |
| V6 Cryptography | Tokens and message bodies encrypted at rest | ✅ | EncryptedToken cast, `encrypted` cast on messages |
| V7 Logging | Audit trails, secrets redacted | ✅ | AuditLogger + Redactor; billing/inbox/domain/compliance events |
| V8 Data protection | Data export and deletion, retention | ✅ | TenantExporter, TenantDeletion, prune commands |
| V9 Communications | TLS everywhere, HSTS | ✅ | One TLS certificate for the single app host; `SecurityHeaders` sends HSTS on HTTPS (includeSubDomains/preload opt-in via `SECURITY_HSTS_INCLUDE_SUBDOMAINS`); tests/Feature/Security |
| V10 Malicious code | Dependency audit in CI | ✅ | `composer audit`, `npm audit` in .github/workflows/ci.yml |
| V11 Business logic | Idempotent payments, webhook replay safety | ✅ | billing_webhook_events / webhook_events unique ids; tests |
| V12 Files | Signed, expiring URLs for media/reports/exports | ✅ | MediaUrls, central.reports.download, export download |
| V13 API | Webhook signatures verified (Razorpay, Meta) | ✅ | HMAC checks; tests |
| V14 Configuration | Fake providers/gateways refused outside local/testing | ✅ | ProviderRegistry boot guard, BillingGateway/AiProvider bindings |
| V14 | Security headers (CSP, X-Frame-Options) | ◐ | `SecurityHeaders` middleware: nonce-locked `script-src`, `object-src 'none'`, `frame-ancestors 'none'`, X-Frame-Options DENY, nosniff, Referrer-Policy, Permissions-Policy, COOP. Still allows `'unsafe-eval'` (Alpine evaluates directive expressions); removing it means switching Livewire to `csp_safe` and moving inline Alpine expressions into `Alpine.data` components. Report-only mode: `SECURITY_CSP_REPORT_ONLY` + `SECURITY_CSP_REPORT_URI` |

## Pen-test

An external penetration test is scheduled before launch. Findings and fixes are tracked here with date, severity and commit.
