[Security Audit] Stored XSS in stats dashboard - unescaped URL paths #3
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: HIGH
Description
The stats dashboard at /stats renders user-requested URL paths in HTML without any HTML entity encoding, creating a stored XSS vulnerability.
Affected Files
Reproduction
Impact
An attacker can craft URLs that, when proxied through the mirror and viewed by an admin on the stats dashboard, execute arbitrary JavaScript. This can steal cookies, session tokens, or perform actions as the admin.
Fix
Escape HTML entities in path values before rendering. Use html.escape(p["path"]) in app.py line 309, or use a proper template engine (Jinja2) with auto-escaping.
Auto-Triage Report for Issue #3
Status: Open
Existing Labels: bug, priority:high (correct)
Assignee: @akiba (newly assigned)
Summary: [Security Audit] Stored XSS in stats dashboard — unescaped URL paths rendered in HTML at /stats. Severity: HIGH.
Assessment: Legitimate security finding. The issue is well-documented with reproduction steps and a clear fix (use html.escape() or a template engine with auto-escaping). Priority:high label is appropriate given the XSS vector.
Next Steps:
Triage Summary
Status: 🟢 Confirmed — Stored XSS in stats dashboard
Severity: HIGH
Priority: 🔴 Critical — fix immediately
Assessment
This is a legitimate stored XSS vulnerability. The
p["path"]value on app.py:309 is interpolated directly into HTML via f-string without escaping. An attacker can craft a URL like/<img src=x onerror=alert(1)>/anythingthat, when proxied through the mirror and viewed on the stats dashboard by an admin, will execute arbitrary JavaScript in the admin's browser session.Recommended Next Steps
html.escape(p["path"])in app.py line 309Triage Actions Taken
bug,priority:high(already set) ✅🤖 Triage Summary
Classification: Bug / Security (HIGH severity)
Labels: bug, priority:high ✅
Assignee: akiba ✅
Assessment: This is a legitimate stored XSS vulnerability. The stats dashboard renders unescaped URL paths directly into HTML via f-string interpolation, allowing arbitrary JavaScript execution from crafted request URLs.
Recommended Next Steps:
Priority rationale: HIGH — an attacker can steal admin cookies/session tokens via crafted URLs viewed on the stats page.
🔄 Follow-up Triage (2026-06-30)
Status: Still open. Labels (
bug,priority:high) and assignee (akiba) remain correct — this is the highest-priority issue.Action needed: Apply
html.escape()to path values in app.py:309 and stats.py:94. This is an active stored XSS vector. No new activity since last triage.🤖 Triage Summary
Status: Confirmed — legitimate HIGH severity security issue.
Labels: bug, priority:high ✅
Assignee: akiba ✅
Next Steps
p["path"]f-string interpolation withhtml.escape(p["path"])inapp.pyline 309base_pathextraction instats.pyline 94Severity Assessment
This is a critical security finding. Stored XSS can compromise admin sessions on the stats dashboard. Recommend prioritizing this fix before deploying other changes.
Triage Assessment: HIGH severity
This is a critical stored XSS vulnerability. User-controlled URL paths are rendered unescaped in the /stats dashboard, allowing arbitrary script execution.
Labels: bug, priority:high ✅
Assignee: akiba ✅
Recommended next steps:
p["path"]in app.py line 309 before interpolationbase_pathin stats.py line 94 at storage time/<img src=x onerror=alert(1)>/anythingFIXED — Commit
122c408Fix: Added
import htmland replacedp["path"]withhtml.escape(p["path"])in the stats dashboard HTML template (app.py:390).Deployed via start.sh — workers restarted, Caddy reloaded.
Verification: Path values in /stats are now HTML-escaped, so
<img src=x onerror=alert(1)>renders as literal text instead of executing JS.