diff --git a/pkg/service/handlers/web/js/script.js b/pkg/service/handlers/web/js/script.js index 09f37c7..05fc7ca 100644 --- a/pkg/service/handlers/web/js/script.js +++ b/pkg/service/handlers/web/js/script.js @@ -1947,7 +1947,8 @@ async function reboot(deviceId, ip) { const statusDiv = document.getElementById("status"); statusDiv.style.display = "block"; statusDiv.style.backgroundColor = "#ffffcc"; - statusDiv.innerHTML = "Rebooting " + display + " via " + rebootMethod + "..."; + // textContent avoids reinterpreting the (user-controlled) device name as HTML. + statusDiv.textContent = "Rebooting " + display + " via " + rebootMethod + "..."; try { const url = "/setup/reboot/" + encodeURIComponent(deviceId) @@ -1993,19 +1994,22 @@ async function loadAccountIDSuggestions(deviceId) { // Reset existingSelect.innerHTML = ""; - (data.known || []).forEach((id) => { + (data.known || []).forEach((/** @type {string} */ id) => { const opt = document.createElement("option"); - opt.value = id; - opt.textContent = id; + opt.value = String(id); + opt.textContent = String(id); existingSelect.appendChild(opt); }); if (data.current) { currentP.style.display = "block"; - currentP.innerHTML = - "Speaker is already paired with account " - + "" + data.current + "" - + ". You can keep it (recommended) or re-pair to a different ID."; + // Build the paragraph with createElement so the user-controlled + // account ID never becomes HTML. + currentP.replaceChildren( + document.createTextNode("Speaker is already paired with account "), + Object.assign(document.createElement("strong"), {textContent: data.current}), + document.createTextNode(". You can keep it (recommended) or re-pair to a different ID."), + ); input.value = data.current; freshDiv.style.display = "block"; } else {