feat(security): apply html.escape sanitization across WebUI table rendering
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m35s
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m35s
This commit is contained in:
+38
-22
@@ -11,6 +11,7 @@ import secrets
|
||||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
import html
|
||||
import warnings
|
||||
import threading
|
||||
from datetime import datetime, timezone, timedelta
|
||||
@@ -1806,24 +1807,29 @@ def serve_webui(request: Request = None):
|
||||
client_rows = []
|
||||
for cl in clients:
|
||||
cid, host, ost, fp, st, first, last = cl
|
||||
esc_cid = html.escape(str(cid))
|
||||
esc_host = html.escape(str(host))
|
||||
esc_ost = html.escape(str(ost))
|
||||
esc_fp = html.escape(str(fp))
|
||||
esc_st = html.escape(str(st))
|
||||
badge_cls = "badge-active" if st == "active" else "badge-revoked"
|
||||
rel_time = format_relative_time(last)
|
||||
last_display = f"<div><strong>{last[:19]}</strong></div><div style='font-size:11px; color:#94a3b8;'>{rel_time}</div>"
|
||||
last_display = f"<div><strong>{html.escape(str(last[:19]))}</strong></div><div style='font-size:11px; color:#94a3b8;'>{html.escape(rel_time)}</div>"
|
||||
|
||||
btn_action = (
|
||||
f'<button class="btn btn-outline" style="padding:4px 8px; font-size:11px;" onclick="revokeClient(\'{cid}\')">Revoke</button>'
|
||||
f'<button class="btn btn-outline" style="padding:4px 8px; font-size:11px;" onclick="revokeClient(\'{esc_cid}\')">Revoke</button>'
|
||||
if st == "active" else
|
||||
f'<button class="btn btn-accent" style="padding:4px 8px; font-size:11px;" onclick="approveClient(\'{cid}\')">Approve</button>'
|
||||
f'<button class="btn btn-accent" style="padding:4px 8px; font-size:11px;" onclick="approveClient(\'{esc_cid}\')">Approve</button>'
|
||||
)
|
||||
btn_del = f'<button class="btn btn-danger" style="padding:4px 8px; font-size:11px;" onclick="deleteClient(\'{cid}\')" title="Remove client and free seat">Remove</button>'
|
||||
btn_del = f'<button class="btn btn-danger" style="padding:4px 8px; font-size:11px;" onclick="deleteClient(\'{esc_cid}\')" title="Remove client and free seat">Remove</button>'
|
||||
|
||||
client_rows.append(f"""
|
||||
<tr>
|
||||
<td><span class="badge {badge_cls}">{st.upper()}</span></td>
|
||||
<td><strong>{host}</strong></td>
|
||||
<td><code>{cid}</code></td>
|
||||
<td><span class="badge badge-os">{ost.upper()}</span></td>
|
||||
<td><code title="{fp}" style="cursor:pointer;" onclick="copyText('{fp}', 'Cert Fingerprint')">{fp[:16]}...</code></td>
|
||||
<td><span class="badge {badge_cls}">{esc_st.upper()}</span></td>
|
||||
<td><strong>{esc_host}</strong></td>
|
||||
<td><code>{esc_cid}</code></td>
|
||||
<td><span class="badge badge-os">{esc_ost.upper()}</span></td>
|
||||
<td><code title="{esc_fp}" style="cursor:pointer;" onclick="copyText('{esc_fp}', 'Cert Fingerprint')">{esc_fp[:16]}...</code></td>
|
||||
<td>{last_display}</td>
|
||||
<td>
|
||||
<div style="display:flex; gap:6px; align-items:center;">
|
||||
@@ -1839,15 +1845,20 @@ def serve_webui(request: Request = None):
|
||||
anomaly_rows = []
|
||||
for an in anomalies:
|
||||
afp, srv, sev, msg, rc, ls = an
|
||||
esc_srv = html.escape(str(srv))
|
||||
esc_sev = html.escape(str(sev))
|
||||
esc_msg = html.escape(str(msg))
|
||||
esc_ls = html.escape(str(ls[:19]))
|
||||
sev_cls = "badge-error" if sev in ("ERROR", "CRITICAL", "FATAL") else "badge-warning"
|
||||
short_msg = msg[:80] + "..." if len(msg) > 80 else msg
|
||||
esc_short_msg = html.escape(str(short_msg))
|
||||
anomaly_rows.append(f"""
|
||||
<tr>
|
||||
<td><span class="badge {sev_cls}">{sev}</span></td>
|
||||
<td><strong>{srv}</strong></td>
|
||||
<td><span class="badge badge-active">{rc} runs</span></td>
|
||||
<td title="{msg}">{short_msg}</td>
|
||||
<td>{ls[:19]}</td>
|
||||
<td><span class="badge {sev_cls}">{esc_sev}</span></td>
|
||||
<td><strong>{esc_srv}</strong></td>
|
||||
<td><span class="badge badge-active">{int(rc)} runs</span></td>
|
||||
<td title="{esc_msg}">{esc_short_msg}</td>
|
||||
<td>{esc_ls}</td>
|
||||
</tr>
|
||||
""")
|
||||
anomalies_html = "".join(anomaly_rows) if anomaly_rows else '<tr><td colspan="5" class="empty-state">No verified anomalies currently reported.</td></tr>'
|
||||
@@ -1856,19 +1867,24 @@ def serve_webui(request: Request = None):
|
||||
token_rows = []
|
||||
for tk in hermes_tokens:
|
||||
tok, tname, tcreated, tlast, tstatus = tk
|
||||
esc_tok = html.escape(str(tok))
|
||||
esc_tname = html.escape(str(tname))
|
||||
esc_tstatus = html.escape(str(tstatus))
|
||||
esc_tcreated = html.escape(str(tcreated[:19]))
|
||||
esc_tlast = html.escape(str(tlast[:19])) if tlast else 'Never'
|
||||
tbadge = "badge-active" if tstatus == "active" else "badge-revoked"
|
||||
masked = tok[:10] + "••••••••" + tok[-4:]
|
||||
tok_action = f'<button class="btn btn-danger" style="padding:4px 8px; font-size:11px;" onclick="revokeToken(\'{tok}\')">Revoke</button>' if tstatus == "active" else f'<button class="btn btn-outline" style="padding:4px 8px; font-size:11px;" onclick="deleteToken(\'{tok}\')">Delete</button>'
|
||||
masked = esc_tok[:10] + "••••••••" + esc_tok[-4:]
|
||||
tok_action = f'<button class="btn btn-danger" style="padding:4px 8px; font-size:11px;" onclick="revokeToken(\'{esc_tok}\')">Revoke</button>' if tstatus == "active" else f'<button class="btn btn-outline" style="padding:4px 8px; font-size:11px;" onclick="deleteToken(\'{esc_tok}\')">Delete</button>'
|
||||
token_rows.append(f"""
|
||||
<tr>
|
||||
<td><strong>{tname}</strong></td>
|
||||
<td><code style="cursor:pointer;" title="Click to copy full token" onclick="copyText('{tok}', 'Hermes API Token')">{masked}</code></td>
|
||||
<td><span class="badge {tbadge}">{tstatus.upper()}</span></td>
|
||||
<td style="color:#94a3b8; font-size:12px;">{tcreated[:19]}</td>
|
||||
<td style="color:#94a3b8; font-size:12px;">{tlast[:19] if tlast else 'Never'}</td>
|
||||
<td><strong>{esc_tname}</strong></td>
|
||||
<td><code style="cursor:pointer;" title="Click to copy full token" onclick="copyText('{esc_tok}', 'Hermes API Token')">{masked}</code></td>
|
||||
<td><span class="badge {tbadge}">{esc_tstatus.upper()}</span></td>
|
||||
<td style="color:#94a3b8; font-size:12px;">{esc_tcreated}</td>
|
||||
<td style="color:#94a3b8; font-size:12px;">{esc_tlast}</td>
|
||||
<td>
|
||||
<div style="display:flex; gap:6px; align-items:center;">
|
||||
<button class="btn btn-secondary" style="padding:4px 8px; font-size:11px;" onclick="copyText('{tok}', 'Hermes API Token')">Copy</button>
|
||||
<button class="btn btn-secondary" style="padding:4px 8px; font-size:11px;" onclick="copyText('{esc_tok}', 'Hermes API Token')">Copy</button>
|
||||
{tok_action}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user