# Ops Portal Full Code Audit — July 12, 2026

Auditor: Hermes | Pages: 11 | Total bugs: 42

## The Four Root Causes

Everything else flows from these four:

### RC-1: `window.initNav` is undefined (affects ALL pages)
`app.js` exports `initNav` only under `window.Ops.initNav`, not `window.initNav`.
Every page's guard `typeof window.initNav === 'function'` evaluates FALSE.
Nav hamburger, active-link, and logout wiring are dead on every page.

**Fix (one line in app.js, inside IIFE before `window.Ops = {...}`):**
```javascript
window.initNav = initNav;
```

### RC-2: `utils.js` does not exist
All pages reference `/js/utils.js?v=2` but the file is `/js/app.js`.
The 404 is silent because `app.js` loads next and provides `window.Ops`.
But if the server returns a hard error, all pages break.

**Fix:** Rename `app.js` → `utils.js` OR update all `<script src="/js/utils.js">` to `app.js`.

### RC-3: `logoutBtn` DOM lookup before nav fetch completes
`services.html`, `servers.html`, `backups.html` all call
`document.getElementById('logoutBtn').classList.add('show')` synchronously.
`logoutBtn` lives in `nav.html` which is fetched asynchronously.
Result: TypeError on every authenticated page load on those 3 pages.

**Fix:** Move the `getElementById('logoutBtn')` call inside the fetch callback:
```javascript
fetch('/nav.html').then(r => r.text()).then(h => {
  document.getElementById('nav').innerHTML = h;
  document.getElementById('logoutBtn').classList.add('show'); // ← here
  if (typeof window.initNav === 'function') window.initNav();
});
```

### RC-4: 6 of 11 pages have NO auth guard
network.html, cron.html, config.html, logs.html, cost.html, fleettracker360.html
load and show data without any `Ops.isAuthenticated()` check.

---

## Full Bug Inventory by Page

### index.html
- B1 [HIGH] utils.js 404 (silent)
- B2 [HIGH] window.initNav() call fails — window.initNav undefined
- B3 [MED] navUser shown via inline style, inconsistent with .show pattern
- B4 [MED] logoutBtn shown but never wired to logout (depends on initNav fix)

### services.html
- S1 [CRIT] utils.js 404
- S2 [HIGH] login-overlay hardcoded class="show" — flash of login for auth'd users
- S3 [HIGH] loginError "Invalid credentials" visible without display:none
- S4 [CRIT] logoutBtn/navUser lookup before nav.html fetch completes → TypeError
- S5 [MED]  window.initNav undefined (nav never initialized)
- S6 [MED]  No guard for session expiry mid-session

### servers.html
- V1 [CRIT] utils.js 404
- V2 [CRIT] logoutBtn.style.display = 'block' before nav loads → TypeError
- V3 [HIGH] Login form inputs have no CSS classes, no form-group wrapper — unstyled
- V4 [MED]  loginError always shows hardcoded "Invalid credentials" text
- V5 [MED]  window.initNav undefined
- V6 [LOW]  No <label> tags on login inputs (accessibility + autofill)
- V7 [LOW]  login-logo uses emoji 🖥 instead of SVG (inconsistent)

### backups.html
- K1 [CRIT] utils.js 404
- K2 [CRIT] logoutBtn.classList.add('show') before nav loads → TypeError
- K3 [HIGH] login-overlay hardcoded class="show"
- K4 [MED]  loginError no display:none
- K5 [MED]  window.initNav undefined
- K6 [HIGH] .summary-bar, .summary-item, .summary-label, .summary-val not in ops.css

### network.html
- N1 [CRIT] utils.js 404
- N2 [HIGH] Scripts in <head> without defer (execute before DOM)
- N3 [CRIT] NO auth guard — page shows all network data without login
- N4 [MED]  window.initNav undefined
- N5 [MED]  Grafana link uses Tailscale IP 100.71.155.7 — unreachable without Tailscale
- N6 [LOW]  Inline <style> redefines .card, .badge, .status-dot (may conflict with ops.css)

### cron.html
- C1 [CRIT] utils.js 404
- C2 [HIGH] Scripts in <head> without defer
- C3 [CRIT] NO auth guard
- C4 [HIGH] <main id="main-content"> placed AFTER the three <section> elements (inverted)
- C5 [MED]  window.initNav undefined
- C6 [LOW]  app.js loaded but Ops.* never used — all utilities re-implemented locally

### config.html
- G1 [CRIT] utils.js 404
- G2 [CRIT] window.escapeHtml() called throughout — not exported by app.js → TypeError
- G3 [HIGH] window.formatTime() used — not exported → falls back to Date.toLocaleString()
- G4 [HIGH] fetchStatus() called — not defined anywhere → typeof guard prevents crash but live data never loads
- G5 [CRIT] NO auth guard
- G6 [MED]  window.initNav undefined
- G7 [HIGH] Local initNav() defined inside IIFE shadows concept, calls window.initNav (self-referencing risk)
- G8 [MED]  .content-area and .page-footer not in ops.css
- G9 [MED]  Uses <header class="header"> but ops.css defines .page-header not .header

### logs.html
- L1 [CRIT] utils.js 404
- L2 [HIGH] Scripts in <head> without defer
- L3 [CRIT] NO auth guard
- L4 [MED]  window.initNav undefined
- L5 [MED]  <main> doesn't cleanly wrap content (navPlaceholder outside main, script inside </main>)

### audit.html
- A1 [CRIT] utils.js 404
- A2 [CRIT] Uses sessionStorage for token — all other pages use localStorage — token not found on nav
- A3 [MED]  app.js loaded but Ops.* never used — own escapeHtml/fmtTime re-implemented
- A4 [MED]  logoutBtn in nav never wired (initNav not called)
- A5 [MED]  window.initNav undefined
- A6 [LOW]  Login form pre-filled with value="germaine" — leaks valid username

### cost.html
- T1 [CRIT] utils.js 404
- T2 [CRIT] window.fetchStatus(...) called — not defined anywhere → TypeError on load → no data renders
- T3 [CRIT] NO auth guard
- T4 [MED]  app.js loaded but unused
- T5 [MED]  window.initNav undefined
- T6 [LOW]  Refresh button uses Unicode ↻ not SVG icon, .spinning animation doesn't apply

### fleettracker360.html
- F1 [HIGH] No app.js or utils.js loaded — no auth system
- F2 [HIGH] NO auth guard
- F3 [MED]  Missing viewport-fit=cover (all other pages have it)
- F4 [LOW]  Hardcoded date "2026-07-12" in timestamp string
- F5 [LOW]  COT hour calculation: `(utcHours + (-5) + 24).slice(-2)` fails at midnight (produces "24")
- F6 [LOW]  Not linked from nav.html — orphaned page

---

## Cross-Cutting Issues

| ID | Bug | Pages | Severity |
|----|-----|-------|----------|
| X1 | /js/utils.js 404 | All 10 auth pages | CRIT |
| X2 | window.initNav undefined | All pages with fetch-nav | HIGH |
| X3 | logoutBtn null before nav loads | services, servers, backups | CRIT |
| X4 | No auth guard | network, cron, config, logs, cost, fleettracker | CRIT |
| X5 | login-overlay hardcoded show class | services, servers, backups | HIGH |
| X6 | Grafana link unreachable on mobile | nav.html + index.html | MED |
| X7 | ops.css vs ops.css?v=2 version inconsistency | Mixed across pages | LOW |

---

## Priority Fix Order

1. Add `window.initNav = initNav;` to app.js (fixes X2, B2, S5, V5, K5, N4, C5, G6, L4, A5, T5)
2. Rename utils.js reference or file (fixes X1)
3. Move logoutBtn lookups inside nav fetch callback (fixes X3)
4. Replace `window.fetchStatus(...)` in cost.html with direct fetch (fixes T2)
5. Add `window.escapeHtml = Ops.escapeHtml` to app.js or fix config.html (fixes G2)
6. Change audit.html to use localStorage not sessionStorage (fixes A2)
7. Add auth guard + login overlay to network, cron, config, logs, cost (fixes X4)
8. Remove hardcoded `show` class from login-overlay in services/servers/backups HTML (fixes X5)
9. Fix <main> placement in cron.html (fixes C4)
10. Add .summary-bar CSS to ops.css (fixes K6)
