mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-11 05:13:01 +00:00
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.
This commit is contained in:
@@ -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 += `
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="${checkboxId}"
|
||||
data-checkbox-group="${fieldId}"
|
||||
data-option-value="${option}"
|
||||
value="${option}"
|
||||
data-option-value="${escapeHtml(option)}"
|
||||
value="${escapeHtml(option)}"
|
||||
${isChecked ? 'checked' : ''}
|
||||
onchange="updateCheckboxGroupData('${fieldId}')"
|
||||
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||||
<span class="ml-2 text-sm text-gray-700">${label}</span>
|
||||
<span class="ml-2 text-sm text-gray-700">${escapeHtml(label)}</span>
|
||||
</label>
|
||||
`;
|
||||
});
|
||||
@@ -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 += `<div class="mb-3"><label class="block text-sm font-medium text-gray-700 mb-1">${propLabel}</label>`;
|
||||
html += `<div class="mb-3"><label class="block text-sm font-medium text-gray-700 mb-1">${escapeHtml(propLabel)}</label>`;
|
||||
if (propSchema.type === 'boolean') {
|
||||
const checked = propValue ? 'checked' : '';
|
||||
html += `<input type="checkbox" data-prop-key="${propKey}" ${checked} class="h-4 w-4 text-blue-600" onchange="window.updateArrayObjectData('${fieldId}')">`;
|
||||
@@ -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 += `<div class="mb-3"><label class="block text-sm font-medium text-gray-700 mb-1">${propLabel}</label>`;
|
||||
itemHtml += `<div class="mb-3"><label class="block text-sm font-medium text-gray-700 mb-1">${escapeHtml(propLabel)}</label>`;
|
||||
if (propSchema.type === 'boolean') {
|
||||
const checked = propValue ? 'checked' : '';
|
||||
// No name attribute - rely solely on _data field to prevent key leakage
|
||||
|
||||
Reference in New Issue
Block a user