Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes vulnerabilities in #38 #6639

Open
wants to merge 1 commit into
base: v1-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions jade/page-contents/autocomplete_content.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
<div id="initialization" class="scrollspy section">
<h3 class="header">Initialization</h3>
<p>The data is a json object where the key is the matching string and the value is an optional image url.</p>
<p>The key must be a text string. If you trust your data, or have properly sanitized your user input, you may
use HTML by setting the option <code class="language-javascript">allowUnsafeHTML: true</code>.</p>
<pre><code class="language-javascript">
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.autocomplete');
Expand Down Expand Up @@ -104,6 +106,12 @@ <h3 class="header">Options</h3>
<td></td>
<td>Sort function that defines the order of the list of autocomplete options.</td>
</tr>
<tr>
<td>allowUnsafeHTML</td>
<td>Boolean</td>
<td>false</td>
<td>If true will render the key from each item directly as HTML. User input MUST be properly sanitized first.</td>
</tr>
</tbody>
</table>

Expand Down
53 changes: 42 additions & 11 deletions jade/page-contents/toasts_content.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

<div id="introduction" class="section scrollspy">
<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>
<a class="waves-effect waves-light btn" onclick="M.toast({html: 'I am a toast'})">Toast!</a>
<p>To do this, call the M.toast() function programatically in JavaScript.</p>
<a class="waves-effect waves-light btn" onclick="M.toast({text: 'I am a toast'})">Toast!</a>
<p>To do this, call the M.toast() function programmatically in JavaScript.</p>
<pre><code class="language-javascript">
M.toast({html: 'I am a toast!'})
M.toast({text: 'I am a toast!'})
</code></pre>
<p>One way to add this into your application is to add this as an onclick event to a button.</p>
<pre><code class="language-markup">
&lt;a onclick="M.toast({html: 'I am a toast'})" class="btn">Toast!&lt;/a>
&lt;a onclick="M.toast({text: 'I am a toast'})" class="btn">Toast!&lt;/a>
</code></pre>
</div>

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

<tbody>
<tr>
<td>text</td>
<td>String</td>
<td>''</td>
<td>The content of the Toast.</td>
</tr>
<tr>
<td>unsafeHTML</td>
<td>String, HTMLElement</td>
<td>''</td>
<td>
HTML content that will be appended to to <code class="language-javascript">text</code>.
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">unsafeHTML</code>.
</td>
</tr>
<tr>
<td>html</td>
<td>String</td>
<td>''</td>
<td>The HTML content of the Toast.</td>
<td>
<p>
(DEPRECATED): will be removed in a later release.
</p>
<p>
HTML content that will be appended to <code class="language-javascript">text</code>.
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">html</code>.
</p>
<p>
Will be ignored if <code class="language-javascript">unsafeHTML</code> is set.
</p>
</td>
</tr>
<tr>
<td>displayLength</td>
Expand Down Expand Up @@ -117,21 +143,26 @@ <h3 class="header">Properties</h3>

<div id="custom-html" class="section scrollspy">
<h3 class="header">Custom HTML</h3>
<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>
<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>
<p>
Only use a properly sanitized or otherwise trusted HTML string.
</p>
<a class="waves-effect waves-light btn" onclick="displayCustomHTMLToast()">Toast with Action</a>
<pre><code class="language-javascript">
var toastHTML = '&lt;span>I am toast content&lt;/span>&lt;button class="btn-flat toast-action">Undo&lt;/button>';
M.toast({html: toastHTML});
M.toast({unsafeHTML: toastHTML});
</code></pre>
</div>


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

Expand All @@ -140,11 +171,11 @@ <h3 class="header">Callback</h3>
<h3 class="header">Styling Toasts</h3>
<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>

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

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

Expand Down
26 changes: 25 additions & 1 deletion jade/page-contents/tooltips_content.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,35 @@ <h3 class="header">Options</h3>
<td>0</td>
<td>Delay time before tooltip appears.</td>
</tr>
<tr>
<td>text</td>
<td>String</td>
<td></td>
<td>Text string for the tooltip.</td>
</tr>
<tr>
<td>unsafeHTML</td>
<td>String</td>
<td>null</td>
<td>HTML content that will be appended to to <code class="language-javascript">text</code>.
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">unsafeHTML</code>.</td>
</tr>
<tr>
<td>html</td>
<td>String</td>
<td>null</td>
<td>Can take regular text or HTML strings.</td>
<td>
<p>
(DEPRECATED): will be removed in a later release.
</p>
<p>
HTML content that will be appended to <code class="language-javascript">text</code>.
Only use properly sanitized or otherwise trusted data for <code class="language-javascript">html</code>.
</p>
<p>
Will be ignored if <code class="language-javascript">unsafeHTML</code> is set.
</p>
</td>
</tr>
<tr>
<td>margin</td>
Expand Down
57 changes: 32 additions & 25 deletions js/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
sortFunction: function(a, b, inputString) {
// Sort function for sorting autocomplete results
return a.indexOf(inputString) - b.indexOf(inputString);
}
},
allowUnsafeHTML: false
};

/**
Expand Down Expand Up @@ -282,22 +283,14 @@
/**
* Highlight partial match
*/
_highlight(string, $el) {
let img = $el.find('img');
let matchStart = $el
.text()
.toLowerCase()
.indexOf('' + string.toLowerCase() + ''),
matchEnd = matchStart + string.length - 1,
beforeMatch = $el.text().slice(0, matchStart),
matchText = $el.text().slice(matchStart, matchEnd + 1),
afterMatch = $el.text().slice(matchEnd + 1);
$el.html(
`<span>${beforeMatch}<span class='highlight'>${matchText}</span>${afterMatch}</span>`
);
if (img.length) {
$el.prepend(img);
}
_highlight(input, label) {
const start = label.toLowerCase().indexOf('' + input.toLowerCase() + '');
const end = start + input.length - 1;
//custom filters may return results where the string does not match any part
if (start == -1 || end == -1) {
return [label, '', ''];
}
return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
}

/**
Expand Down Expand Up @@ -376,18 +369,32 @@

// Render
for (let i = 0; i < matchingData.length; i++) {
let entry = matchingData[i];
let $autocompleteOption = $('<li></li>');
const entry = matchingData[i];
const item = document.createElement('li');
if (!!entry.data) {
$autocompleteOption.append(
`<img src="${entry.data}" class="right circle"><span>${entry.key}</span>`
);
const img = document.createElement('img');
img.classList.add("right", "circle");
img.src = entry.data;
item.appendChild(img);
}

const parts = this._highlight(val, entry.key);
const s = document.createElement('span');
if (this.options.allowUnsafeHTML) {
s.innerHTML = parts[0] + '<span class="highlight">' + parts[1] + '</span>' + parts[2];
} else {
$autocompleteOption.append('<span>' + entry.key + '</span>');
s.appendChild(document.createTextNode(parts[0]))
if (!!parts[1]){
const highlight = document.createElement('span');
highlight.textContent = parts[1];
highlight.classList.add("highlight");
s.appendChild(highlight);
s.appendChild(document.createTextNode(parts[2]));
}
}
item.appendChild(s);

$(this.container).append($autocompleteOption);
this._highlight(val, $autocompleteOption);
$(this.container).append(item);
}
}

Expand Down
41 changes: 23 additions & 18 deletions js/toasts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

let _defaults = {
html: '',
unsafeHTML: '',
text: '',
displayLength: 4000,
inDuration: 300,
outDuration: 375,
Expand All @@ -18,7 +20,12 @@
* @member Toast#options
*/
this.options = $.extend({}, Toast.defaults, options);
this.message = this.options.html;
this.htmlMessage = this.options.html;
// If the new unsafeHTML is used, prefer that
if (!!this.options.unsafeHTML){
this.htmlMessage = this.options.unsafeHTML;
}
this.message = this.options.text;

/**
* Describes current pan state toast
Expand Down Expand Up @@ -188,28 +195,26 @@
$(toast).addClass(this.options.classes);
}

// Set content
// Set safe text content
toast.textContent = this.message;
if (
typeof HTMLElement === 'object'
? this.message instanceof HTMLElement
: this.message &&
typeof this.message === 'object' &&
this.message !== null &&
this.message.nodeType === 1 &&
typeof this.message.nodeName === 'string'
) {
toast.appendChild(this.message);

// Check if it is jQuery object
} else if (!!this.message.jquery) {
$(toast).append(this.message[0]);

// Insert as html;
? this.htmlMessage instanceof HTMLElement
: this.htmlMessage &&
typeof this.htmlMessage === 'object' &&
this.htmlMessage !== null &&
this.htmlMessage.nodeType === 1 &&
typeof this.htmlMessage.nodeName === 'string'
) { //if the htmlMessage is an HTML node, append it directly
toast.appendChild(this.htmlMessage);
} else if (!!this.htmlMessage.jquery) { // Check if it is jQuery object, append the node
$(toast).append(this.htmlMessage[0]);
} else {
toast.innerHTML = this.message;
// Append as unsanitized html;
$(toast).append(this.htmlMessage);
}

// Append toasft
// Append toast
Toast._container.appendChild(toast);
return toast;
}
Expand Down
19 changes: 16 additions & 3 deletions js/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
exitDelay: 200,
enterDelay: 0,
html: null,
text: '',
unsafeHTML: null,
margin: 5,
inDuration: 250,
outDuration: 200,
Expand Down Expand Up @@ -68,13 +70,24 @@

let tooltipContentEl = document.createElement('div');
tooltipContentEl.classList.add('tooltip-content');
tooltipContentEl.innerHTML = this.options.html;
this._setTooltipContent(tooltipContentEl);

tooltipEl.appendChild(tooltipContentEl);
document.body.appendChild(tooltipEl);
}

_setTooltipContent(tooltipContentEl) {
tooltipContentEl.textContent = this.options.text;
if (!!this.options.html){
$(tooltipContentEl).append(this.options.html);
}
if (!!this.options.unsafeHTML){
$(tooltipContentEl).append(this.options.unsafeHTML);
}
}

_updateTooltipContent() {
this.tooltipEl.querySelector('.tooltip-content').innerHTML = this.options.html;
this._setTooltipContent(this.tooltipEl.querySelector('.tooltip-content'));
}

_setupEventHandlers() {
Expand Down Expand Up @@ -285,7 +298,7 @@
let positionOption = this.el.getAttribute('data-position');

if (tooltipTextOption) {
attributeOptions.html = tooltipTextOption;
attributeOptions.text = tooltipTextOption;
}

if (positionOption) {
Expand Down
Loading