# Security and Data Integrity Audit — Phase 7A1

## Scope

Reviewed authorization, payments and wallets, lead assignment, public quotation input, reminders and notifications, CSV export, model mass assignment, database constraints, Inertia response data, and application logging.

## Confirmed Protections

- Agent Premium Leads require both assignment ownership and `assigned` distribution status for reads, notes, and pipeline updates.
- Billing and notification reads/mutations are owner-scoped; admin lead, monitor, retry, and export routes use `auth` plus `admin` middleware.
- Wallet balances mutate through `CreditWalletService` or `EWalletService`, each using a transaction and wallet row lock with a non-negative balance check.
- Payment callbacks verify CHIP signatures and retrieve/validate the provider purchase against the stored order amount, currency, and local reference. Fulfilment locks the attempt and order and stops after `fulfilled_at`.
- Lead distribution locks the assignment and then its operator state; normal assignment debits through the Lead Credit wallet service. Same-day reuse stays operator/phone/Malaysia-date scoped, has no debit, and does not advance the pointer.
- One active reminder is enforced by the unique reminder `active_key`; processing locks the reminder and assignment and revalidates the owner before notification.
- Public quotation accepts a constrained plan/smoking set, validates input, normalizes the phone, and revalidates active/eligible selected operators at submission time.
- Public registration is rate-limited to five requests per minute per server-derived IP. Public quotation submission is limited to ten requests per minute and fifty per hour per server-derived IP. These limits run before validation and distribution, so rejected requests cannot create prospects, submissions, leads, assignments, wallet debits, or round-robin mutations.
- Login retains Laravel's native five failed-attempt limiter keyed by normalized email plus IP. Email verification routes retain their existing six-per-minute throttle. CHIP callbacks intentionally remain signature-verified and idempotent without a restrictive route throttle, so provider retries are not blocked.
- CSV export reuses the lead-listing query, streams output, localizes timestamps to `Asia/Kuala_Lumpur`, excludes sensitive fields, and neutralizes spreadsheet formula prefixes.

## Defects Found and Fixed

1. Billing detail responses serialized raw payment attempts, including checkout URLs, provider references, and metadata. They now use a safe read model with operational status/timestamps only.
2. Checkout controllers reported raw provider exceptions. They now log only a safe flow label and exception class.
3. `User` exposed `is_admin`, affiliate code, and referrer fields through general mass assignment. These fields are no longer fillable; registration sets the server-derived referrer with `forceFill` after validation.

## Database Integrity

Existing constraints cover one wallet per user, affiliate codes, one affiliate commission per membership order, payment provider references and attempt numbers, lead/operator assignment uniqueness, one round-robin state per operator, and one active reminder key. No additional constraint was required by this audit.

## IC Number Protection

- `users.ic_no` stores the normalized 12-digit Malaysian IC number with Laravel's encrypted cast. `users.ic_no_hash` stores an HMAC-SHA-256 fingerprint and has the unique database constraint used for duplicate detection.
- Registration accepts only digits, spaces, and hyphens, normalizes valid variants such as `900101-14-1234` to `900101141234`, then validates and hashes the canonical value. The browser never submits a hash.
- Controlled server-side methods are `User::decryptedIcNo()` and `User::maskedIcNo()`. The mask format is `900101******`. Neither the encrypted value, plaintext, nor hash is included in standard serialization or Inertia props.
- The migration encrypts existing plaintext rows and creates their fingerprints before enforcing uniqueness. For a future production deployment, back up the database, verify all existing IC values normalize to valid unique canonical values, deploy with the required encryption/hash keys available, run the migration in a maintenance window, and validate counts before removing any rollback plan.
- Laravel encryption depends on `APP_KEY`; IC hash stability uses `IC_HASH_KEY` (falling back to `APP_KEY` only when the dedicated value is absent). Rotating either key requires a controlled decrypt/rehash backfill; rotation tooling is intentionally not included.

## Deferred Risks and Next Steps

- Reverse proxies/CDNs must be configured to trust only their known proxy addresses before relying on `$request->ip()` in production; otherwise forwarded client-IP headers can be spoofed or ignored. No deployment-specific proxy configuration is included in the application.
- CSP, secret rotation, monitoring/alerting, and broader infrastructure controls remain outside this phase.
- This review does not replace an external penetration test or provider-specific CHIP security review.
