fix(security): remove innerHTML XSS sink in showAlert

This commit is contained in:
2026-07-08 13:51:45 +02:00
parent d8c947f88f
commit 284e685ed3
+23 -4
View File
@@ -160,7 +160,26 @@
* @param {string} type - Bootstrap alert type (success, danger, warning, info)
*/
showAlert(element, message, type = 'info') {
element.innerHTML = `<div class="alert alert-${type}" role="alert">${message}</div>`;
element.replaceChildren();
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.setAttribute('role', 'alert');
if (typeof message === 'string') {
alert.textContent = message;
} else if (message && typeof message === 'object') {
if (message.title) {
const strong = document.createElement('strong');
strong.textContent = message.title;
alert.appendChild(strong);
}
if (message.body) {
if (message.title) {
alert.appendChild(document.createTextNode(' '));
}
alert.appendChild(document.createTextNode(message.body));
}
}
element.appendChild(alert);
},
/**
@@ -465,7 +484,7 @@
ui.showUserInfo(user);
}
utils.showAlert(elements.secretResult, `<strong>Secret Phrase:</strong> ${cachedSecret}`, 'success');
utils.showAlert(elements.secretResult, { title: 'Secret Phrase:', body: cachedSecret }, 'success');
} else {
// Fetch fresh secret phrase and user info
setTimeout(ui.getSecretPhrase, 500);
@@ -581,7 +600,7 @@
localStorage.setItem('secret_phrase', data.secret_phrase);
localStorage.setItem('secret_last_fetch', Date.now().toString());
utils.showAlert(elements.secretResult, `<strong>Secret Phrase:</strong> ${data.secret_phrase}`, 'success');
utils.showAlert(elements.secretResult, { title: 'Secret Phrase:', body: data.secret_phrase }, 'success');
} else {
// Clear cached secret if user is no longer authorized
localStorage.removeItem('secret_phrase');
@@ -618,7 +637,7 @@
const price = data.bitcoin.chf;
utils.showAlert(
elements.btcPriceResult,
`<strong>1 Bitcoin (BTC) = CHF ${price.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}</strong>`,
{ title: `1 Bitcoin (BTC) = CHF ${price.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}` },
'success'
);
} else {