# Security ## Reporting Report vulnerabilities to **andikadebiputra@gmail.com**. Please do not open public issues for security problems. We aim to acknowledge within 48 hours and to ship a fix or mitigation for critical issues within 7 days. --- ## Supply Chain — Known Advisories (as of 2026-05-15) ### Backend (`composer audit`) - **No vulnerability advisories.** - `laragear/webauthn` is marked **abandoned**. Replacement: `laravel/passkeys`. Migration planned for a separate change since the public surface differs. ### Frontend root (`npm audit`) - **No vulnerabilities.** ### Mobile (`mobile/`, `npm audit`) - **4 moderate** — `postcss < 8.5.10` ([GHSA-qx2v-qp2m-jg93](https://github.com/advisories/GHSA-qx2v-qp2m-jg93), XSS via unescaped `` in CSS output). - Path: transitively pulled by `expo` → `@expo/cli` → `@expo/metro-config` → `postcss`. - Reachability: the vulnerable code path is in dev build tooling, not the runtime bundle shipped to devices. End-user impact is **low**. - Fix requires bumping Expo SDK (breaking change). Tracked separately. CI runs `composer audit --abandoned=ignore` on every push and will fail on new vulnerabilities. --- ## Defense in Depth ### Authentication - **2FA** — email OTP (6 digit) with optional **trust-device** cookie (UUID + secret hashed, 30 days). Verify endpoint rate-limited 5/min. - **Passkey (WebAuthn)** — via `laragear/webauthn`, FIDO2-compliant. Login flow is challenge-bound. - **Social OAuth** — Google, Facebook, GitHub via Socialite. Callback explicitly refuses identity-overwrite when an email is already linked to a different provider id. - **Captcha** — Google reCAPTCHA v2/v3 toggleable per environment. ### Authorization - Spatie `permission`/`role` system with `active-permission` gate — a permission can be disabled centrally without revoking it from each role. - **Granular tab permissions** — 85 named permissions covering every settings tab in Global Settings and Mobile Settings. `CheckTabPermission` middleware enforces access; `@cantab`/`@managetab` Blade directives available for views. - **Impersonation** guarded against: self-impersonate, Developer role, inactive users, and nested loop. Tracked in `Cache` for the target user awareness banner and audit logged via `ImpersonationStatusChanged` event. ### Network & Boundary - **IP access control** — `IpAccessControl` middleware with: - Global blacklist - Admin route whitelist (`/users*`, `/roles*`, `/permissions*`, `/system-config*`, `/backups*`, `/admin/*`) - Auto-block IPs that exceed configurable burst threshold (24h cooldown). Sends Telegram firewall alert. - HSTS toggle (HTTPS only). - **Security headers** — `SecurityHeaders` middleware sets `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()`, `X-XSS-Protection`, and (when HTTPS + opt-in) `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload`. - **Rate limit** — Per-endpoint throttle: `/login` (5/min), `/2fa verify` (5/min), `/forgot-password`, `/api/v1/login` (10/min), `/api/v1/register` (5/min), `/api/v1/otp/send` (5/min), `/api/v1/otp/verify` (10/min). Per-IP buckets isolated. - **Single-session enforcement** — optional; logs out previous device when user logs in elsewhere. ### Passwords & Sessions - **Bcrypt 12 rounds** in production (4 in test for speed). - **Password policy** (`App\Services\Auth\PasswordPolicyService`): min/max length, mixed-case, digit, symbol, expiry, history reuse blocker (configurable N). - **Password history** stored hashed in `password_histories`; reuse of last N raises validation error. - **Sessions** stored in Redis. Admins can revoke sessions per user from `/system-settings/session-manager`. ### Data Integrity - **Foreign keys** — all audit (`created_by`/`updated_by`) → `users(id) ON DELETE SET NULL`. Owned data (`password_histories`, `user_consents`, `user_trusted_devices`, `model_has_*`, `role_has_permissions`) → `ON DELETE CASCADE`. Cascade behavior is locked in by `tests/Feature/Database/CascadeIntegrityTest.php`. - **Composite indexes** on hot lookups (`password_histories(user_id, created_at)`, `system_setting_revisions(key, created_at)`, `notifications(notifiable, read_at)`). - **Soft deletes** — User, Role, Permission. Restore + force-delete flows tested. ### Data Retention Semua data time-sensitive memiliki kebijakan retensi otomatis yang dijalankan oleh scheduler harian: | Tabel | Retensi | Mekanisme | |-------|---------|-----------| | `otp_codes` | Setelah expired | `OtpCode::prunable()` — `expires_at < now()` | | `user_trusted_devices` | Setelah expired | `UserTrustedDevice::prunable()` — `expires_at < now()` | | `ai_healing_logs` | 90 hari | `AiHealingLog::prunable()` — `created_at` | | `password_histories` | 365 hari | `PasswordHistory::prunable()` — `created_at` | | `mobile_error_logs` | 90 hari | `MobileErrorLog::prunable()` — `occurred_at` | | `ai_usage_logs` | 90 hari | `AiUsageLog::prunable()` — `created_at` | | `mobile_sync_logs` | 30 hari | `MobileSyncLog::prunable()` — `synced_at` | | `notifications` | 30 hari | `Notification::prunable()` — `created_at` | | `activity_log` | 365 hari | `activitylog:clean` via Spatie config | | `telescope_entries` | 48 jam | `telescope:prune --hours=48` | | `dashboard_widget_preferences` | Dihapus saat user dihapus | FK `ON DELETE CASCADE` ke `users` | Scheduler berjalan: `model:prune` pukul 03:00, `telescope:prune` pukul 03:05, `activitylog:clean` harian. Sesuai prinsip **data minimization** UU PDP No. 27/2022. ### Audit & Forensics - **Spatie ActivityLog** on User, Role, Permission, SystemSetting. - **System config revisions** — separate `system_setting_revisions` table captures actor, IP, user agent on every change. - **`ActivityFormatter`** redacts sensitive keys (`password`, `remember_token`, `secret`, `key`, `token`, `2fa_secret`) from displayed diffs. ### Error Handling & Monitoring - **Custom exception classes** — `App\Exceptions\{SystemConfig,BackupOperation,Monitoring}Exception` with factory methods, allowing specific handlers rather than generic `\Exception` catches. - **Sentry** — production error reporting; sample rates set in `.env`. - **Health endpoint** — `GET /api/health` reports DB, Redis, storage, queue; returns `503` only when any check `fail`s, `200` for `warn` (e.g. disk >90%). --- ## Quality Gate | Check | Tool | Threshold | |-------|------|-----------| | Unit + feature tests | Pest 4 | All passing | | Static analysis | Larastan level 5 + baseline | No new errors | | Code style | Laravel Pint | All files PSR-12 compliant | | Dependency audit | `composer audit` | 0 vulnerabilities | | N+1 regression | Pest (Query Log) | Bounded query count | CI workflow: `.github/workflows/ci.yml`. --- ## Disclosure Timeline (template) ``` T+0 Report received T+48h Acknowledgement sent T+7d Patch landed (critical) / ETA shared (others) T+30d Coordinated disclosure window ends ```