Files
hermes-skills/skills/devops/ops-portal-and-collector/references/portal-audit-jul12-2026.md
T

7.2 KiB

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 = {...}):

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.jsutils.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:

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 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] placed AFTER the three
    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 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] doesn't cleanly wrap content (navPlaceholder outside main, script inside )

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 placement in cron.html (fixes C4)
  10. Add .summary-bar CSS to ops.css (fixes K6)