Sweep every onclick built from data: an unescaped quote silently killed nine handlers

This commit is contained in:
Gmer4Lfe
2026-08-06 22:13:10 -04:00
parent d135dff5cd
commit d5944c2443
2 changed files with 25 additions and 13 deletions
+16 -4
View File
@@ -591,6 +591,18 @@ function _dur(s) {
return sec + 's';
}
// Escapes text going INSIDE a double-quoted HTML attribute. A bare " there ends the attribute
// early and silently destroys everything after it — an onclick built by string concatenation
// becomes a syntax error and the element simply stops responding, with nothing logged.
//
// Duplicated from scheduler.php rather than shared: js/varaverk.js is loaded after the page
// partials, so a page cannot rely on it during its own setup. Four lines in two places beats a
// load-order bug that only shows up on a slow load.
function vvRyEscAttr(s) {
return String(s).replace(/&/g,'&').replace(/"/g,'"')
.replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
// ── Status + active card ──────────────────────────────────────────────────────
function _statusCard(data) {
const en = data.enabled;
@@ -1207,14 +1219,14 @@ function vvRyEBrowse(key) {
let h = '';
if (d.parent) {
const pn = d.parent === '/' ? '/' : (d.parent.replace(/^.*\//,'') || d.parent) + '/';
h += `<div class="vv-ms-dir parent" onclick="vvRyENavTo('${key}',${JSON.stringify(d.parent)})">↑ ${pn}</div>`;
h += `<div class="vv-ms-dir parent" onclick="vvRyENavTo('${key}',${vvRyEscAttr(JSON.stringify(d.parent))})">↑ ${pn}</div>`;
}
if (!d.dirs.length) {
h += '<div class="vv-ms-dir" style="color:#2a2a2a;cursor:default;">— empty —</div>';
} else {
for (const dir of d.dirs) {
const name = dir.replace(/^.*\//,'') || dir;
h += `<div class="vv-ms-dir" onclick="vvRyENavTo('${key}',${JSON.stringify(dir)})" title="${dir}">▶ ${name}</div>`;
h += `<div class="vv-ms-dir" onclick="vvRyENavTo('${key}',${vvRyEscAttr(JSON.stringify(dir))})" title="${vvRyEscAttr(dir)}">▶ ${name}</div>`;
}
}
dirsEl.innerHTML = h;
@@ -1653,7 +1665,7 @@ function _vvMsRenderDirs(dirsElId, dirs, parent, navFn) {
if (parent !== null) {
const pname = parent === '/' ? '/' : (parent.replace(/^.*\//, '') || parent) + '/';
html += `<div class="vv-ms-dir parent" onclick="${navFn}(${JSON.stringify(parent)})">↑ ${pname}</div>`;
html += `<div class="vv-ms-dir parent" onclick="${navFn}(${vvRyEscAttr(JSON.stringify(parent))})">↑ ${pname}</div>`;
}
if (!dirs.length) {
@@ -1661,7 +1673,7 @@ function _vvMsRenderDirs(dirsElId, dirs, parent, navFn) {
} else {
for (const d of dirs) {
const name = d.replace(/^.*\//, '') || d;
html += `<div class="vv-ms-dir" onclick="${navFn}(${JSON.stringify(d)})" title="${d}">▶ ${name}</div>`;
html += `<div class="vv-ms-dir" onclick="${navFn}(${vvRyEscAttr(JSON.stringify(d))})" title="${vvRyEscAttr(d)}">▶ ${name}</div>`;
}
}