From 52d835cbaddbea11787c36fd4514bf4189b4eec5 Mon Sep 17 00:00:00 2001 From: Chuck Date: Thu, 8 Jan 2026 15:33:04 -0500 Subject: [PATCH] fix: Add HTML escaping to prevent XSS in fallback code and checkbox-group Add proper HTML escaping for schema-derived values to prevent XSS vulnerabilities in fallback rendering code and checkbox-group widget. Problem: - Fallback code in generateFieldHtml (line 3094) doesn't escape propLabel when building HTML strings, while main renderArrayObjectItem uses escapeHtml() - Checkbox-group widget (lines 3012-3025) doesn't escape option or label values - While risk is limited (values come from plugin schemas), malicious plugin schemas or untrusted schema sources could inject XSS - Inconsistent with main renderArrayObjectItem which properly escapes Solution: - Added escapeHtml() calls for propLabel in fallback array-of-objects rendering (both locations: generateFieldHtml and addArrayObjectItem fallback) - Added escapeHtml() calls for option values in checkbox-group widget: - checkboxId (contains option) - data-option-value attribute - value attribute - label text in span - Ensures consistent XSS protection across all rendering paths This prevents potential XSS if plugin schemas contain malicious HTML/script content in enum values or property titles. --- web_interface/static/v3/plugins_manager.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/web_interface/static/v3/plugins_manager.js b/web_interface/static/v3/plugins_manager.js index 4c1c1c2d..2f13ac25 100644 --- a/web_interface/static/v3/plugins_manager.js +++ b/web_interface/static/v3/plugins_manager.js @@ -3012,18 +3012,18 @@ function generateFieldHtml(key, prop, value, prefix = '') { enumItems.forEach((option) => { const isChecked = arrayValue.includes(option); const label = labels[option] || option.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); - const checkboxId = `${fieldId}_${option}`; + const checkboxId = `${fieldId}_${escapeHtml(option)}`; html += ` `; }); @@ -3092,7 +3092,7 @@ function generateFieldHtml(key, prop, value, prefix = '') { const propSchema = itemProperties[propKey]; const propValue = item[propKey] !== undefined ? item[propKey] : propSchema.default; const propLabel = propSchema.title || propKey.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); - html += `
`; + html += `
`; if (propSchema.type === 'boolean') { const checked = propValue ? 'checked' : ''; html += ``; @@ -6497,7 +6497,7 @@ if (typeof window !== 'undefined') { const propSchema = itemsSchema.properties[propKey]; const propValue = newItem[propKey] !== undefined ? newItem[propKey] : propSchema.default; const propLabel = propSchema.title || propKey.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); - itemHtml += `
`; + itemHtml += `
`; if (propSchema.type === 'boolean') { const checked = propValue ? 'checked' : ''; // No name attribute - rely solely on _data field to prevent key leakage