From 0000926358d3b071d582655442c9fb98542622ae Mon Sep 17 00:00:00 2001 From: dtzp555-max Date: Mon, 1 Jun 2026 06:59:15 +1000 Subject: [PATCH] fix: escape dashboard status/plan cards (#124) (#126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up defense-in-depth from #114's review. The status-cards and plan-cards in dashboard.html rendered string values into innerHTML without escaping. These are trusted-but-external (p.version/p.uptime are server-local; s.percent/s.resetsIn/ w.percent/w.resetsIn come from Anthropic's upstream plan API), so not the stored-XSS vector #114 fixed — but wrapping them in the existing escapeHtml() helper gives uniform defense-in-depth across all innerHTML sinks. Numeric/computed fields (request counts, sPct/wPct, barColor) are left unescaped (not injectable). dashboard.html is a client-side static asset, not server.mjs → cli.js citation N/A. Independent fresh-context reviewer (opus): APPROVE (Iron Rule 10) — confirmed every string sink wrapped, numbers correctly untouched, escapeHtml in scope, template literals intact, 181 tests pass (no regression). Closes #124. Co-authored-by: dtzp555 Co-authored-by: Claude Opus 4.8 --- dashboard.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dashboard.html b/dashboard.html index 1e5d320..1709acb 100644 --- a/dashboard.html +++ b/dashboard.html @@ -148,8 +148,8 @@ async function refreshStatus() { const r = data.requests || {}; document.getElementById("status-cards").innerHTML = ` -
Status
${p.status || '?'}
v${p.version || '?'}
-
Uptime
${p.uptime || '?'}
+
Status
${escapeHtml(p.status || '?')}
v${escapeHtml(p.version || '?')}
+
Uptime
${escapeHtml(p.uptime || '?')}
Requests
${r.total || 0}
${r.active || 0} active
Errors
${r.errors || 0}
${r.timeouts || 0} timeouts
Sessions
${p.activeSessions || 0}
@@ -164,15 +164,15 @@ async function refreshStatus() { document.getElementById("plan-cards").innerHTML = `
Session (5h)
-
${s.percent || '?'}
+
${escapeHtml(s.percent || '?')}
-
Resets in ${s.resetsIn || '?'}
+
Resets in ${escapeHtml(s.resetsIn || '?')}
Weekly (7d)
-
${w.percent || '?'}
+
${escapeHtml(w.percent || '?')}
-
Resets in ${w.resetsIn || '?'}
+
Resets in ${escapeHtml(w.resetsIn || '?')}
`; }