From 9b7c626062667a354169ce99a7a38d5f1a798b34 Mon Sep 17 00:00:00 2001 From: Pan Luo Date: Fri, 10 Jul 2026 21:43:01 -0700 Subject: [PATCH] Build the library browser alert toast with DOM methods The alertToast helper in the library browser interpolated its title and msg arguments directly into an innerHTML template, letting DOM text be reinterpreted as HTML (CodeQL js/xss-through-dom). Build the toast with document.createElement instead and assign the title and message with textContent, so caller-supplied strings are always treated as text. This matches the toast construction already used in htdocs/js/TagWidget. --- htdocs/js/SetMaker/setmaker.js | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/htdocs/js/SetMaker/setmaker.js b/htdocs/js/SetMaker/setmaker.js index 566774c053..8987d2e958 100644 --- a/htdocs/js/SetMaker/setmaker.js +++ b/htdocs/js/SetMaker/setmaker.js @@ -19,16 +19,37 @@ 'p-3' ); toastContainer.style.zIndex = 20; - toastContainer.innerHTML = - ''; + + const toast = document.createElement('div'); + toast.classList.add('toast', 'bg-white'); + toast.setAttribute('role', 'alert'); + toast.setAttribute('aria-live', 'assertive'); + toast.setAttribute('aria-atomic', 'true'); + toastContainer.append(toast); + + const toastHeader = document.createElement('div'); + toastHeader.classList.add('toast-header'); + toast.append(toastHeader); + + const toastTitle = document.createElement('strong'); + toastTitle.classList.add('me-auto'); + toastTitle.textContent = title; + + const closeButton = document.createElement('button'); + closeButton.classList.add('btn-close'); + closeButton.type = 'button'; + closeButton.dataset.bsDismiss = 'toast'; + closeButton.setAttribute('aria-label', 'close'); + + toastHeader.append(toastTitle, closeButton); + + const toastBody = document.createElement('div'); + toastBody.classList.add('toast-body', 'alert', good ? 'alert-success' : 'alert-danger', 'mb-0', 'text-center'); + toastBody.textContent = msg; + toast.append(toastBody); + document.body.prepend(toastContainer); - const bsToast = new bootstrap.Toast(toastContainer.firstElementChild); + const bsToast = new bootstrap.Toast(toast); toastContainer.addEventListener('hidden.bs.toast', () => { bsToast.dispose(); toastContainer.remove();