Skip to content

Commit 32c104c

Browse files
committed
Fix #642 Rewriting refresh method to avoid vulnerable string concatenation.
1 parent 4ecbffc commit 32c104c

File tree

1 file changed

+56
-65
lines changed

1 file changed

+56
-65
lines changed

src/jquery.multiselect.js

Lines changed: 56 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -128,97 +128,88 @@
128128
},
129129

130130
refresh: function(init) {
131+
var inputIdCounter = 0;
131132
var el = this.element;
132133
var o = this.options;
133134
var menu = this.menu;
134135
var checkboxContainer = this.checkboxContainer;
135-
var optgroups = {};
136136
var html = "";
137+
var $dropdown = $("<ul/>").addClass('ui-multiselect-checkboxes ui-helper-reset');
137138
var id = el.attr('id') || multiselectID++; // unique ID for the label & option tags
138139

139-
// update header link container visibility if needed
140-
if (this.options.header) {
141-
if(!this.options.multiple) {
142-
this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide();
143-
} else {
144-
this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').show();
145-
}
146-
}
147-
148-
// build items
149-
el.find('option').each(function(i) {
150-
var $this = $(this);
151-
var parent = this.parentNode;
152-
var contents = this.innerHTML;
153-
var title = this.title;
154-
var value = this.value;
155-
var inputID = 'ui-multiselect-' + multiselectID + '-' + (this.id || id + '-option-' + i);
156-
var isDisabled = this.disabled;
157-
var isSelected = this.selected;
140+
function makeItem(option, isInOptionGroup) {
141+
var title = option.title ? option.title : null;
142+
var value = option.value;
143+
var inputID = 'ui-multiselect-' + multiselectID + '-' + (option.id || id + '-option-' + inputIdCounter++);
144+
var isDisabled = option.disabled;
145+
var isSelected = option.selected;
158146
var labelClasses = [ 'ui-corner-all' ];
159147
var liClasses = [];
160-
var optLabel;
161148

162149
if(isDisabled) {
163150
liClasses.push('ui-multiselect-disabled');
151+
labelClasses.push('ui-state-disabled');
164152
}
165-
166-
if(this.className) {
167-
liClasses.push(this.className);
153+
if(option.className) {
154+
liClasses.push(option.className);
168155
}
169-
170-
// is this an optgroup?
171-
if(parent.tagName === 'OPTGROUP') {
172-
optLabel = parent.getAttribute('label');
156+
if(isSelected && !o.multiple) {
157+
labelClasses.push('ui-state-active');
158+
}
159+
if(isInOptionGroup) {
173160
liClasses.push('ui-multiselect-optgrp-child');
174-
175-
// has this optgroup been added already?
176-
if(!optgroups[optLabel]) {
177-
var optLabelEscaped = optLabel.replace(/&/g, '&amp;')
178-
.replace(/>/g, '&gt;')
179-
.replace(/</g, '&lt;')
180-
.replace(/'/g, '&#39;')
181-
.replace(/\//g, '&#x2F;')
182-
.replace(/"/g, '&quot;');
183-
html += '<li class="ui-multiselect-optgroup-label ' + parent.className + '"><a href="#">' + optLabelEscaped + '</a></li>';
184-
optgroups[optLabel] = true;
185-
}
186161
}
187162

188-
if(isDisabled) {
189-
labelClasses.push('ui-state-disabled');
190-
}
163+
var $item = $("<li/>").addClass(liClasses.join(' '));
164+
var $label = $("<label/>").attr({
165+
"for": inputID,
166+
"title": title
167+
}).addClass(labelClasses.join(' ')).appendTo($item);
168+
var $input = $("<input/>").attr({
169+
"name": "multiselect_" + id,
170+
"type": o.multiple ? "checkbox" : "radio",
171+
"value": value,
172+
"title": title,
173+
"id": inputID,
174+
"checked": isSelected ? "checked" : null,
175+
"aria-selected": isSelected ? "true" : null,
176+
"disabled": isDisabled ? "disabled" : null,
177+
"aria-disabled": isDisabled ? "true" : null
178+
}).appendTo($label);
179+
180+
$("<span/>").text($(option).text()).appendTo($label);
181+
182+
return $item;
183+
}//makeItem
191184

192-
// browsers automatically select the first option
193-
// by default with single selects
194-
if(isSelected && !o.multiple) {
195-
labelClasses.push('ui-state-active');
185+
// update header link container visibility if needed
186+
if (this.options.header) {
187+
if(!this.options.multiple) {
188+
this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide();
189+
} else {
190+
this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').show();
196191
}
192+
}
197193

198-
html += '<li class="' + liClasses.join(' ') + '">';
199-
200-
// create the label
201-
html += '<label for="' + inputID + '" title="' + title + '" class="' + labelClasses.join(' ') + '">';
202-
html += '<input id="' + inputID + '" name="multiselect_' + id + '" type="' + (o.multiple ? "checkbox" : "radio") + '" value="' + value + '" title="' + title + '"';
194+
//Turn all the options and optiongroups into list items
195+
el.children().each(function(i) {
196+
var $this = $(this);
203197

204-
// pre-selected?
205-
if(isSelected) {
206-
html += ' checked="checked"';
207-
html += ' aria-selected="true"';
208-
}
198+
if(this.tagName === 'OPTGROUP') {
199+
var $groupLabel = $("<li/>").addClass('ui-multiselect-optgroup-label ' + this.className).appendTo($dropdown);
200+
var $link = $("<a/>").attr("href", "#").text(this.getAttribute('label')).appendTo($groupLabel);
209201

210-
// disabled?
211-
if(isDisabled) {
212-
html += ' disabled="disabled"';
213-
html += ' aria-disabled="true"';
202+
$this.children().each(function() {
203+
var $listItem = makeItem(this, true).appendTo($dropdown);
204+
});
205+
} else {
206+
var $listItem = makeItem(this).appendTo($dropdown);
214207
}
215208

216-
// add the contents and close everything off
217-
html += ' /><span>' + contents + '</span></label></li>';
218209
});
219210

220-
// insert into the DOM
221-
checkboxContainer.html(html);
211+
this.menu.find(".ui-multiselect-checkboxes").remove();
212+
this.menu.append($dropdown);
222213

223214
// cache some moar useful elements
224215
this.labels = menu.find('label');

0 commit comments

Comments
 (0)