docs: remove admin SPA references and update production URLs

The admin SPA (apps/admin/) has been retired. Its functionality now
lives in apps/app/ under /platform/* routes for super_admin users.
Updated all documentation to reflect: 2 SPAs instead of 3, removed
FRONTEND_ADMIN_URL/port 5173 references, changed production URL from
app.crewli.app to crewli.app. Retired admin-specific security audit
findings (A13-2, A13-4, A13-5, A13-7) and APPS-01 backlog item.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 08:21:44 +02:00
parent 2933d957a6
commit 945e22f322
13 changed files with 102 additions and 168 deletions

View File

@@ -5,7 +5,7 @@
- **Total findings: 57**
- **Critical: 10 | High: 19 | Medium: 18 | Low: 10**
Audit scope: all files under `api/` and `apps/` (admin, app, portal).
Audit scope: all files under `api/` and `apps/` (app, portal).
---
@@ -337,7 +337,7 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### [MEDIUM] A05-3: Session `SameSite=Lax` may break cross-subdomain Sanctum cookie auth
- **File:** `api/config/session.php:202`
- **Description:** With three SPAs on different subdomains calling `api.crewli.app`, `SameSite=Lax` blocks session cookies on cross-origin requests.
- **Description:** With two SPAs on different subdomains calling `api.crewli.app`, `SameSite=Lax` blocks session cookies on cross-origin requests.
- **Risk:** Sanctum SPA authentication may fail silently in production.
- **Fix:** Set `SESSION_DOMAIN=.crewli.app` and `SESSION_SAME_SITE=none` (with `SESSION_SECURE_COOKIE=true`) in production.
@@ -364,7 +364,7 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### Positive findings (A05)
- CORS `allowed_origins` correctly restricted to three specific SPAs (not `*`).
- CORS `allowed_origins` correctly restricted to two specific SPAs (not `*`).
- `supports_credentials: true` correctly set for Sanctum.
- `APP_DEBUG` defaults to `false` in `config/app.php`.
- No Telescope/Horizon routes found (not installed).
@@ -378,21 +378,21 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### [CRITICAL] A06-1: `@casl/ability` 6.7.3 — Prototype Pollution
- **File:** `apps/*/package.json`
- **Description:** CASL is used for frontend permission enforcement across all three apps. Prototype pollution could corrupt ability checks.
- **File:** `apps/app/package.json`, `apps/portal/package.json`
- **Description:** CASL is used for frontend permission enforcement across both apps. Prototype pollution could corrupt ability checks.
- **Risk:** Permission bypass in the frontend. Admin tokens at elevated risk.
- **Fix:** Upgrade to `>=6.7.5` in all three apps.
- **Fix:** Upgrade to `>=6.7.5` in both apps.
#### [CRITICAL] A06-2: `axios` ~1.13.x — SSRF and metadata exfiltration
- **File:** `apps/*/package.json`
- **File:** `apps/app/package.json`, `apps/portal/package.json`
- **Description:** Two critical CVEs: `NO_PROXY` hostname normalization bypass (SSRF) and unrestricted cloud metadata exfiltration via header injection.
- **Risk:** Cloud metadata theft, SSRF in SSR contexts.
- **Fix:** Upgrade to `>=1.15.0` in all three apps.
- **Fix:** Upgrade to `>=1.15.0` in both apps.
#### [CRITICAL] A06-3: `swiper` 11.2.10 — Prototype Pollution
- **File:** `apps/*/package.json`
- **File:** `apps/app/package.json`, `apps/portal/package.json`
- **Description:** Prototype pollution vulnerability.
- **Risk:** Client-side code execution.
- **Fix:** Upgrade to `>=12.1.2` (major version bump — review migration guide).
@@ -420,7 +420,7 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### [LOW] A06-7: Frontend dev dependencies — 45 vulnerabilities in build tools
- **File:** `apps/*/pnpm-lock.yaml`
- **File:** `apps/app/pnpm-lock.yaml`, `apps/portal/pnpm-lock.yaml`
- **Description:** `vite`, `rollup`, `tar`, `undici`, `minimatch`, `svgo`, `flatted`, `immutable`, `lodash`, `picomatch` — various high/moderate vulnerabilities in dev/build-time dependencies.
- **Risk:** Not shipped to production. CI/CD pipeline risk.
- **Fix:** Update dev dependencies: `pnpm update` for each app.
@@ -554,58 +554,51 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### [CRITICAL] A13-1: Bearer tokens stored in `localStorage` (apps/app and apps/portal)
- **File:** `apps/app/src/stores/useAuthStore.ts:7,10,30,66`
- **File:** `apps/portal/src/stores/useAuthStore.ts:6,9,18,20`
- **File:** `apps/app/src/stores/useAuthStore.ts`
- **File:** `apps/portal/src/stores/useAuthStore.ts`
- **Description:** Sanctum bearer tokens stored in `localStorage` under `crewli_token` and `crewli_portal_token`. Accessible to any JavaScript on the page.
- **Risk:** Any XSS vulnerability (or supply-chain attack) can steal tokens and impersonate users indefinitely.
- **Fix:** Migrate to `httpOnly; Secure; SameSite=Strict` cookies set by the Laravel backend. Remove `setItem`/`getItem` usage.
#### [HIGH] A13-2: Admin app cookies lack `httpOnly`, `Secure`, and `SameSite` flags
#### ~~[HIGH] A13-2: Admin app cookies lack `httpOnly`, `Secure`, and `SameSite` flags~~ RETIRED
- **File:** `apps/admin/src/@core/composable/useCookie.ts:38-43`
- **Description:** Token, user data, and ability rules stored in cookies written via `document.cookie` with no `httpOnly`, `secure`, or `sameSite`.
- **Risk:** XSS token theft and CSRF.
- **Fix:** Refactor to have the API set httpOnly cookies via `Set-Cookie` response header. Add `sameSite: 'strict'` as interim measure.
- **File:** `apps/admin/` (removed)
- **Description:** The admin SPA has been retired. Its functionality now lives in `apps/app/` under `/platform/*` routes.
- **Resolution:** Finding no longer applicable — `apps/admin/` has been removed.
#### [HIGH] A13-3: Open redirect vulnerability on post-login redirect (all apps)
- **File:** `apps/portal/src/pages/login.vue:61,74-76`
- **File:** `apps/app/src/pages/login.vue:55`
- **File:** `apps/admin/src/pages/login.vue:97`
- **Description:** All login pages accept `?to=` query parameter and redirect to it after login without validating it's a relative path. Portal falls back to `window.location.href` with the raw value.
- **Risk:** Phishing: `https://portal.crewli.app/login?to=https://evil.com/steal`.
- **Fix:** Validate that redirect target starts with `/` before using it.
#### [HIGH] A13-4: `v-html` with API-sourced data in admin app (template pages)
#### ~~[HIGH] A13-4: `v-html` with API-sourced data in admin app (template pages)~~ RETIRED
- **File:** `apps/admin/src/pages/front-pages/help-center/article/[title].vue:59-62`
- **File:** `apps/admin/src/pages/apps/academy/course-details.vue:154`
- **Description:** Template/demo pages from Vuexy render API data with `v-html` — stored XSS vector.
- **Risk:** XSS within the admin app where users have super_admin privileges.
- **Fix:** Remove these template demo pages entirely, or sanitize with DOMPurify.
- **File:** `apps/admin/` (removed)
- **Description:** The admin SPA has been retired. Its functionality now lives in `apps/app/` under `/platform/*` routes.
- **Resolution:** Finding no longer applicable — `apps/admin/` has been removed.
#### [HIGH] A13-5: Admin router guard relies on JS-readable cookie without server validation
#### ~~[HIGH] A13-5: Admin router guard relies on JS-readable cookie without server validation~~ RETIRED
- **File:** `apps/admin/src/plugins/1.router/guards.ts:40-42`
- **Description:** Guard reads `userData`/`accessToken` from `document.cookie`. Does not call API to validate — just checks truthy value.
- **Risk:** An XSS attacker who steals cookies bypasses the guard. Stale or tampered cookies pass validation.
- **Fix:** Use the `authStore.initialize()` pattern from the app/portal guards (server-validates token on init).
- **File:** `apps/admin/` (removed)
- **Description:** The admin SPA has been retired. Its functionality now lives in `apps/app/` under `/platform/*` routes.
- **Resolution:** Finding no longer applicable — `apps/admin/` has been removed.
#### [MEDIUM] A13-6: Server error messages forwarded to UI
- **File:** `apps/admin/src/pages/events/create.vue:74`
- **File:** `apps/app/src/pages/login.vue:59-69`
- **File:** `apps/portal/src/pages/login.vue:51`
- **Description:** Raw `data.message` from API errors rendered in UI. Could expose internal details if backend misconfigured.
- **Risk:** Information disclosure (table names, file paths).
- **Fix:** Use safelist-based error mapping. The portal's `mapLoginErrorMessage` is a good pattern — extend it.
#### [MEDIUM] A13-7: Admin `main.ts` mount-error handler uses `innerHTML` interpolation
#### ~~[MEDIUM] A13-7: Admin `main.ts` mount-error handler uses `innerHTML` interpolation~~ RETIRED
- **File:** `apps/admin/src/main.ts:41`
- **Description:** Error fallback injects `error` object via template string into `innerHTML`.
- **Risk:** XSS if error message is attacker-influenced.
- **Fix:** Use `document.createTextNode(String(error))` instead.
- **File:** `apps/admin/` (removed)
- **Description:** The admin SPA has been retired. Its functionality now lives in `apps/app/` under `/platform/*` routes.
- **Resolution:** Finding no longer applicable — `apps/admin/` has been removed.
#### [MEDIUM] A13-8: Portal localStorage event state persists across session expiry
@@ -616,8 +609,8 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
#### [LOW] A13-9: No Content Security Policy on any SPA — RESOLVED
- **File:** `apps/*/index.html`, `api/app/Http/Middleware/SecurityHeaders.php`, `deploy/nginx/`
- **Description:** ~~None of the three apps set a CSP meta tag or header.~~
- **File:** `apps/app/index.html`, `apps/portal/index.html`, `api/app/Http/Middleware/SecurityHeaders.php`, `deploy/nginx/`
- **Description:** ~~Neither app set a CSP meta tag or header.~~
- **Risk:** Injected scripts have unrestricted access.
- **Resolution:** API CSP enforced via `SecurityHeaders` middleware (`default-src 'none'; frame-ancestors 'none'`). SPA CSP configured via Nginx snippets (`deploy/nginx/csp-spa.conf`, `csp-portal.conf`). Dev CSP meta tags added to all `index.html` files for local testing. See `deploy/README.md` for rollout instructions.
@@ -632,7 +625,7 @@ Audit scope: all files under `api/` and `apps/` (admin, app, portal).
The following security measures ARE correctly implemented:
1. **Password hashing:** bcrypt with `BCRYPT_ROUNDS=12`. `$password` in User model's `$hidden` array.
2. **CORS origins:** Correctly restricted to three specific SPA origins (not `*`). `supports_credentials: true` set.
2. **CORS origins:** Correctly restricted to two specific SPA origins (not `*`). `supports_credentials: true` set.
3. **SQL injection prevention:** All Eloquent queries use PDO parameter binding. All `whereRaw()`/`selectRaw()` calls use `?` placeholders.
4. **No shell execution:** No `exec()`, `shell_exec()`, `system()`, `proc_open()`, or `passthru()` in application code.
5. **Blade escaping:** All Blade output uses `{{ }}` (escaped). Zero instances of `{!! !!}`.
@@ -685,5 +678,5 @@ The following security measures ARE correctly implemented:
| 17 | A01-13: Move event routes under org prefix | Large |
| 18 | A09-1/2/3: Add security logging | Medium |
| 19 | A07-3: Strengthen password rules | Small |
| 20 | A13-2/5: Fix admin cookie security | Medium |
| 20 | ~~A13-2/5: Fix admin cookie security~~ RETIRED (apps/admin removed) | N/A |
| 21 | A06-3: Upgrade swiper (major version) | Medium |