← back to Agentabrams Viewer
Fix self-XSS in domain search filter: escape filter + labels before innerHTML
a983e0c9e9928c8b82bf74169b4a9a865129ffab · 2026-07-25 10:14:07 -0700 · Steve Abrams
Files touched
Diff
commit a983e0c9e9928c8b82bf74169b4a9a865129ffab
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 25 10:14:07 2026 -0700
Fix self-XSS in domain search filter: escape filter + labels before innerHTML
---
public/index.html | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index fb83de1..aa6dfc4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -67,6 +67,9 @@ const list = document.getElementById('list'), q = document.getElementById('q');
const frame = document.getElementById('frame'), empty = document.getElementById('empty');
const cur = document.getElementById('cur'), count = document.getElementById('count');
+// Escape untrusted text before it touches innerHTML (search filter + domain labels).
+const esc = s => String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
+
function render() {
const f = q.value.trim().toLowerCase();
const shown = domains.filter(d => d.includes(f));
@@ -77,7 +80,8 @@ function render() {
const row = document.createElement('div');
row.className = 'item' + (d === current ? ' active' : '');
const dotCls = !s ? '' : s.authFail ? 'down' : s.gated ? 'auth' : s.up ? 'up' : 'down';
- const label = f ? d.replace(f, `<b>${f}</b>`) : d;
+ const ed = esc(d);
+ const label = f ? ed.replace(esc(f), m => `<b>${m}</b>`) : ed;
row.innerHTML = `<span class="dot ${dotCls}"></span><span class="name">${label}</span>` +
(s && s.up ? `<span class="ms">${s.ms}ms</span>` : s ? `<span class="ms">—</span>` : '');
row.onclick = () => select(d);
@@ -88,7 +92,7 @@ function render() {
function select(d) {
current = d;
localStorage.setItem('aav-current', d);
- cur.innerHTML = `<a href="https://${d}" target="_blank" rel="noopener noreferrer">${d}</a>`;
+ cur.innerHTML = `<a href="https://${esc(d)}" target="_blank" rel="noopener noreferrer">${esc(d)}</a>`;
// Load through the server-side proxy — it injects the unified admin credential
// and strips X-Frame-Options, so every site renders in-panel without prompting.
empty.style.display = 'none';
← 1eb9bf1 viewer: follow redirects + detect login-form/SSO gating, not
·
back to Agentabrams Viewer
·
viewer UI: 3-way status dots (Basic/login/open) + per-dot to 814faa8 →