fix(projects): prevent JS injection via bin names in onclick handlers

binCard() was building onclick="renameBinPrompt('id', 'NAME')" by
calling esc() then .replace(/'/g, "\\'").  The problem: esc() converts
' to ', so the replace never fires on raw single quotes.  When the
HTML parser evaluates the attribute it decodes ' back to ', breaking
the JS string — and for injected payloads like `'; alert(1)//` this is
stored XSS.

Fix: use JSON.stringify(b.name) to produce a properly-escaped double-
quoted JS string literal, then esc() to HTML-encode the surrounding
double-quotes for safe embedding in the HTML attribute.
This commit is contained in:
Zac Gaetano 2026-05-19 00:09:49 -04:00
parent 58e2e539f8
commit 1e8cde81be

View file

@ -480,6 +480,9 @@
}
function binCard(b) {
// Use JSON.stringify + esc so the bin name is safe in an onclick JS string
// regardless of quotes, backslashes, or other special characters it may contain.
const nameJs = esc(JSON.stringify(b.name));
return '<div class="proj-bin-card">' +
'<div class="proj-bin-card-name">' +
'<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" width="14" height="14"><path d="M1 4a1 1 0 0 1 1-1h4l2 2h5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1z"/></svg>' +
@ -487,7 +490,7 @@
'</div>' +
'<div class="proj-bin-card-meta">Created ' + new Date(b.created_at).toLocaleDateString() + '</div>' +
'<div class="proj-bin-card-actions">' +
'<button class="btn btn-ghost btn-sm" style="padding:4px 8px" onclick="renameBinPrompt(\'' + b.id + '\', \'' + esc(b.name).replace(/'/g, "\\'") + '\')">Rename</button>' +
'<button class="btn btn-ghost btn-sm" style="padding:4px 8px" onclick="renameBinPrompt(\'' + b.id + '\', ' + nameJs + ')">Rename</button>' +
'<button class="btn btn-ghost btn-sm" style="padding:4px 8px;color:var(--status-red)" onclick="deleteBin(\'' + b.id + '\')">Delete</button>' +
'</div>' +
'</div>';