The tickets code escaped HTML, but the logistics + fuel renderers and the error banners interpolated API strings straight into innerHTML. Fuel Log fields (driver, department, fuel_type, plate) come from WhatsApp messages and vehicle/driver names from the Tracksolid registry — both user-controlled — so this was a stored-XSS path into every dispatcher's browser. - Hoist escapeHtml into HELPERS + add esc(); route every logistics/fuel renderer and the three error banners through it (21 -> 37 escaped call sites). - SRI integrity + crossorigin on Chart.js 4.4.1 and maplibre-gl 4.7.1 JS/CSS. - Caddyfile: CSP (self + pinned CDNs + CARTO basemap + the two fleet APIs), X-Content-Type-Options, Referrer-Policy, frame-ancestors 'none', -Server. Validated with `caddy validate` inside the deployed image. - loadLive(): check r.ok; pause the 15s live poll while hidden or off the Tickets tab, refresh immediately on return (FO-BUG-01/02). - Missing-API_BASE fallback flipped staging -> prod, matching the documented design (FO-OPS-01). Inline app script passes `node --check`. Audit + plan + work log in docs/260702_*. Local only; not deployed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
27 lines
2.3 KiB
Markdown
27 lines
2.3 KiB
Markdown
# FleetOps — Fix Plan (2026-07-02)
|
|
|
|
Companion to `260702_audit_report.md` and `260702_work_done.md`.
|
|
|
|
## Phase A — repo changes (implemented in this session)
|
|
|
|
| Step | Finding | Change |
|
|
|---|---|---|
|
|
| A1 | FO-SEC-01 | Hoist `escapeHtml` into the HELPERS section, add an `esc()` convenience (escapes or em-dashes), and route every API string in the logistics + fuel renderers and the three error banners through it. The tickets code already escaped — now the whole file is consistent. |
|
|
| A2 | FO-SEC-02 | Add `integrity="sha384-…" crossorigin="anonymous"` to the three CDN assets (hashes computed from the exact pinned files; recompute command documented inline). |
|
|
| A3 | FO-SEC-03 | Caddyfile: `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `frame-ancestors 'none'`, and a CSP restricting script/style/img/font/connect origins to self + the two pinned CDNs + CARTO + the two fleet APIs. `script-src` keeps `'unsafe-inline'` (the app is one inline script block); the CSP's value here is limiting external script origins and exfil targets. |
|
|
| A4 | FO-BUG-01/02 | `loadLive()` checks `r.ok`, and skips while `document.hidden` or when the Tickets tab isn't active; switching back to Tickets (or the page becoming visible) triggers an immediate refresh so markers don't wait out the 15 s interval. |
|
|
| A5 | FO-OPS-01 | Fallback API base flipped to the prod bridge (`fleetapi.rahamafresh.com`), matching the documented design. |
|
|
|
|
## Phase B — operational (needs operator decision)
|
|
|
|
| # | Item |
|
|
|---|---|
|
|
| B1 | Promotion pairing: when this branch is promoted to prod (Coolify deploy of fleetops.rahamafresh.com), redeploy the prod `dashboard_api` bridge first — it currently lacks the `/webhook/{inc,crq}-*` + `/analytics/fuel-fills` routes the SPA calls. |
|
|
| B2 | Clean up untracked root scratch files; rewrite `tracksolid_db_connection.md` for the SSH-tunnel workflow once the DB port closes. |
|
|
| B3 | Longer-term: consider vendoring Chart.js/MapLibre into `src/` (removes the CDN trust surface entirely and lets the CSP drop jsdelivr/unpkg). |
|
|
|
|
## Verification
|
|
- Extracted inline script passes `node --check`.
|
|
- Caddyfile passes `caddy validate` (run inside the deployed fleetops image).
|
|
- Manual: staging deploy → open all four tabs, confirm charts/map/popups render,
|
|
check DevTools console for CSP violations before promoting.
|