Skip to content

Commit

Permalink
Add sanitize for tooltips and popovers html content.
Browse files Browse the repository at this point in the history
On browsers that `createHTMLDocument` isn't available just return the unsafe HTML.
  • Loading branch information
Johann-S authored and XhmikosR committed Feb 13, 2019
1 parent d4129df commit 2c8abb9
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 9 deletions.
23 changes: 19 additions & 4 deletions js/popover.js
Expand Up @@ -45,10 +45,25 @@
var title = this.getTitle()
var content = this.getContent()

$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
](content)
if (this.options.html) {
var typeContent = typeof content

if (this.options.sanitize) {
title = this.sanitizeHtml(title)

if (typeContent === 'string') {
content = this.sanitizeHtml(content)
}
}

$tip.find('.popover-title').html(title)
$tip.find('.popover-content').children().detach().end()[
typeContent === 'string' ? 'html' : 'append'
](content)
} else {
$tip.find('.popover-title').text(title)
$tip.find('.popover-content').children().detach().end().text(content)
}

$tip.removeClass('fade top bottom left right in')

Expand Down
2 changes: 1 addition & 1 deletion js/tests/unit/popover.js
Expand Up @@ -190,7 +190,7 @@ $(function () {
.bootstrapPopover({
title: 'Test',
content: 'Test',
template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"/><div class="content"><p/></div></div></div>'
template: '<div class="popover foobar"><div class="arrow"></div><div class="inner"><h3 class="title"></h3><div class="content"><p></p></div></div></div>'
})
.one('shown.bs.popover', function () {
assert.notEqual($('.popover').length, 0, 'popover was inserted')
Expand Down
180 changes: 180 additions & 0 deletions js/tests/unit/tooltip.js
Expand Up @@ -1526,4 +1526,184 @@ $(function () {
}
})
})

QUnit.test('should disable sanitizer', function (assert) {
assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
sanitize: false
})

var tooltip = $trigger.data('bs.tooltip')
assert.strictEqual(tooltip.options.sanitize, false)
})

QUnit.test('should sanitize template by removing disallowed tags', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<div>',
' <script>console.log("oups script inserted")</script>',
' <span>Some content</span>',
'</div>'
].join('')
})

var tooltip = $trigger.data('bs.tooltip')
assert.strictEqual(tooltip.options.template.indexOf('script'), -1)
})

QUnit.test('should sanitize template by removing disallowed attributes', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<div>',
' <img src="x" onError="alert(\'test\')">Some content</img>',
'</div>'
].join('')
})

var tooltip = $trigger.data('bs.tooltip')
assert.strictEqual(tooltip.options.template.indexOf('onError'), -1)
})

QUnit.test('should sanitize template by removing tags with XSS', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')
})

var tooltip = $trigger.data('bs.tooltip')
assert.strictEqual(tooltip.options.template.indexOf('javascript'), -1)
})

QUnit.test('should allow custom sanitization rules', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(2)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<a href="javascript:alert(7)">Click me</a>',
'<span>Some content</span>'
].join(''),
whiteList: {
span: null
}
})

var tooltip = $trigger.data('bs.tooltip')

assert.strictEqual(tooltip.options.template.indexOf('<a'), -1)
assert.ok(tooltip.options.template.indexOf('span') !== -1)
})

QUnit.test('should allow passing a custom function for sanitization', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<span>Some content</span>'
].join(''),
sanitizeFn: function (input) {
return input
}
})

var tooltip = $trigger.data('bs.tooltip')

assert.ok(tooltip.options.template.indexOf('span') !== -1)
})

QUnit.test('should allow passing aria attributes', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<span aria-pressed="true">Some content</span>'
].join('')
})

var tooltip = $trigger.data('bs.tooltip')

assert.ok(tooltip.options.template.indexOf('aria-pressed') !== -1)
})

QUnit.test('should not take into account sanitize in data attributes', function (assert) {
if (!document.implementation || !document.implementation.createHTMLDocument) {
assert.expect(0)

return
}

assert.expect(1)

var $trigger = $('<a href="#" rel="tooltip" data-sanitize="false" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<span aria-pressed="true">Some content</span>'
].join('')
})

var tooltip = $trigger.data('bs.tooltip')

assert.strictEqual(tooltip.options.sanitize, true)
})
})

0 comments on commit 2c8abb9

Please sign in to comment.