68 lines
3.5 KiB
Markdown
68 lines
3.5 KiB
Markdown
|
|
# FleetOps — Platform Audit Report (2026-07-02)
|
||
|
|
|
||
|
|
Part of the 2026-07-02 cross-repo audit (tracksolid + fleettickets + fleetops; see
|
||
|
|
the tracksolid repo's `docs/reports/260702_platform_audit_report.md` for the
|
||
|
|
DB/host-wide findings). Scope here: the SPA (`src/index.html` + `src/env.js`),
|
||
|
|
the Caddy/Docker serving layer, and the two deployed containers on twala.
|
||
|
|
Companion docs: `260702_fix_plan.md`, `260702_work_done.md`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## High
|
||
|
|
|
||
|
|
### FO-SEC-01 — Stored XSS: API strings rendered into `innerHTML` unescaped
|
||
|
|
The tickets explorer and map popups escape correctly (`escapeHtml`), but the
|
||
|
|
**logistics tables (`renderVehicles`, `renderDrivers`), the fuel banner notes, and
|
||
|
|
all three Fuel Log tables (`renderFuelVehicles`, `renderFuelDepartments`,
|
||
|
|
`renderFuelRecent`)** interpolate API strings straight into `innerHTML`. The Fuel
|
||
|
|
Log fields (`driver`, `department`, `fuel_type`, `plate`) originate from **WhatsApp
|
||
|
|
messages** — genuinely user-generated content — and vehicle/driver names come from
|
||
|
|
the Tracksolid registry, editable by any fleet-console user. A crafted name like
|
||
|
|
`<img src=x onerror=…>` executes in every dispatcher's browser. Error banners also
|
||
|
|
render `e.message` unescaped (server-influenced text).
|
||
|
|
|
||
|
|
### FO-SEC-02 — CDN scripts loaded without Subresource Integrity
|
||
|
|
Chart.js (jsdelivr) and MapLibre (unpkg) load with no `integrity`/`crossorigin`
|
||
|
|
attributes — a compromised or MITM'd CDN response executes arbitrary script with
|
||
|
|
access to the fleet APIs.
|
||
|
|
|
||
|
|
### FO-SEC-03 — No security headers from Caddy
|
||
|
|
No `Content-Security-Policy`, `X-Content-Type-Options`, `Referrer-Policy`, or
|
||
|
|
frame-ancestors protection. Combined with FO-SEC-01 this leaves injected markup
|
||
|
|
free to load external script and exfiltrate anywhere.
|
||
|
|
|
||
|
|
## Medium
|
||
|
|
|
||
|
|
### FO-BUG-01 — `loadLive()` parses the response without checking `r.ok`
|
||
|
|
A 4xx/5xx from `/webhook/live-positions` throws inside `r.json()` on non-JSON
|
||
|
|
bodies with an unhelpful error; harmless but noisy.
|
||
|
|
|
||
|
|
### FO-BUG-02 — Live-position poll never pauses
|
||
|
|
The 15-second `/webhook/live-positions` poll starts when the Tickets map is first
|
||
|
|
opened and then runs forever — including when the user is on the Logistics/Fuel
|
||
|
|
tabs and when the browser tab is hidden overnight. A dashboard left open ≈ 5,760
|
||
|
|
wasted API calls/day per viewer.
|
||
|
|
|
||
|
|
### FO-OPS-01 — Fallback API base pointed at *staging*
|
||
|
|
`index.html` fell back to `https://fleetapi.fivetitude.com` when `env.js` isn't
|
||
|
|
templated. CLAUDE.md documents the intended fallback as the **prod** API. A prod
|
||
|
|
pod missing `API_BASE` would silently lean on the staging bridge (and fail CORS
|
||
|
|
confusingly).
|
||
|
|
|
||
|
|
## Notes / by design
|
||
|
|
|
||
|
|
- **FO-OPS-02** — the prod container (`API_BASE=fleetapi.rahamafresh.com`) runs
|
||
|
|
commit `21bca24`, ~19 commits behind HEAD — this is the documented frozen-prod
|
||
|
|
staging-umbrella pattern, not drift. But note: **promotion must pair the SPA
|
||
|
|
deploy with the prod `dashboard_api` bridge redeploy** — prod's bridge currently
|
||
|
|
lacks all `/webhook/{inc,crq}-*` and `/analytics/fuel-fills` routes the newer
|
||
|
|
SPA calls (see the tracksolid audit, OPS-01).
|
||
|
|
- **FO-OPS-03** — untracked scratch files at the repo root
|
||
|
|
(`marker-preview.html`, `tracksolid_db_connection.md`, `webook_instructions.txt`).
|
||
|
|
`tracksolid_db_connection.md` documents the public `twala:5433` superuser
|
||
|
|
connection pattern that the tracksolid audit is eliminating — update or remove
|
||
|
|
once the SSH-tunnel workflow lands.
|
||
|
|
- The serving layer is otherwise good: Caddyfile validated at build time,
|
||
|
|
healthcheck endpoint, runtime `API_BASE` injection via `templates`, `no-store`
|
||
|
|
on the shell and env.js.
|