Add sanitize template option for tooltip/popover plugins.
This commit is contained in:
parent
bf2515ae68
commit
7bc4d2e0bc
|
@ -0,0 +1,127 @@
|
||||||
|
/**
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* Bootstrap (v4.3.0): tools/sanitizer.js
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const uriAttrs = [
|
||||||
|
'background',
|
||||||
|
'cite',
|
||||||
|
'href',
|
||||||
|
'itemtype',
|
||||||
|
'longdesc',
|
||||||
|
'poster',
|
||||||
|
'src',
|
||||||
|
'xlink:href'
|
||||||
|
]
|
||||||
|
|
||||||
|
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
||||||
|
|
||||||
|
export const DefaultWhitelist = {
|
||||||
|
// Global attributes allowed on any supplied element below.
|
||||||
|
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
||||||
|
a: ['target', 'href', 'title', 'rel'],
|
||||||
|
area: [],
|
||||||
|
b: [],
|
||||||
|
br: [],
|
||||||
|
col: [],
|
||||||
|
code: [],
|
||||||
|
div: [],
|
||||||
|
em: [],
|
||||||
|
hr: [],
|
||||||
|
h1: [],
|
||||||
|
h2: [],
|
||||||
|
h3: [],
|
||||||
|
h4: [],
|
||||||
|
h5: [],
|
||||||
|
h6: [],
|
||||||
|
i: [],
|
||||||
|
img: ['src', 'alt', 'title', 'width', 'height'],
|
||||||
|
li: [],
|
||||||
|
ol: [],
|
||||||
|
p: [],
|
||||||
|
pre: [],
|
||||||
|
s: [],
|
||||||
|
small: [],
|
||||||
|
span: [],
|
||||||
|
sub: [],
|
||||||
|
sup: [],
|
||||||
|
strong: [],
|
||||||
|
u: [],
|
||||||
|
ul: []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
||||||
|
*
|
||||||
|
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
|
||||||
|
*/
|
||||||
|
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pattern that matches safe data URLs. Only matches image, video and audio types.
|
||||||
|
*
|
||||||
|
* Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
|
||||||
|
*/
|
||||||
|
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i
|
||||||
|
|
||||||
|
function allowedAttribute(attr, allowedAttributeList) {
|
||||||
|
const attrName = attr.nodeName.toLowerCase()
|
||||||
|
|
||||||
|
if (allowedAttributeList.indexOf(attrName) !== -1) {
|
||||||
|
if (uriAttrs.indexOf(attrName) !== -1) {
|
||||||
|
return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const regExp = allowedAttributeList.filter((attrRegex) => attrRegex instanceof RegExp)
|
||||||
|
|
||||||
|
// Check if a regular expression validates the attribute.
|
||||||
|
for (let i = 0, l = regExp.length; i < l; i++) {
|
||||||
|
if (attrName.match(regExp[i])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
|
||||||
|
if (unsafeHtml.length === 0) {
|
||||||
|
return unsafeHtml
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sanitizeFn && typeof sanitizeFn === 'function') {
|
||||||
|
return sanitizeFn(unsafeHtml)
|
||||||
|
}
|
||||||
|
|
||||||
|
const domParser = new window.DOMParser()
|
||||||
|
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
|
||||||
|
const whitelistKeys = Object.keys(whiteList)
|
||||||
|
const elements = [].slice.call(createdDocument.body.querySelectorAll('*'))
|
||||||
|
|
||||||
|
for (let i = 0, len = elements.length; i < len; i++) {
|
||||||
|
const el = elements[i]
|
||||||
|
const elName = el.nodeName.toLowerCase()
|
||||||
|
|
||||||
|
if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributeList = [].slice.call(el.attributes)
|
||||||
|
const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
|
||||||
|
|
||||||
|
attributeList.forEach((attr) => {
|
||||||
|
if (!allowedAttribute(attr, whitelistedAttributes)) {
|
||||||
|
el.removeAttribute(attr.nodeName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdDocument.body.innerHTML
|
||||||
|
}
|
|
@ -5,6 +5,10 @@
|
||||||
* --------------------------------------------------------------------------
|
* --------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
DefaultWhitelist,
|
||||||
|
sanitizeHtml
|
||||||
|
} from './tools/sanitizer'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import Popper from 'popper.js'
|
import Popper from 'popper.js'
|
||||||
import Util from './util'
|
import Util from './util'
|
||||||
|
@ -15,13 +19,14 @@ import Util from './util'
|
||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NAME = 'tooltip'
|
const NAME = 'tooltip'
|
||||||
const VERSION = '4.3.0'
|
const VERSION = '4.3.0'
|
||||||
const DATA_KEY = 'bs.tooltip'
|
const DATA_KEY = 'bs.tooltip'
|
||||||
const EVENT_KEY = `.${DATA_KEY}`
|
const EVENT_KEY = `.${DATA_KEY}`
|
||||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||||
const CLASS_PREFIX = 'bs-tooltip'
|
const CLASS_PREFIX = 'bs-tooltip'
|
||||||
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
||||||
|
const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
|
||||||
|
|
||||||
const DefaultType = {
|
const DefaultType = {
|
||||||
animation : 'boolean',
|
animation : 'boolean',
|
||||||
|
@ -35,7 +40,10 @@ const DefaultType = {
|
||||||
offset : '(number|string|function)',
|
offset : '(number|string|function)',
|
||||||
container : '(string|element|boolean)',
|
container : '(string|element|boolean)',
|
||||||
fallbackPlacement : '(string|array)',
|
fallbackPlacement : '(string|array)',
|
||||||
boundary : '(string|element)'
|
boundary : '(string|element)',
|
||||||
|
sanitize : 'boolean',
|
||||||
|
sanitizeFn : '(null|function)',
|
||||||
|
whiteList : 'object'
|
||||||
}
|
}
|
||||||
|
|
||||||
const AttachmentMap = {
|
const AttachmentMap = {
|
||||||
|
@ -60,7 +68,10 @@ const Default = {
|
||||||
offset : 0,
|
offset : 0,
|
||||||
container : false,
|
container : false,
|
||||||
fallbackPlacement : 'flip',
|
fallbackPlacement : 'flip',
|
||||||
boundary : 'scrollParent'
|
boundary : 'scrollParent',
|
||||||
|
sanitize : true,
|
||||||
|
sanitizeFn : null,
|
||||||
|
whiteList : DefaultWhitelist
|
||||||
}
|
}
|
||||||
|
|
||||||
const HoverState = {
|
const HoverState = {
|
||||||
|
@ -419,18 +430,27 @@ class Tooltip {
|
||||||
}
|
}
|
||||||
|
|
||||||
setElementContent($element, content) {
|
setElementContent($element, content) {
|
||||||
const html = this.config.html
|
|
||||||
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
|
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
|
||||||
// Content is a DOM node or a jQuery
|
// Content is a DOM node or a jQuery
|
||||||
if (html) {
|
if (this.config.html) {
|
||||||
if (!$(content).parent().is($element)) {
|
if (!$(content).parent().is($element)) {
|
||||||
$element.empty().append(content)
|
$element.empty().append(content)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$element.text($(content).text())
|
$element.text($(content).text())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.html) {
|
||||||
|
if (this.config.sanitize) {
|
||||||
|
content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
$element.html(content)
|
||||||
} else {
|
} else {
|
||||||
$element[html ? 'html' : 'text'](content)
|
$element.text(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,9 +656,18 @@ class Tooltip {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getConfig(config) {
|
_getConfig(config) {
|
||||||
|
const dataAttributes = $(this.element).data()
|
||||||
|
|
||||||
|
Object.keys(dataAttributes)
|
||||||
|
.forEach((dataAttr) => {
|
||||||
|
if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
|
||||||
|
delete dataAttributes[dataAttr]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
...this.constructor.Default,
|
...this.constructor.Default,
|
||||||
...$(this.element).data(),
|
...dataAttributes,
|
||||||
...typeof config === 'object' && config ? config : {}
|
...typeof config === 'object' && config ? config : {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +692,10 @@ class Tooltip {
|
||||||
this.constructor.DefaultType
|
this.constructor.DefaultType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (config.sanitize) {
|
||||||
|
config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1106,4 +1106,164 @@ $(function () {
|
||||||
assert.strictEqual(offset.offset, myOffset)
|
assert.strictEqual(offset.offset, myOffset)
|
||||||
assert.ok(typeof offset.fn === 'undefined')
|
assert.ok(typeof offset.fn === 'undefined')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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.config.sanitize, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should sanitize template by removing disallowed tags', function (assert) {
|
||||||
|
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.config.template.indexOf('script'), -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should sanitize template by removing disallowed attributes', function (assert) {
|
||||||
|
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.config.template.indexOf('onError'), -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should sanitize template by removing tags with XSS', function (assert) {
|
||||||
|
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.config.template.indexOf('script'), -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should allow custom sanitization rules', function (assert) {
|
||||||
|
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.config.template.indexOf('<a'), -1)
|
||||||
|
assert.ok(tooltip.config.template.indexOf('span') !== -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should allow passing a custom function for sanitization', function (assert) {
|
||||||
|
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.config.template.indexOf('span') !== -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should allow passing aria attributes', function (assert) {
|
||||||
|
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.config.template.indexOf('aria-pressed') !== -1)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should not sanitize element content', function (assert) {
|
||||||
|
assert.expect(1)
|
||||||
|
|
||||||
|
var $element = $('<div />').appendTo('#qunit-fixture')
|
||||||
|
var content = '<script>var test = 1;</script>'
|
||||||
|
|
||||||
|
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(''),
|
||||||
|
html: true,
|
||||||
|
sanitize: false
|
||||||
|
})
|
||||||
|
|
||||||
|
var tooltip = $trigger.data('bs.tooltip')
|
||||||
|
tooltip.setElementContent($element, content)
|
||||||
|
|
||||||
|
assert.strictEqual($element[0].innerHTML, content)
|
||||||
|
})
|
||||||
|
|
||||||
|
QUnit.test('should not take into account sanitize in data attributes', function (assert) {
|
||||||
|
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.config.sanitize, true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -182,19 +182,19 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.bundle.js",
|
"path": "./dist/js/bootstrap.bundle.js",
|
||||||
"maxSize": "45 kB"
|
"maxSize": "47 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.bundle.min.js",
|
"path": "./dist/js/bootstrap.bundle.min.js",
|
||||||
"maxSize": "21.25 kB"
|
"maxSize": "22 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.js",
|
"path": "./dist/js/bootstrap.js",
|
||||||
"maxSize": "23 kB"
|
"maxSize": "25 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "./dist/js/bootstrap.min.js",
|
"path": "./dist/js/bootstrap.min.js",
|
||||||
"maxSize": "14.5 kB"
|
"maxSize": "15.5 kB"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"jspm": {
|
"jspm": {
|
||||||
|
|
|
@ -140,6 +140,11 @@ Enable popovers via JavaScript:
|
||||||
|
|
||||||
Options can be passed via data attributes or JavaScript. For data attributes, append the option name to `data-`, as in `data-animation=""`.
|
Options can be passed via data attributes or JavaScript. For data attributes, append the option name to `data-`, as in `data-animation=""`.
|
||||||
|
|
||||||
|
{% capture callout %}
|
||||||
|
Note that for security reasons the `sanitize`, `sanitizeFn` and `whiteList` options cannot be supplied using data attributes.
|
||||||
|
{% endcapture %}
|
||||||
|
{% include callout.html content=callout type="warning" %}
|
||||||
|
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -250,6 +255,24 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
|
||||||
<td>'scrollParent'</td>
|
<td>'scrollParent'</td>
|
||||||
<td>Overflow constraint boundary of the popover. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper.js's <a href="https://popper.js.org/popper-documentation.html#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
|
<td>Overflow constraint boundary of the popover. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper.js's <a href="https://popper.js.org/popper-documentation.html#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>sanitize</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>true</td>
|
||||||
|
<td>Enable or disable the sanitization. If activated <code>'template'</code>, <code>'content'</code> and <code>'title'</code> options will be sanitized.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>whiteList</td>
|
||||||
|
<td>object</td>
|
||||||
|
<td><a href="{{ site.baseurl }}/docs/{{ site.docs_version }}/getting-started/javascript/#sanitizer">Default value</a></td>
|
||||||
|
<td>Object which contains allowed attributes and tags</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>sanitizeFn</td>
|
||||||
|
<td>null | function</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization.</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,11 @@ Elements with the `disabled` attribute aren't interactive, meaning users cannot
|
||||||
|
|
||||||
Options can be passed via data attributes or JavaScript. For data attributes, append the option name to `data-`, as in `data-animation=""`.
|
Options can be passed via data attributes or JavaScript. For data attributes, append the option name to `data-`, as in `data-animation=""`.
|
||||||
|
|
||||||
|
{% capture callout %}
|
||||||
|
Note that for security reasons the `sanitize`, `sanitizeFn` and `whiteList` options cannot be supplied using data attributes.
|
||||||
|
{% endcapture %}
|
||||||
|
{% include callout.html content=callout type="warning" %}
|
||||||
|
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -255,6 +260,24 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
|
||||||
<td>'scrollParent'</td>
|
<td>'scrollParent'</td>
|
||||||
<td>Overflow constraint boundary of the tooltip. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper.js's <a href="https://popper.js.org/popper-documentation.html#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
|
<td>Overflow constraint boundary of the tooltip. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper.js's <a href="https://popper.js.org/popper-documentation.html#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>sanitize</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>true</td>
|
||||||
|
<td>Enable or disable the sanitization. If activated <code>'template'</code> and <code>'title'</code> options will be sanitized.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>whiteList</td>
|
||||||
|
<td>object</td>
|
||||||
|
<td><a href="{{ site.baseurl }}/docs/{{ site.docs_version }}/getting-started/javascript/#sanitizer">Default value</a></td>
|
||||||
|
<td>Object which contains allowed attributes and tags</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>sanitizeFn</td>
|
||||||
|
<td>null | function</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization.</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -139,3 +139,73 @@ Bootstrap's plugins don't fall back particularly gracefully when JavaScript is d
|
||||||
All Bootstrap's JavaScript files depend on `util.js` and it has to be included alongside the other JavaScript files. If you're using the compiled (or minified) `bootstrap.js`, there is no need to include this—it's already there.
|
All Bootstrap's JavaScript files depend on `util.js` and it has to be included alongside the other JavaScript files. If you're using the compiled (or minified) `bootstrap.js`, there is no need to include this—it's already there.
|
||||||
|
|
||||||
`util.js` includes utility functions and a basic helper for `transitionEnd` events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.
|
`util.js` includes utility functions and a basic helper for `transitionEnd` events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.
|
||||||
|
|
||||||
|
## Sanitizer
|
||||||
|
|
||||||
|
Tooltips and Popovers use our built-in sanitizer to sanitize options which accept HTML.
|
||||||
|
|
||||||
|
The default `whiteList` value is the following:
|
||||||
|
|
||||||
|
{% highlight js %}
|
||||||
|
var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
||||||
|
var DefaultWhitelist = {
|
||||||
|
// Global attributes allowed on any supplied element below.
|
||||||
|
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
||||||
|
a: ['target', 'href', 'title', 'rel'],
|
||||||
|
area: [],
|
||||||
|
b: [],
|
||||||
|
br: [],
|
||||||
|
col: [],
|
||||||
|
code: [],
|
||||||
|
div: [],
|
||||||
|
em: [],
|
||||||
|
hr: [],
|
||||||
|
h1: [],
|
||||||
|
h2: [],
|
||||||
|
h3: [],
|
||||||
|
h4: [],
|
||||||
|
h5: [],
|
||||||
|
h6: [],
|
||||||
|
i: [],
|
||||||
|
img: ['src', 'alt', 'title', 'width', 'height'],
|
||||||
|
li: [],
|
||||||
|
ol: [],
|
||||||
|
p: [],
|
||||||
|
pre: [],
|
||||||
|
s: [],
|
||||||
|
small: [],
|
||||||
|
span: [],
|
||||||
|
sub: [],
|
||||||
|
sup: [],
|
||||||
|
strong: [],
|
||||||
|
u: [],
|
||||||
|
ul: []
|
||||||
|
}
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
If you want to add new values to this default `whiteList` you can do the following:
|
||||||
|
|
||||||
|
{% highlight js %}
|
||||||
|
var myDefaultWhiteList = $.fn.tooltip.Constructor.Default.whiteList
|
||||||
|
|
||||||
|
// To allow table elements
|
||||||
|
myDefaultWhiteList.table = []
|
||||||
|
|
||||||
|
// To allow td elements and data-option attributes on td elements
|
||||||
|
myDefaultWhiteList.td = ['data-option']
|
||||||
|
|
||||||
|
// You can push your custom regex to validate your attributes.
|
||||||
|
// Be careful about your regular expressions being too lax
|
||||||
|
var myCustomRegex = /^data-my-app-[\w-]+/
|
||||||
|
myDefaultWhiteList['*'].push(myCustomRegex)
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
If you want to bypass our sanitizer because you prefer to use a dedicated library, for example [DOMPurify](https://www.npmjs.com/package/dompurify), you should do the following:
|
||||||
|
|
||||||
|
{% highlight js %}
|
||||||
|
$('#yourTooltip').tooltip({
|
||||||
|
sanitizeFn: function (content) {
|
||||||
|
return DOMPurify.sanitize(content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{% endhighlight %}
|
||||||
|
|
Loading…
Reference in New Issue