Skip to content

Commit 3aae4cc

Browse files
committed
fixes vulnerabilities in materializecss#38
1 parent a8a30a3 commit 3aae4cc

File tree

9 files changed

+440
-57
lines changed

9 files changed

+440
-57
lines changed

jade/page-contents/autocomplete_content.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
<div id="initialization" class="scrollspy section">
4040
<h3 class="header">Initialization</h3>
4141
<p>The data is a json object where the key is the matching string and the value is an optional image url.</p>
42+
<p>The key must be a text string. If you trust your data, or have properly sanitized your user input, you may
43+
use HTML by setting the option <code class="language-javascript">allowUnsafeHTML: true</code>.</p>
4244
<pre><code class="language-javascript">
4345
document.addEventListener('DOMContentLoaded', function() {
4446
var elems = document.querySelectorAll('.autocomplete');
@@ -112,6 +114,12 @@ <h3 class="header">Options</h3>
112114
<td></td>
113115
<td>Sort function that defines the order of the list of autocomplete options.</td>
114116
</tr>
117+
<tr>
118+
<td>allowUnsafeHTML</td>
119+
<td>Boolean</td>
120+
<td>false</td>
121+
<td>If true will render the key from each item directly as HTML. User input MUST be properly sanitized first.</td>
122+
</tr>
115123
</tbody>
116124
</table>
117125

jade/page-contents/toasts_content.html

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44

55
<div id="introduction" class="section scrollspy">
66
<p>Materialize provides an easy way for you to send unobtrusive alerts to your users through toasts. These toasts are also placed and sized responsively, try it out by clicking the button below on different device sizes.</p>
7-
<a class="waves-effect waves-light btn" onclick="M.toast({html: 'I am a toast'})">Toast!</a>
7+
<a class="waves-effect waves-light btn" onclick="M.toast({text: 'I am a toast'})">Toast!</a>
88
<p>To do this, call the M.toast() function programmatically in JavaScript.</p>
99
<pre><code class="language-javascript">
10-
M.toast({html: 'I am a toast!'})
10+
M.toast({text: 'I am a toast!'})
1111
</code></pre>
1212
<p>One way to add this into your application is to add this as an onclick event to a button.</p>
1313
<pre><code class="language-markup">
14-
&lt;a onclick="M.toast({html: 'I am a toast'})" class="btn">Toast!&lt;/a>
14+
&lt;a onclick="M.toast({text: 'I am a toast'})" class="btn">Toast!&lt;/a>
1515
</code></pre>
1616
</div>
1717

@@ -30,11 +30,37 @@ <h3 class="header">Options</h3>
3030
</thead>
3131

3232
<tbody>
33+
<tr>
34+
<td>text</td>
35+
<td>String</td>
36+
<td>''</td>
37+
<td>The content of the Toast.</td>
38+
</tr>
39+
<tr>
40+
<td>unsafeHTML</td>
41+
<td>String, HTMLElement</td>
42+
<td>''</td>
43+
<td>
44+
HTML content that will be appended to to <code class="language-javascript">text</code>.
45+
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">unsafeHTML</code>.
46+
</td>
47+
</tr>
3348
<tr>
3449
<td>html</td>
3550
<td>String</td>
3651
<td>''</td>
37-
<td>The HTML content of the Toast.</td>
52+
<td>
53+
<p>
54+
(DEPRECATED): will be removed in a later release.
55+
</p>
56+
<p>
57+
HTML content that will be appended to <code class="language-javascript">text</code>.
58+
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">html</code>.
59+
</p>
60+
<p>
61+
Will be ignored if <code class="language-javascript">unsafeHTML</code> is set.
62+
</p>
63+
</td>
3864
</tr>
3965
<tr>
4066
<td>displayLength</td>
@@ -117,21 +143,26 @@ <h3 class="header">Properties</h3>
117143

118144
<div id="custom-html" class="section scrollspy">
119145
<h3 class="header">Custom HTML</h3>
120-
<p>You can pass in an HTML String as the first argument as well. Take a look at the example below, where we pass in text as well as a flat button. If you call an external function instead of in-line JavaScript, you will not need to escape quotation marks. </p>
146+
<p>You can pass in an HTML String as the first argument as well. Take a look at the example below, where we pass
147+
in text as well as a flat button. If you call an external function instead of in-line JavaScript, you will not
148+
need to escape quotation marks. </p>
149+
<p>
150+
Only use a properly sanitized or otherwise trusted HTML string.
151+
</p>
121152
<a class="waves-effect waves-light btn" onclick="displayCustomHTMLToast()">Toast with Action</a>
122153
<pre><code class="language-javascript">
123154
var toastHTML = '&lt;span>I am toast content&lt;/span>&lt;button class="btn-flat toast-action">Undo&lt;/button>';
124-
M.toast({html: toastHTML});
155+
M.toast({unsafeHTML: toastHTML});
125156
</code></pre>
126157
</div>
127158

128159

129160
<div id="callback" class="scrollspy section">
130161
<h3 class="header">Callback</h3>
131162
<p>You can have the toast callback a function when it has been dismissed.</p>
132-
<a class="btn" onclick="M.toast({html: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!</a>
163+
<a class="btn" onclick="M.toast({text: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!</a>
133164
<pre><code class="language-markup">
134-
&lt;a class="btn" onclick="M.toast({html: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!&lt;/a>
165+
&lt;a class="btn" onclick="M.toast({text: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!&lt;/a>
135166
</code></pre>
136167
</div>
137168

@@ -140,11 +171,11 @@ <h3 class="header">Callback</h3>
140171
<h3 class="header">Styling Toasts</h3>
141172
<p>We've added the ability to customize your toasts easily. You can pass in classes as an optional parameter into the toast function. We've added a rounded class for you, but you can create your own CSS classes and apply them to toasts. Checkout out our full example below.</p>
142173

143-
<a class="waves-effect waves-light btn" onclick="M.toast({html: 'I am a toast!', classes: 'rounded'})">Round Toast!</a>
174+
<a class="waves-effect waves-light btn" onclick="M.toast({text: 'I am a toast!', classes: 'rounded'})">Round Toast!</a>
144175

145176
<pre><code class="language-javascript">
146177
// 'rounded' is the class I'm applying to the toast
147-
M.toast({html: 'I am a toast!', classes: 'rounded'});
178+
M.toast({text: 'I am a toast!', classes: 'rounded'});
148179
</code></pre>
149180
</div>
150181

jade/page-contents/tooltips_content.html

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,35 @@ <h3 class="header">Options</h3>
7070
<td>0</td>
7171
<td>Delay time before tooltip appears.</td>
7272
</tr>
73+
<tr>
74+
<td>text</td>
75+
<td>String</td>
76+
<td></td>
77+
<td>Text string for the tooltip.</td>
78+
</tr>
79+
<tr>
80+
<td>unsafeHTML</td>
81+
<td>String</td>
82+
<td>null</td>
83+
<td>HTML content that will be appended to to <code class="language-javascript">text</code>.
84+
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">unsafeHTML</code>.</td>
85+
</tr>
7386
<tr>
7487
<td>html</td>
7588
<td>String</td>
7689
<td>null</td>
77-
<td>Can take regular text or HTML strings.</td>
90+
<td>
91+
<p>
92+
(DEPRECATED): will be removed in a later release.
93+
</p>
94+
<p>
95+
HTML content that will be appended to <code class="language-javascript">text</code>.
96+
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">html</code>.
97+
</p>
98+
<p>
99+
Will be ignored if <code class="language-javascript">unsafeHTML</code> is set.
100+
</p>
101+
</td>
78102
</tr>
79103
<tr>
80104
<td>margin</td>

js/autocomplete.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
sortFunction: function(a, b, inputString) {
1010
// Sort function for sorting autocomplete results
1111
return a.indexOf(inputString) - b.indexOf(inputString);
12-
}
12+
},
13+
allowUnsafeHTML: false
1314
};
1415

1516
/**
@@ -282,22 +283,14 @@
282283
/**
283284
* Highlight partial match
284285
*/
285-
_highlight(string, $el) {
286-
let img = $el.find('img');
287-
let matchStart = $el
288-
.text()
289-
.toLowerCase()
290-
.indexOf('' + string.toLowerCase() + ''),
291-
matchEnd = matchStart + string.length - 1,
292-
beforeMatch = $el.text().slice(0, matchStart),
293-
matchText = $el.text().slice(matchStart, matchEnd + 1),
294-
afterMatch = $el.text().slice(matchEnd + 1);
295-
$el.html(
296-
`<span>${beforeMatch}<span class='highlight'>${matchText}</span>${afterMatch}</span>`
297-
);
298-
if (img.length) {
299-
$el.prepend(img);
300-
}
286+
_highlight(input, label) {
287+
const start = label.toLowerCase().indexOf('' + input.toLowerCase() + '');
288+
const end = start + input.length - 1;
289+
//custom filters may return results where the string does not match any part
290+
if (start == -1 || end == -1) {
291+
return [label, '', ''];
292+
}
293+
return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
301294
}
302295

303296
/**
@@ -376,18 +369,32 @@
376369

377370
// Render
378371
for (let i = 0; i < matchingData.length; i++) {
379-
let entry = matchingData[i];
380-
let $autocompleteOption = $('<li></li>');
372+
const entry = matchingData[i];
373+
const item = document.createElement('li');
381374
if (!!entry.data) {
382-
$autocompleteOption.append(
383-
`<img src="${entry.data}" class="right circle"><span>${entry.key}</span>`
384-
);
375+
const img = document.createElement('img');
376+
img.classList.add("right", "circle");
377+
img.src = entry.data;
378+
item.appendChild(img);
379+
}
380+
381+
const parts = this._highlight(val, entry.key);
382+
const s = document.createElement('span');
383+
if (this.options.allowUnsafeHTML) {
384+
s.innerHTML = parts[0] + '<span class="highlight">' + parts[1] + '</span>' + parts[2];
385385
} else {
386-
$autocompleteOption.append('<span>' + entry.key + '</span>');
386+
s.appendChild(document.createTextNode(parts[0]))
387+
if (!!parts[1]){
388+
const highlight = document.createElement('span');
389+
highlight.textContent = parts[1];
390+
highlight.classList.add("highlight");
391+
s.appendChild(highlight);
392+
s.appendChild(document.createTextNode(parts[2]));
393+
}
387394
}
395+
item.appendChild(s);
388396

389-
$(this.container).append($autocompleteOption);
390-
this._highlight(val, $autocompleteOption);
397+
$(this.container).append(item);
391398
}
392399
}
393400

js/toasts.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
let _defaults = {
55
html: '',
6+
unsafeHTML: '',
7+
text: '',
68
displayLength: 4000,
79
inDuration: 300,
810
outDuration: 375,
@@ -18,7 +20,12 @@
1820
* @member Toast#options
1921
*/
2022
this.options = $.extend({}, Toast.defaults, options);
21-
this.message = this.options.html;
23+
this.htmlMessage = this.options.html;
24+
// If the new unsafeHTML is used, prefer that
25+
if (!!this.options.unsafeHTML){
26+
this.htmlMessage = this.options.unsafeHTML;
27+
}
28+
this.message = this.options.text;
2229

2330
/**
2431
* Describes current pan state toast
@@ -188,28 +195,26 @@
188195
$(toast).addClass(this.options.classes);
189196
}
190197

191-
// Set content
198+
// Set safe text content
199+
toast.textContent = this.message;
192200
if (
193201
typeof HTMLElement === 'object'
194-
? this.message instanceof HTMLElement
195-
: this.message &&
196-
typeof this.message === 'object' &&
197-
this.message !== null &&
198-
this.message.nodeType === 1 &&
199-
typeof this.message.nodeName === 'string'
200-
) {
201-
toast.appendChild(this.message);
202-
203-
// Check if it is jQuery object
204-
} else if (!!this.message.jquery) {
205-
$(toast).append(this.message[0]);
206-
207-
// Insert as html;
202+
? this.htmlMessage instanceof HTMLElement
203+
: this.htmlMessage &&
204+
typeof this.htmlMessage === 'object' &&
205+
this.htmlMessage !== null &&
206+
this.htmlMessage.nodeType === 1 &&
207+
typeof this.htmlMessage.nodeName === 'string'
208+
) { //if the htmlMessage is an HTML node, append it directly
209+
toast.appendChild(this.htmlMessage);
210+
} else if (!!this.htmlMessage.jquery) { // Check if it is jQuery object, append the node
211+
$(toast).append(this.htmlMessage[0]);
208212
} else {
209-
toast.innerHTML = this.message;
213+
// Append as unsanitized html;
214+
$(toast).append(this.htmlMessage);
210215
}
211216

212-
// Append toasft
217+
// Append toast
213218
Toast._container.appendChild(toast);
214219
return toast;
215220
}

js/tooltip.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
exitDelay: 200,
66
enterDelay: 0,
77
html: null,
8+
text: '',
9+
unsafeHTML: null,
810
margin: 5,
911
inDuration: 250,
1012
outDuration: 200,
@@ -68,13 +70,24 @@
6870

6971
let tooltipContentEl = document.createElement('div');
7072
tooltipContentEl.classList.add('tooltip-content');
71-
tooltipContentEl.innerHTML = this.options.html;
73+
this._setTooltipContent(tooltipContentEl);
74+
7275
tooltipEl.appendChild(tooltipContentEl);
7376
document.body.appendChild(tooltipEl);
7477
}
7578

79+
_setTooltipContent(tooltipContentEl) {
80+
tooltipContentEl.textContent = this.options.text;
81+
if (!!this.options.html){
82+
$(tooltipContentEl).append(this.options.html);
83+
}
84+
if (!!this.options.unsafeHTML){
85+
$(tooltipContentEl).append(this.options.unsafeHTML);
86+
}
87+
}
88+
7689
_updateTooltipContent() {
77-
this.tooltipEl.querySelector('.tooltip-content').innerHTML = this.options.html;
90+
this._setTooltipContent(this.tooltipEl.querySelector('.tooltip-content'));
7891
}
7992

8093
_setupEventHandlers() {
@@ -285,7 +298,7 @@
285298
let positionOption = this.el.getAttribute('data-position');
286299

287300
if (tooltipTextOption) {
288-
attributeOptions.html = tooltipTextOption;
301+
attributeOptions.text = tooltipTextOption;
289302
}
290303

291304
if (positionOption) {

0 commit comments

Comments
 (0)