From c80c23cd08334a33e1534a1e32b13a490969c873 Mon Sep 17 00:00:00 2001 From: Chuck Date: Thu, 8 Jan 2026 12:27:41 -0500 Subject: [PATCH] fix(security): Fix XSS vulnerability in handleCustomFeedLogoUpload Replace innerHTML usage with safe DOM manipulation using createElement and setAttribute to prevent XSS when injecting uploadedFile.path and uploadedFile.id values. - Clear logoCell using textContent instead of innerHTML - Create all DOM elements using document.createElement - Set uploadedFile.path and uploadedFile.id via setAttribute (automatically escaped) - Properly structure DOM tree by appending elements in order - Prevents malicious HTML/script injection through file path or ID values --- web_interface/templates/v3/base.html | 70 +++++++++++++++++++++------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/web_interface/templates/v3/base.html b/web_interface/templates/v3/base.html index 72a02d5e..4263a091 100644 --- a/web_interface/templates/v3/base.html +++ b/web_interface/templates/v3/base.html @@ -4949,23 +4949,59 @@ const pathName = existingPathInput ? existingPathInput.name : `${fullKey}.${index}.logo.path`; const idName = existingIdInput ? existingIdInput.name : `${fullKey}.${index}.logo.id`; - logoCell.innerHTML = ` -
- - - Logo - - -
- `; + // Clear logoCell and build DOM safely to prevent XSS + logoCell.textContent = ''; // Clear existing content + + // Create container div + const container = document.createElement('div'); + container.className = 'flex items-center space-x-2'; + + // Create file input + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.id = `${fieldId}_logo_${index}`; + fileInput.accept = 'image/png,image/jpeg,image/bmp'; + fileInput.style.display = 'none'; + fileInput.setAttribute('onchange', `handleCustomFeedLogoUpload(event, '${fieldId}', ${index}, '${pluginId}', '${fullKey}')`); + + // Create upload button + const uploadButton = document.createElement('button'); + uploadButton.type = 'button'; + uploadButton.className = 'px-2 py-1 text-xs bg-gray-200 hover:bg-gray-300 rounded'; + uploadButton.setAttribute('onclick', `document.getElementById('${fieldId}_logo_${index}').click()`); + const uploadIcon = document.createElement('i'); + uploadIcon.className = 'fas fa-upload mr-1'; + uploadButton.appendChild(uploadIcon); + uploadButton.appendChild(document.createTextNode(' Upload')); + + // Create img element - set src via setAttribute to prevent XSS + const img = document.createElement('img'); + img.setAttribute('src', `/${uploadedFile.path}`); + img.setAttribute('alt', 'Logo'); + img.className = 'w-8 h-8 object-cover rounded border'; + img.id = `${fieldId}_logo_preview_${index}`; + + // Create hidden input for path - set value via setAttribute to prevent XSS + const pathInput = document.createElement('input'); + pathInput.type = 'hidden'; + pathInput.setAttribute('name', pathName); + pathInput.setAttribute('value', uploadedFile.path); + + // Create hidden input for id - set value via setAttribute to prevent XSS + const idInput = document.createElement('input'); + idInput.type = 'hidden'; + idInput.setAttribute('name', idName); + idInput.setAttribute('value', String(uploadedFile.id)); // Ensure it's a string + + // Append all elements to container + container.appendChild(fileInput); + container.appendChild(uploadButton); + container.appendChild(img); + container.appendChild(pathInput); + container.appendChild(idInput); + + // Append container to logoCell + logoCell.appendChild(container); } } else { alert('Upload failed: ' + (data.message || 'Unknown error'));