dompurify 2.3.6 → 2.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/purify.cjs.js +451 -283
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +452 -284
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +453 -285
- package/dist/purify.js.map +1 -1
- package/dist/purify.min.js +2 -2
- package/dist/purify.min.js.map +1 -1
- package/package.json +14 -19
package/dist/purify.js
CHANGED
|
@@ -1,12 +1,92 @@
|
|
|
1
|
-
/*! @license DOMPurify 2.3.
|
|
1
|
+
/*! @license DOMPurify 2.3.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.9/LICENSE */
|
|
2
2
|
|
|
3
3
|
(function (global, factory) {
|
|
4
4
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
5
5
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
6
|
-
(global = global || self, global.DOMPurify = factory());
|
|
7
|
-
}(this, function () { 'use strict';
|
|
6
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DOMPurify = factory());
|
|
7
|
+
})(this, (function () { 'use strict';
|
|
8
8
|
|
|
9
|
-
function
|
|
9
|
+
function _typeof(obj) {
|
|
10
|
+
"@babel/helpers - typeof";
|
|
11
|
+
|
|
12
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
|
|
13
|
+
return typeof obj;
|
|
14
|
+
} : function (obj) {
|
|
15
|
+
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
16
|
+
}, _typeof(obj);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function _setPrototypeOf(o, p) {
|
|
20
|
+
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
21
|
+
o.__proto__ = p;
|
|
22
|
+
return o;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return _setPrototypeOf(o, p);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _isNativeReflectConstruct() {
|
|
29
|
+
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
30
|
+
if (Reflect.construct.sham) return false;
|
|
31
|
+
if (typeof Proxy === "function") return true;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
|
35
|
+
return true;
|
|
36
|
+
} catch (e) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _construct(Parent, args, Class) {
|
|
42
|
+
if (_isNativeReflectConstruct()) {
|
|
43
|
+
_construct = Reflect.construct;
|
|
44
|
+
} else {
|
|
45
|
+
_construct = function _construct(Parent, args, Class) {
|
|
46
|
+
var a = [null];
|
|
47
|
+
a.push.apply(a, args);
|
|
48
|
+
var Constructor = Function.bind.apply(Parent, a);
|
|
49
|
+
var instance = new Constructor();
|
|
50
|
+
if (Class) _setPrototypeOf(instance, Class.prototype);
|
|
51
|
+
return instance;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return _construct.apply(null, arguments);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function _toConsumableArray(arr) {
|
|
59
|
+
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _arrayWithoutHoles(arr) {
|
|
63
|
+
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _iterableToArray(iter) {
|
|
67
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _unsupportedIterableToArray(o, minLen) {
|
|
71
|
+
if (!o) return;
|
|
72
|
+
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
73
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
74
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
75
|
+
if (n === "Map" || n === "Set") return Array.from(o);
|
|
76
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function _arrayLikeToArray(arr, len) {
|
|
80
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
81
|
+
|
|
82
|
+
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
83
|
+
|
|
84
|
+
return arr2;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function _nonIterableSpread() {
|
|
88
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
89
|
+
}
|
|
10
90
|
|
|
11
91
|
var hasOwnProperty = Object.hasOwnProperty,
|
|
12
92
|
setPrototypeOf = Object.setPrototypeOf,
|
|
@@ -41,46 +121,43 @@
|
|
|
41
121
|
|
|
42
122
|
if (!construct) {
|
|
43
123
|
construct = function construct(Func, args) {
|
|
44
|
-
return
|
|
124
|
+
return _construct(Func, _toConsumableArray(args));
|
|
45
125
|
};
|
|
46
126
|
}
|
|
47
127
|
|
|
48
128
|
var arrayForEach = unapply(Array.prototype.forEach);
|
|
49
129
|
var arrayPop = unapply(Array.prototype.pop);
|
|
50
130
|
var arrayPush = unapply(Array.prototype.push);
|
|
51
|
-
|
|
52
131
|
var stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
53
132
|
var stringMatch = unapply(String.prototype.match);
|
|
54
133
|
var stringReplace = unapply(String.prototype.replace);
|
|
55
134
|
var stringIndexOf = unapply(String.prototype.indexOf);
|
|
56
135
|
var stringTrim = unapply(String.prototype.trim);
|
|
57
|
-
|
|
58
136
|
var regExpTest = unapply(RegExp.prototype.test);
|
|
59
|
-
|
|
60
137
|
var typeErrorCreate = unconstruct(TypeError);
|
|
61
|
-
|
|
62
138
|
function unapply(func) {
|
|
63
139
|
return function (thisArg) {
|
|
64
|
-
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
140
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
65
141
|
args[_key - 1] = arguments[_key];
|
|
66
142
|
}
|
|
67
143
|
|
|
68
144
|
return apply(func, thisArg, args);
|
|
69
145
|
};
|
|
70
146
|
}
|
|
71
|
-
|
|
72
147
|
function unconstruct(func) {
|
|
73
148
|
return function () {
|
|
74
|
-
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
149
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
75
150
|
args[_key2] = arguments[_key2];
|
|
76
151
|
}
|
|
77
152
|
|
|
78
153
|
return construct(func, args);
|
|
79
154
|
};
|
|
80
155
|
}
|
|
81
|
-
|
|
82
156
|
/* Add properties to a lookup table */
|
|
83
|
-
|
|
157
|
+
|
|
158
|
+
function addToSet(set, array, transformCaseFunc) {
|
|
159
|
+
transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase;
|
|
160
|
+
|
|
84
161
|
if (setPrototypeOf) {
|
|
85
162
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
86
163
|
// independent of any properties defined on Object.prototype.
|
|
@@ -89,10 +166,13 @@
|
|
|
89
166
|
}
|
|
90
167
|
|
|
91
168
|
var l = array.length;
|
|
169
|
+
|
|
92
170
|
while (l--) {
|
|
93
171
|
var element = array[l];
|
|
172
|
+
|
|
94
173
|
if (typeof element === 'string') {
|
|
95
|
-
var lcElement =
|
|
174
|
+
var lcElement = transformCaseFunc(element);
|
|
175
|
+
|
|
96
176
|
if (lcElement !== element) {
|
|
97
177
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
98
178
|
if (!isFrozen(array)) {
|
|
@@ -108,12 +188,12 @@
|
|
|
108
188
|
|
|
109
189
|
return set;
|
|
110
190
|
}
|
|
111
|
-
|
|
112
191
|
/* Shallow clone an object */
|
|
192
|
+
|
|
113
193
|
function clone(object) {
|
|
114
194
|
var newObject = create(null);
|
|
195
|
+
var property;
|
|
115
196
|
|
|
116
|
-
var property = void 0;
|
|
117
197
|
for (property in object) {
|
|
118
198
|
if (apply(hasOwnProperty, object, [property])) {
|
|
119
199
|
newObject[property] = object[property];
|
|
@@ -122,14 +202,15 @@
|
|
|
122
202
|
|
|
123
203
|
return newObject;
|
|
124
204
|
}
|
|
125
|
-
|
|
126
205
|
/* IE10 doesn't support __lookupGetter__ so lets'
|
|
127
206
|
* simulate it. It also automatically checks
|
|
128
207
|
* if the prop is function or getter and behaves
|
|
129
208
|
* accordingly. */
|
|
209
|
+
|
|
130
210
|
function lookupGetter(object, prop) {
|
|
131
211
|
while (object !== null) {
|
|
132
212
|
var desc = getOwnPropertyDescriptor(object, prop);
|
|
213
|
+
|
|
133
214
|
if (desc) {
|
|
134
215
|
if (desc.get) {
|
|
135
216
|
return unapply(desc.get);
|
|
@@ -151,40 +232,33 @@
|
|
|
151
232
|
return fallbackValue;
|
|
152
233
|
}
|
|
153
234
|
|
|
154
|
-
var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
|
|
155
|
-
|
|
156
|
-
// SVG
|
|
157
|
-
var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
235
|
+
var html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG
|
|
158
236
|
|
|
159
|
-
var
|
|
160
|
-
|
|
161
|
-
// List of SVG elements that are disallowed by default.
|
|
237
|
+
var svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
|
|
238
|
+
var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default.
|
|
162
239
|
// We still need to know them so that we can do namespace
|
|
163
240
|
// checks properly in case one wants to add them to
|
|
164
241
|
// allow-list.
|
|
165
|
-
var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
|
|
166
242
|
|
|
167
|
-
var
|
|
168
|
-
|
|
169
|
-
// Similarly to SVG, we want to know all MathML elements,
|
|
243
|
+
var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);
|
|
244
|
+
var mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']); // Similarly to SVG, we want to know all MathML elements,
|
|
170
245
|
// even those that we disallow by default.
|
|
171
|
-
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
172
246
|
|
|
247
|
+
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
173
248
|
var text = freeze(['#text']);
|
|
174
249
|
|
|
175
|
-
var html
|
|
176
|
-
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
180
|
-
|
|
250
|
+
var html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']);
|
|
251
|
+
var svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
252
|
+
var mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
181
253
|
var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
182
254
|
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
var ERB_EXPR = seal(/<%[\
|
|
255
|
+
var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
256
|
+
|
|
257
|
+
var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
186
258
|
var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
259
|
+
|
|
187
260
|
var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
261
|
+
|
|
188
262
|
var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
|
189
263
|
);
|
|
190
264
|
var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
@@ -192,14 +266,9 @@
|
|
|
192
266
|
);
|
|
193
267
|
var DOCTYPE_NAME = seal(/^html$/i);
|
|
194
268
|
|
|
195
|
-
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
196
|
-
|
|
197
|
-
function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
198
|
-
|
|
199
269
|
var getGlobal = function getGlobal() {
|
|
200
270
|
return typeof window === 'undefined' ? null : window;
|
|
201
271
|
};
|
|
202
|
-
|
|
203
272
|
/**
|
|
204
273
|
* Creates a no-op policy for internal use only.
|
|
205
274
|
* Don't export this function outside this module!
|
|
@@ -208,16 +277,19 @@
|
|
|
208
277
|
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
209
278
|
* are not supported).
|
|
210
279
|
*/
|
|
280
|
+
|
|
281
|
+
|
|
211
282
|
var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
|
|
212
|
-
if (
|
|
283
|
+
if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
213
284
|
return null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Allow the callers to control the unique policy name
|
|
285
|
+
} // Allow the callers to control the unique policy name
|
|
217
286
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
218
287
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
288
|
+
|
|
289
|
+
|
|
219
290
|
var suffix = null;
|
|
220
291
|
var ATTR_NAME = 'data-tt-policy-suffix';
|
|
292
|
+
|
|
221
293
|
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
|
|
222
294
|
suffix = document.currentScript.getAttribute(ATTR_NAME);
|
|
223
295
|
}
|
|
@@ -226,8 +298,8 @@
|
|
|
226
298
|
|
|
227
299
|
try {
|
|
228
300
|
return trustedTypes.createPolicy(policyName, {
|
|
229
|
-
createHTML: function createHTML(html
|
|
230
|
-
return html
|
|
301
|
+
createHTML: function createHTML(html) {
|
|
302
|
+
return html;
|
|
231
303
|
}
|
|
232
304
|
});
|
|
233
305
|
} catch (_) {
|
|
@@ -245,29 +317,28 @@
|
|
|
245
317
|
var DOMPurify = function DOMPurify(root) {
|
|
246
318
|
return createDOMPurify(root);
|
|
247
319
|
};
|
|
248
|
-
|
|
249
320
|
/**
|
|
250
321
|
* Version label, exposed for easier checks
|
|
251
322
|
* if DOMPurify is up to date or not
|
|
252
323
|
*/
|
|
253
|
-
DOMPurify.version = '2.3.6';
|
|
254
324
|
|
|
325
|
+
|
|
326
|
+
DOMPurify.version = '2.3.9';
|
|
255
327
|
/**
|
|
256
328
|
* Array of elements that DOMPurify removed during sanitation.
|
|
257
329
|
* Empty if nothing was removed.
|
|
258
330
|
*/
|
|
331
|
+
|
|
259
332
|
DOMPurify.removed = [];
|
|
260
333
|
|
|
261
334
|
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
262
335
|
// Not running in a browser, provide a factory function
|
|
263
336
|
// so that you can pass your own Window
|
|
264
337
|
DOMPurify.isSupported = false;
|
|
265
|
-
|
|
266
338
|
return DOMPurify;
|
|
267
339
|
}
|
|
268
340
|
|
|
269
341
|
var originalDocument = window.document;
|
|
270
|
-
|
|
271
342
|
var document = window.document;
|
|
272
343
|
var DocumentFragment = window.DocumentFragment,
|
|
273
344
|
HTMLTemplateElement = window.HTMLTemplateElement,
|
|
@@ -275,63 +346,57 @@
|
|
|
275
346
|
Element = window.Element,
|
|
276
347
|
NodeFilter = window.NodeFilter,
|
|
277
348
|
_window$NamedNodeMap = window.NamedNodeMap,
|
|
278
|
-
NamedNodeMap = _window$NamedNodeMap ===
|
|
349
|
+
NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
|
|
279
350
|
HTMLFormElement = window.HTMLFormElement,
|
|
280
351
|
DOMParser = window.DOMParser,
|
|
281
352
|
trustedTypes = window.trustedTypes;
|
|
282
|
-
|
|
283
|
-
|
|
284
353
|
var ElementPrototype = Element.prototype;
|
|
285
|
-
|
|
286
354
|
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
287
355
|
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
288
356
|
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
289
|
-
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
290
|
-
|
|
291
|
-
// As per issue #47, the web-components registry is inherited by a
|
|
357
|
+
var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a
|
|
292
358
|
// new document created via createHTMLDocument. As per the spec
|
|
293
359
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
294
360
|
// a new empty registry is used when creating a template contents owner
|
|
295
361
|
// document, so we use that as our parent document to ensure nothing
|
|
296
362
|
// is inherited.
|
|
363
|
+
|
|
297
364
|
if (typeof HTMLTemplateElement === 'function') {
|
|
298
365
|
var template = document.createElement('template');
|
|
366
|
+
|
|
299
367
|
if (template.content && template.content.ownerDocument) {
|
|
300
368
|
document = template.content.ownerDocument;
|
|
301
369
|
}
|
|
302
370
|
}
|
|
303
371
|
|
|
304
372
|
var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
|
|
305
|
-
var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
|
|
306
373
|
|
|
374
|
+
var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
|
|
307
375
|
var _document = document,
|
|
308
376
|
implementation = _document.implementation,
|
|
309
377
|
createNodeIterator = _document.createNodeIterator,
|
|
310
378
|
createDocumentFragment = _document.createDocumentFragment,
|
|
311
379
|
getElementsByTagName = _document.getElementsByTagName;
|
|
312
380
|
var importNode = originalDocument.importNode;
|
|
313
|
-
|
|
314
|
-
|
|
315
381
|
var documentMode = {};
|
|
382
|
+
|
|
316
383
|
try {
|
|
317
384
|
documentMode = clone(document).documentMode ? document.documentMode : {};
|
|
318
385
|
} catch (_) {}
|
|
319
386
|
|
|
320
387
|
var hooks = {};
|
|
321
|
-
|
|
322
388
|
/**
|
|
323
389
|
* Expose whether this browser supports running the full DOMPurify.
|
|
324
390
|
*/
|
|
325
|
-
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
|
|
326
|
-
|
|
327
|
-
var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
|
|
328
|
-
ERB_EXPR$$1 = ERB_EXPR,
|
|
329
|
-
DATA_ATTR$$1 = DATA_ATTR,
|
|
330
|
-
ARIA_ATTR$$1 = ARIA_ATTR,
|
|
331
|
-
IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
|
|
332
|
-
ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
|
|
333
|
-
var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
|
|
334
391
|
|
|
392
|
+
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
|
|
393
|
+
var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
|
|
394
|
+
ERB_EXPR$1 = ERB_EXPR,
|
|
395
|
+
DATA_ATTR$1 = DATA_ATTR,
|
|
396
|
+
ARIA_ATTR$1 = ARIA_ATTR,
|
|
397
|
+
IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
|
|
398
|
+
ATTR_WHITESPACE$1 = ATTR_WHITESPACE;
|
|
399
|
+
var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
335
400
|
/**
|
|
336
401
|
* We consider the elements and attributes below to be safe. Ideally
|
|
337
402
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
@@ -340,18 +405,18 @@
|
|
|
340
405
|
/* allowed element names */
|
|
341
406
|
|
|
342
407
|
var ALLOWED_TAGS = null;
|
|
343
|
-
var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1
|
|
344
|
-
|
|
408
|
+
var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
|
|
345
409
|
/* Allowed attribute names */
|
|
346
|
-
var ALLOWED_ATTR = null;
|
|
347
|
-
var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));
|
|
348
410
|
|
|
411
|
+
var ALLOWED_ATTR = null;
|
|
412
|
+
var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
|
|
349
413
|
/*
|
|
350
414
|
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
351
415
|
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
352
416
|
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
353
417
|
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
354
418
|
*/
|
|
419
|
+
|
|
355
420
|
var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
356
421
|
tagNameCheck: {
|
|
357
422
|
writable: true,
|
|
@@ -372,93 +437,93 @@
|
|
|
372
437
|
value: false
|
|
373
438
|
}
|
|
374
439
|
}));
|
|
375
|
-
|
|
376
440
|
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
377
|
-
var FORBID_TAGS = null;
|
|
378
441
|
|
|
442
|
+
var FORBID_TAGS = null;
|
|
379
443
|
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
380
|
-
var FORBID_ATTR = null;
|
|
381
444
|
|
|
445
|
+
var FORBID_ATTR = null;
|
|
382
446
|
/* Decide if ARIA attributes are okay */
|
|
383
|
-
var ALLOW_ARIA_ATTR = true;
|
|
384
447
|
|
|
448
|
+
var ALLOW_ARIA_ATTR = true;
|
|
385
449
|
/* Decide if custom data attributes are okay */
|
|
386
|
-
var ALLOW_DATA_ATTR = true;
|
|
387
450
|
|
|
451
|
+
var ALLOW_DATA_ATTR = true;
|
|
388
452
|
/* Decide if unknown protocols are okay */
|
|
389
|
-
var ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
390
453
|
|
|
454
|
+
var ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
391
455
|
/* Output should be safe for common template engines.
|
|
392
456
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
393
457
|
*/
|
|
394
|
-
var SAFE_FOR_TEMPLATES = false;
|
|
395
458
|
|
|
459
|
+
var SAFE_FOR_TEMPLATES = false;
|
|
396
460
|
/* Decide if document with <html>... should be returned */
|
|
397
|
-
var WHOLE_DOCUMENT = false;
|
|
398
461
|
|
|
462
|
+
var WHOLE_DOCUMENT = false;
|
|
399
463
|
/* Track whether config is already set on this instance of DOMPurify. */
|
|
400
|
-
var SET_CONFIG = false;
|
|
401
464
|
|
|
465
|
+
var SET_CONFIG = false;
|
|
402
466
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
403
467
|
* document.body. By default, browsers might move them to document.head */
|
|
404
|
-
var FORCE_BODY = false;
|
|
405
468
|
|
|
469
|
+
var FORCE_BODY = false;
|
|
406
470
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
407
471
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
408
472
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
409
473
|
*/
|
|
410
|
-
var RETURN_DOM = false;
|
|
411
474
|
|
|
475
|
+
var RETURN_DOM = false;
|
|
412
476
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
413
477
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
414
|
-
var RETURN_DOM_FRAGMENT = false;
|
|
415
478
|
|
|
479
|
+
var RETURN_DOM_FRAGMENT = false;
|
|
416
480
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
417
481
|
* case Trusted Types are not supported */
|
|
418
|
-
var RETURN_TRUSTED_TYPE = false;
|
|
419
482
|
|
|
483
|
+
var RETURN_TRUSTED_TYPE = false;
|
|
420
484
|
/* Output should be free from DOM clobbering attacks? */
|
|
421
|
-
var SANITIZE_DOM = true;
|
|
422
485
|
|
|
486
|
+
var SANITIZE_DOM = true;
|
|
423
487
|
/* Keep element content when removing element? */
|
|
424
|
-
var KEEP_CONTENT = true;
|
|
425
488
|
|
|
489
|
+
var KEEP_CONTENT = true;
|
|
426
490
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
427
491
|
* of importing it into a new Document and returning a sanitized copy */
|
|
428
|
-
var IN_PLACE = false;
|
|
429
492
|
|
|
493
|
+
var IN_PLACE = false;
|
|
430
494
|
/* Allow usage of profiles like html, svg and mathMl */
|
|
431
|
-
var USE_PROFILES = {};
|
|
432
495
|
|
|
496
|
+
var USE_PROFILES = {};
|
|
433
497
|
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
498
|
+
|
|
434
499
|
var FORBID_CONTENTS = null;
|
|
435
500
|
var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
|
|
436
|
-
|
|
437
501
|
/* Tags that are safe for data: URIs */
|
|
502
|
+
|
|
438
503
|
var DATA_URI_TAGS = null;
|
|
439
504
|
var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
440
|
-
|
|
441
505
|
/* Attributes safe for values like "javascript:" */
|
|
506
|
+
|
|
442
507
|
var URI_SAFE_ATTRIBUTES = null;
|
|
443
508
|
var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
444
|
-
|
|
445
509
|
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
446
510
|
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
447
511
|
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
448
512
|
/* Document namespace */
|
|
513
|
+
|
|
449
514
|
var NAMESPACE = HTML_NAMESPACE;
|
|
450
515
|
var IS_EMPTY_INPUT = false;
|
|
451
|
-
|
|
452
516
|
/* Parsing of strict XHTML documents */
|
|
453
|
-
|
|
517
|
+
|
|
518
|
+
var PARSER_MEDIA_TYPE;
|
|
454
519
|
var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
455
520
|
var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
456
|
-
var transformCaseFunc
|
|
457
|
-
|
|
521
|
+
var transformCaseFunc;
|
|
458
522
|
/* Keep a reference to config to pass to hooks */
|
|
459
|
-
var CONFIG = null;
|
|
460
523
|
|
|
524
|
+
var CONFIG = null;
|
|
461
525
|
/* Ideally, do not touch anything below this line */
|
|
526
|
+
|
|
462
527
|
/* ______________________________________________ */
|
|
463
528
|
|
|
464
529
|
var formElement = document.createElement('form');
|
|
@@ -466,49 +531,79 @@
|
|
|
466
531
|
var isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
467
532
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
468
533
|
};
|
|
469
|
-
|
|
470
534
|
/**
|
|
471
535
|
* _parseConfig
|
|
472
536
|
*
|
|
473
537
|
* @param {Object} cfg optional config literal
|
|
474
538
|
*/
|
|
475
539
|
// eslint-disable-next-line complexity
|
|
540
|
+
|
|
541
|
+
|
|
476
542
|
var _parseConfig = function _parseConfig(cfg) {
|
|
477
543
|
if (CONFIG && CONFIG === cfg) {
|
|
478
544
|
return;
|
|
479
545
|
}
|
|
480
|
-
|
|
481
546
|
/* Shield configuration object from tampering */
|
|
482
|
-
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
if (!cfg || _typeof(cfg) !== 'object') {
|
|
483
550
|
cfg = {};
|
|
484
551
|
}
|
|
485
|
-
|
|
486
552
|
/* Shield configuration object from prototype pollution */
|
|
553
|
+
|
|
554
|
+
|
|
487
555
|
cfg = clone(cfg);
|
|
556
|
+
PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
|
|
557
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE; // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
488
558
|
|
|
559
|
+
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
|
|
560
|
+
return x;
|
|
561
|
+
} : stringToLowerCase;
|
|
489
562
|
/* Set configuration parameters */
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
563
|
+
|
|
564
|
+
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
565
|
+
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
566
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent
|
|
567
|
+
cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent
|
|
568
|
+
transformCaseFunc // eslint-disable-line indent
|
|
569
|
+
) // eslint-disable-line indent
|
|
570
|
+
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
571
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent
|
|
572
|
+
cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent
|
|
573
|
+
transformCaseFunc // eslint-disable-line indent
|
|
574
|
+
) // eslint-disable-line indent
|
|
575
|
+
: DEFAULT_DATA_URI_TAGS;
|
|
576
|
+
FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
|
|
577
|
+
FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : {};
|
|
578
|
+
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
497
579
|
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
498
580
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
581
|
+
|
|
499
582
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
583
|
+
|
|
500
584
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
585
|
+
|
|
501
586
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
587
|
+
|
|
502
588
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
589
|
+
|
|
503
590
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
591
|
+
|
|
504
592
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
593
|
+
|
|
505
594
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
595
|
+
|
|
506
596
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
597
|
+
|
|
507
598
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
599
|
+
|
|
508
600
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
601
|
+
|
|
509
602
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
510
|
-
|
|
603
|
+
|
|
604
|
+
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
|
|
511
605
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
606
|
+
|
|
512
607
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
513
608
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
514
609
|
}
|
|
@@ -521,15 +616,6 @@
|
|
|
521
616
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
522
617
|
}
|
|
523
618
|
|
|
524
|
-
PARSER_MEDIA_TYPE =
|
|
525
|
-
// eslint-disable-next-line unicorn/prefer-includes
|
|
526
|
-
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
|
|
527
|
-
|
|
528
|
-
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
529
|
-
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? function (x) {
|
|
530
|
-
return x;
|
|
531
|
-
} : stringToLowerCase;
|
|
532
|
-
|
|
533
619
|
if (SAFE_FOR_TEMPLATES) {
|
|
534
620
|
ALLOW_DATA_ATTR = false;
|
|
535
621
|
}
|
|
@@ -537,42 +623,45 @@
|
|
|
537
623
|
if (RETURN_DOM_FRAGMENT) {
|
|
538
624
|
RETURN_DOM = true;
|
|
539
625
|
}
|
|
540
|
-
|
|
541
626
|
/* Parse profile info */
|
|
627
|
+
|
|
628
|
+
|
|
542
629
|
if (USE_PROFILES) {
|
|
543
|
-
ALLOWED_TAGS = addToSet({},
|
|
630
|
+
ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
|
|
544
631
|
ALLOWED_ATTR = [];
|
|
632
|
+
|
|
545
633
|
if (USE_PROFILES.html === true) {
|
|
546
|
-
addToSet(ALLOWED_TAGS, html);
|
|
547
|
-
addToSet(ALLOWED_ATTR, html
|
|
634
|
+
addToSet(ALLOWED_TAGS, html$1);
|
|
635
|
+
addToSet(ALLOWED_ATTR, html);
|
|
548
636
|
}
|
|
549
637
|
|
|
550
638
|
if (USE_PROFILES.svg === true) {
|
|
551
|
-
addToSet(ALLOWED_TAGS, svg);
|
|
552
|
-
addToSet(ALLOWED_ATTR, svg
|
|
639
|
+
addToSet(ALLOWED_TAGS, svg$1);
|
|
640
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
553
641
|
addToSet(ALLOWED_ATTR, xml);
|
|
554
642
|
}
|
|
555
643
|
|
|
556
644
|
if (USE_PROFILES.svgFilters === true) {
|
|
557
645
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
558
|
-
addToSet(ALLOWED_ATTR, svg
|
|
646
|
+
addToSet(ALLOWED_ATTR, svg);
|
|
559
647
|
addToSet(ALLOWED_ATTR, xml);
|
|
560
648
|
}
|
|
561
649
|
|
|
562
650
|
if (USE_PROFILES.mathMl === true) {
|
|
563
|
-
addToSet(ALLOWED_TAGS, mathMl);
|
|
564
|
-
addToSet(ALLOWED_ATTR, mathMl
|
|
651
|
+
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
652
|
+
addToSet(ALLOWED_ATTR, mathMl);
|
|
565
653
|
addToSet(ALLOWED_ATTR, xml);
|
|
566
654
|
}
|
|
567
655
|
}
|
|
568
|
-
|
|
569
656
|
/* Merge configuration parameters */
|
|
657
|
+
|
|
658
|
+
|
|
570
659
|
if (cfg.ADD_TAGS) {
|
|
571
660
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
572
661
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
573
662
|
}
|
|
574
663
|
|
|
575
|
-
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
|
|
664
|
+
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
576
665
|
}
|
|
577
666
|
|
|
578
667
|
if (cfg.ADD_ATTR) {
|
|
@@ -580,11 +669,11 @@
|
|
|
580
669
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
581
670
|
}
|
|
582
671
|
|
|
583
|
-
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
|
|
672
|
+
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
584
673
|
}
|
|
585
674
|
|
|
586
675
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
587
|
-
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
|
|
676
|
+
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
588
677
|
}
|
|
589
678
|
|
|
590
679
|
if (cfg.FORBID_CONTENTS) {
|
|
@@ -592,27 +681,30 @@
|
|
|
592
681
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
593
682
|
}
|
|
594
683
|
|
|
595
|
-
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS);
|
|
684
|
+
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
596
685
|
}
|
|
597
|
-
|
|
598
686
|
/* Add #text in case KEEP_CONTENT is set to true */
|
|
687
|
+
|
|
688
|
+
|
|
599
689
|
if (KEEP_CONTENT) {
|
|
600
690
|
ALLOWED_TAGS['#text'] = true;
|
|
601
691
|
}
|
|
602
|
-
|
|
603
692
|
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
693
|
+
|
|
694
|
+
|
|
604
695
|
if (WHOLE_DOCUMENT) {
|
|
605
696
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
606
697
|
}
|
|
607
|
-
|
|
608
698
|
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
699
|
+
|
|
700
|
+
|
|
609
701
|
if (ALLOWED_TAGS.table) {
|
|
610
702
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
611
703
|
delete FORBID_TAGS.tbody;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// Prevent further manipulation of configuration.
|
|
704
|
+
} // Prevent further manipulation of configuration.
|
|
615
705
|
// Not available in IE8, Safari 5, etc.
|
|
706
|
+
|
|
707
|
+
|
|
616
708
|
if (freeze) {
|
|
617
709
|
freeze(cfg);
|
|
618
710
|
}
|
|
@@ -621,19 +713,21 @@
|
|
|
621
713
|
};
|
|
622
714
|
|
|
623
715
|
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
716
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML
|
|
717
|
+
// namespace. We need to specify them explicitly
|
|
718
|
+
// so that they don't get erroneously deleted from
|
|
719
|
+
// HTML namespace.
|
|
624
720
|
|
|
625
|
-
var
|
|
626
|
-
|
|
721
|
+
var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
627
722
|
/* Keep track of all possible SVG and MathML tags
|
|
628
723
|
* so that we can perform the namespace checks
|
|
629
724
|
* correctly. */
|
|
630
|
-
|
|
725
|
+
|
|
726
|
+
var ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
631
727
|
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
632
728
|
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
633
|
-
|
|
634
|
-
var ALL_MATHML_TAGS = addToSet({}, mathMl);
|
|
729
|
+
var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
635
730
|
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
636
|
-
|
|
637
731
|
/**
|
|
638
732
|
*
|
|
639
733
|
*
|
|
@@ -642,11 +736,11 @@
|
|
|
642
736
|
* namespace that a spec-compliant parser would never
|
|
643
737
|
* return. Return true otherwise.
|
|
644
738
|
*/
|
|
645
|
-
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
646
|
-
var parent = getParentNode(element);
|
|
647
739
|
|
|
648
|
-
|
|
740
|
+
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
741
|
+
var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode
|
|
649
742
|
// can be null. We just simulate parent in this case.
|
|
743
|
+
|
|
650
744
|
if (!parent || !parent.tagName) {
|
|
651
745
|
parent = {
|
|
652
746
|
namespaceURI: HTML_NAMESPACE,
|
|
@@ -663,17 +757,17 @@
|
|
|
663
757
|
// it should be killed.
|
|
664
758
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
665
759
|
return tagName === 'svg';
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// The only way to switch from MathML to SVG is via
|
|
760
|
+
} // The only way to switch from MathML to SVG is via
|
|
669
761
|
// svg if parent is either <annotation-xml> or MathML
|
|
670
762
|
// text integration points.
|
|
763
|
+
|
|
764
|
+
|
|
671
765
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
672
766
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// We only allow elements that are defined in SVG
|
|
767
|
+
} // We only allow elements that are defined in SVG
|
|
676
768
|
// spec. All others are disallowed in SVG namespace.
|
|
769
|
+
|
|
770
|
+
|
|
677
771
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
678
772
|
}
|
|
679
773
|
|
|
@@ -683,16 +777,16 @@
|
|
|
683
777
|
// it should be killed.
|
|
684
778
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
685
779
|
return tagName === 'math';
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// The only way to switch from SVG to MathML is via
|
|
780
|
+
} // The only way to switch from SVG to MathML is via
|
|
689
781
|
// <math> and HTML integration points
|
|
782
|
+
|
|
783
|
+
|
|
690
784
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
691
785
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// We only allow elements that are defined in MathML
|
|
786
|
+
} // We only allow elements that are defined in MathML
|
|
695
787
|
// spec. All others are disallowed in MathML namespace.
|
|
788
|
+
|
|
789
|
+
|
|
696
790
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
697
791
|
}
|
|
698
792
|
|
|
@@ -706,32 +800,30 @@
|
|
|
706
800
|
|
|
707
801
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
708
802
|
return false;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// Certain elements are allowed in both SVG and HTML
|
|
712
|
-
// namespace. We need to specify them explicitly
|
|
713
|
-
// so that they don't get erronously deleted from
|
|
714
|
-
// HTML namespace.
|
|
715
|
-
var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
716
|
-
|
|
717
|
-
// We disallow tags that are specific for MathML
|
|
803
|
+
} // We disallow tags that are specific for MathML
|
|
718
804
|
// or SVG and should never appear in HTML namespace
|
|
719
|
-
return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
720
|
-
}
|
|
721
805
|
|
|
722
|
-
|
|
806
|
+
|
|
807
|
+
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
808
|
+
} // The code should never reach this place (this means
|
|
723
809
|
// that the element somehow got namespace that is not
|
|
724
810
|
// HTML, SVG or MathML). Return false just in case.
|
|
811
|
+
|
|
812
|
+
|
|
725
813
|
return false;
|
|
726
814
|
};
|
|
727
|
-
|
|
728
815
|
/**
|
|
729
816
|
* _forceRemove
|
|
730
817
|
*
|
|
731
818
|
* @param {Node} node a DOM node
|
|
732
819
|
*/
|
|
820
|
+
|
|
821
|
+
|
|
733
822
|
var _forceRemove = function _forceRemove(node) {
|
|
734
|
-
arrayPush(DOMPurify.removed, {
|
|
823
|
+
arrayPush(DOMPurify.removed, {
|
|
824
|
+
element: node
|
|
825
|
+
});
|
|
826
|
+
|
|
735
827
|
try {
|
|
736
828
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
737
829
|
node.parentNode.removeChild(node);
|
|
@@ -743,13 +835,14 @@
|
|
|
743
835
|
}
|
|
744
836
|
}
|
|
745
837
|
};
|
|
746
|
-
|
|
747
838
|
/**
|
|
748
839
|
* _removeAttribute
|
|
749
840
|
*
|
|
750
841
|
* @param {String} name an Attribute name
|
|
751
842
|
* @param {Node} node a DOM node
|
|
752
843
|
*/
|
|
844
|
+
|
|
845
|
+
|
|
753
846
|
var _removeAttribute = function _removeAttribute(name, node) {
|
|
754
847
|
try {
|
|
755
848
|
arrayPush(DOMPurify.removed, {
|
|
@@ -763,9 +856,8 @@
|
|
|
763
856
|
});
|
|
764
857
|
}
|
|
765
858
|
|
|
766
|
-
node.removeAttribute(name);
|
|
859
|
+
node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes
|
|
767
860
|
|
|
768
|
-
// We void attribute values for unremovable "is"" attributes
|
|
769
861
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
770
862
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
771
863
|
try {
|
|
@@ -778,17 +870,18 @@
|
|
|
778
870
|
}
|
|
779
871
|
}
|
|
780
872
|
};
|
|
781
|
-
|
|
782
873
|
/**
|
|
783
874
|
* _initDocument
|
|
784
875
|
*
|
|
785
876
|
* @param {String} dirty a string of dirty markup
|
|
786
877
|
* @return {Document} a DOM, filled with the dirty markup
|
|
787
878
|
*/
|
|
879
|
+
|
|
880
|
+
|
|
788
881
|
var _initDocument = function _initDocument(dirty) {
|
|
789
882
|
/* Create a HTML document */
|
|
790
|
-
var doc
|
|
791
|
-
var leadingWhitespace
|
|
883
|
+
var doc;
|
|
884
|
+
var leadingWhitespace;
|
|
792
885
|
|
|
793
886
|
if (FORCE_BODY) {
|
|
794
887
|
dirty = '<remove></remove>' + dirty;
|
|
@@ -808,19 +901,21 @@
|
|
|
808
901
|
* Use the DOMParser API by default, fallback later if needs be
|
|
809
902
|
* DOMParser not work for svg when has multiple root element.
|
|
810
903
|
*/
|
|
904
|
+
|
|
811
905
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
812
906
|
try {
|
|
813
907
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
814
908
|
} catch (_) {}
|
|
815
909
|
}
|
|
816
|
-
|
|
817
910
|
/* Use createHTMLDocument in case DOMParser is not available */
|
|
911
|
+
|
|
912
|
+
|
|
818
913
|
if (!doc || !doc.documentElement) {
|
|
819
914
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
915
|
+
|
|
820
916
|
try {
|
|
821
917
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload;
|
|
822
|
-
} catch (_) {
|
|
823
|
-
// Syntax error if dirtyPayload is invalid xml
|
|
918
|
+
} catch (_) {// Syntax error if dirtyPayload is invalid xml
|
|
824
919
|
}
|
|
825
920
|
}
|
|
826
921
|
|
|
@@ -829,47 +924,49 @@
|
|
|
829
924
|
if (dirty && leadingWhitespace) {
|
|
830
925
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
831
926
|
}
|
|
832
|
-
|
|
833
927
|
/* Work on whole document or just its body */
|
|
928
|
+
|
|
929
|
+
|
|
834
930
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
835
931
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
836
932
|
}
|
|
837
933
|
|
|
838
934
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
839
935
|
};
|
|
840
|
-
|
|
841
936
|
/**
|
|
842
937
|
* _createIterator
|
|
843
938
|
*
|
|
844
939
|
* @param {Document} root document/fragment to create iterator for
|
|
845
940
|
* @return {Iterator} iterator instance
|
|
846
941
|
*/
|
|
942
|
+
|
|
943
|
+
|
|
847
944
|
var _createIterator = function _createIterator(root) {
|
|
848
|
-
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
849
|
-
// eslint-disable-next-line no-bitwise
|
|
945
|
+
return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
|
|
850
946
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
|
|
851
947
|
};
|
|
852
|
-
|
|
853
948
|
/**
|
|
854
949
|
* _isClobbered
|
|
855
950
|
*
|
|
856
951
|
* @param {Node} elm element to check for clobbering attacks
|
|
857
952
|
* @return {Boolean} true if clobbered, false if safe
|
|
858
953
|
*/
|
|
954
|
+
|
|
955
|
+
|
|
859
956
|
var _isClobbered = function _isClobbered(elm) {
|
|
860
957
|
return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function');
|
|
861
958
|
};
|
|
862
|
-
|
|
863
959
|
/**
|
|
864
960
|
* _isNode
|
|
865
961
|
*
|
|
866
962
|
* @param {Node} obj object to check whether it's a DOM node
|
|
867
963
|
* @return {Boolean} true is object is a DOM node
|
|
868
964
|
*/
|
|
965
|
+
|
|
966
|
+
|
|
869
967
|
var _isNode = function _isNode(object) {
|
|
870
|
-
return
|
|
968
|
+
return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
871
969
|
};
|
|
872
|
-
|
|
873
970
|
/**
|
|
874
971
|
* _executeHook
|
|
875
972
|
* Execute user configurable hooks
|
|
@@ -878,6 +975,8 @@
|
|
|
878
975
|
* @param {Node} currentNode node to work on with the hook
|
|
879
976
|
* @param {Object} data additional hook parameters
|
|
880
977
|
*/
|
|
978
|
+
|
|
979
|
+
|
|
881
980
|
var _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
882
981
|
if (!hooks[entryPoint]) {
|
|
883
982
|
return;
|
|
@@ -887,7 +986,6 @@
|
|
|
887
986
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
888
987
|
});
|
|
889
988
|
};
|
|
890
|
-
|
|
891
989
|
/**
|
|
892
990
|
* _sanitizeElements
|
|
893
991
|
*
|
|
@@ -898,54 +996,67 @@
|
|
|
898
996
|
* @param {Node} currentNode to check for permission to exist
|
|
899
997
|
* @return {Boolean} true if node was killed, false if left alive
|
|
900
998
|
*/
|
|
901
|
-
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
902
|
-
var content = void 0;
|
|
903
999
|
|
|
1000
|
+
|
|
1001
|
+
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1002
|
+
var content;
|
|
904
1003
|
/* Execute a hook if present */
|
|
905
|
-
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
906
1004
|
|
|
1005
|
+
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
907
1006
|
/* Check if element is clobbered or can clobber */
|
|
1007
|
+
|
|
1008
|
+
|
|
908
1009
|
if (_isClobbered(currentNode)) {
|
|
909
1010
|
_forceRemove(currentNode);
|
|
1011
|
+
|
|
910
1012
|
return true;
|
|
911
1013
|
}
|
|
912
|
-
|
|
913
1014
|
/* Check if tagname contains Unicode */
|
|
914
|
-
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
915
1018
|
_forceRemove(currentNode);
|
|
1019
|
+
|
|
916
1020
|
return true;
|
|
917
1021
|
}
|
|
918
|
-
|
|
919
1022
|
/* Now let's check the element's type and name */
|
|
920
|
-
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
921
1023
|
|
|
1024
|
+
|
|
1025
|
+
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
922
1026
|
/* Execute a hook if present */
|
|
1027
|
+
|
|
923
1028
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
924
1029
|
tagName: tagName,
|
|
925
1030
|
allowedTags: ALLOWED_TAGS
|
|
926
1031
|
});
|
|
927
|
-
|
|
928
1032
|
/* Detect mXSS attempts abusing namespace confusion */
|
|
929
|
-
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
930
1036
|
_forceRemove(currentNode);
|
|
1037
|
+
|
|
931
1038
|
return true;
|
|
932
1039
|
}
|
|
933
|
-
|
|
934
1040
|
/* Mitigate a problem with templates inside select */
|
|
1041
|
+
|
|
1042
|
+
|
|
935
1043
|
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
936
1044
|
_forceRemove(currentNode);
|
|
1045
|
+
|
|
937
1046
|
return true;
|
|
938
1047
|
}
|
|
939
|
-
|
|
940
1048
|
/* Remove element if anything forbids its presence */
|
|
1049
|
+
|
|
1050
|
+
|
|
941
1051
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
942
1052
|
/* Check if we have a custom element to handle */
|
|
943
1053
|
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
944
1054
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
945
1055
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
946
1056
|
}
|
|
947
|
-
|
|
948
1057
|
/* Keep content except for bad-listed elements */
|
|
1058
|
+
|
|
1059
|
+
|
|
949
1060
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
950
1061
|
var parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
951
1062
|
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
@@ -960,38 +1071,46 @@
|
|
|
960
1071
|
}
|
|
961
1072
|
|
|
962
1073
|
_forceRemove(currentNode);
|
|
1074
|
+
|
|
963
1075
|
return true;
|
|
964
1076
|
}
|
|
965
|
-
|
|
966
1077
|
/* Check whether element has a valid namespace */
|
|
1078
|
+
|
|
1079
|
+
|
|
967
1080
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
968
1081
|
_forceRemove(currentNode);
|
|
1082
|
+
|
|
969
1083
|
return true;
|
|
970
1084
|
}
|
|
971
1085
|
|
|
972
1086
|
if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
|
|
973
1087
|
_forceRemove(currentNode);
|
|
1088
|
+
|
|
974
1089
|
return true;
|
|
975
1090
|
}
|
|
976
|
-
|
|
977
1091
|
/* Sanitize element content to be template-safe */
|
|
1092
|
+
|
|
1093
|
+
|
|
978
1094
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
979
1095
|
/* Get the element's text content */
|
|
980
1096
|
content = currentNode.textContent;
|
|
981
|
-
content = stringReplace(content, MUSTACHE_EXPR
|
|
982
|
-
content = stringReplace(content, ERB_EXPR
|
|
1097
|
+
content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
|
|
1098
|
+
content = stringReplace(content, ERB_EXPR$1, ' ');
|
|
1099
|
+
|
|
983
1100
|
if (currentNode.textContent !== content) {
|
|
984
|
-
arrayPush(DOMPurify.removed, {
|
|
1101
|
+
arrayPush(DOMPurify.removed, {
|
|
1102
|
+
element: currentNode.cloneNode()
|
|
1103
|
+
});
|
|
985
1104
|
currentNode.textContent = content;
|
|
986
1105
|
}
|
|
987
1106
|
}
|
|
988
|
-
|
|
989
1107
|
/* Execute a hook if present */
|
|
1108
|
+
|
|
1109
|
+
|
|
990
1110
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
991
1111
|
|
|
992
1112
|
return false;
|
|
993
1113
|
};
|
|
994
|
-
|
|
995
1114
|
/**
|
|
996
1115
|
* _isValidAttribute
|
|
997
1116
|
*
|
|
@@ -1001,45 +1120,47 @@
|
|
|
1001
1120
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1002
1121
|
*/
|
|
1003
1122
|
// eslint-disable-next-line complexity
|
|
1123
|
+
|
|
1124
|
+
|
|
1004
1125
|
var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1005
1126
|
/* Make sure attribute cannot clobber */
|
|
1006
1127
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1007
1128
|
return false;
|
|
1008
1129
|
}
|
|
1009
|
-
|
|
1010
1130
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1011
1131
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1012
1132
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1013
1133
|
We don't need to check the value; it's always URI safe. */
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
|
|
1137
|
+
if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1017
1138
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1018
1139
|
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1019
|
-
_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) ||
|
|
1020
|
-
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1140
|
+
_basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1021
1141
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1022
1142
|
lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
|
|
1023
1143
|
return false;
|
|
1024
1144
|
}
|
|
1025
1145
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1026
|
-
|
|
1146
|
+
|
|
1147
|
+
} else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (!value) ; else {
|
|
1027
1148
|
return false;
|
|
1028
1149
|
}
|
|
1029
1150
|
|
|
1030
1151
|
return true;
|
|
1031
1152
|
};
|
|
1032
|
-
|
|
1033
1153
|
/**
|
|
1034
1154
|
* _basicCustomElementCheck
|
|
1035
1155
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1036
1156
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1037
1157
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1038
1158
|
*/
|
|
1159
|
+
|
|
1160
|
+
|
|
1039
1161
|
var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1040
1162
|
return tagName.indexOf('-') > 0;
|
|
1041
1163
|
};
|
|
1042
|
-
|
|
1043
1164
|
/**
|
|
1044
1165
|
* _sanitizeAttributes
|
|
1045
1166
|
*
|
|
@@ -1050,16 +1171,18 @@
|
|
|
1050
1171
|
*
|
|
1051
1172
|
* @param {Node} currentNode to sanitize
|
|
1052
1173
|
*/
|
|
1174
|
+
|
|
1175
|
+
|
|
1053
1176
|
var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1054
|
-
var attr
|
|
1055
|
-
var value
|
|
1056
|
-
var lcName
|
|
1057
|
-
var l
|
|
1177
|
+
var attr;
|
|
1178
|
+
var value;
|
|
1179
|
+
var lcName;
|
|
1180
|
+
var l;
|
|
1058
1181
|
/* Execute a hook if present */
|
|
1182
|
+
|
|
1059
1183
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1060
1184
|
|
|
1061
1185
|
var attributes = currentNode.attributes;
|
|
1062
|
-
|
|
1063
1186
|
/* Check if we have attributes; if not we might have a text node */
|
|
1064
1187
|
|
|
1065
1188
|
if (!attributes) {
|
|
@@ -1073,56 +1196,66 @@
|
|
|
1073
1196
|
allowedAttributes: ALLOWED_ATTR
|
|
1074
1197
|
};
|
|
1075
1198
|
l = attributes.length;
|
|
1076
|
-
|
|
1077
1199
|
/* Go backwards over all attributes; safely remove bad ones */
|
|
1200
|
+
|
|
1078
1201
|
while (l--) {
|
|
1079
1202
|
attr = attributes[l];
|
|
1080
1203
|
var _attr = attr,
|
|
1081
1204
|
name = _attr.name,
|
|
1082
1205
|
namespaceURI = _attr.namespaceURI;
|
|
1083
|
-
|
|
1084
|
-
value = stringTrim(attr.value);
|
|
1206
|
+
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1085
1207
|
lcName = transformCaseFunc(name);
|
|
1086
|
-
|
|
1087
1208
|
/* Execute a hook if present */
|
|
1209
|
+
|
|
1088
1210
|
hookEvent.attrName = lcName;
|
|
1089
1211
|
hookEvent.attrValue = value;
|
|
1090
1212
|
hookEvent.keepAttr = true;
|
|
1091
1213
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1214
|
+
|
|
1092
1215
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1216
|
+
|
|
1093
1217
|
value = hookEvent.attrValue;
|
|
1094
1218
|
/* Did the hooks approve of the attribute? */
|
|
1219
|
+
|
|
1095
1220
|
if (hookEvent.forceKeepAttr) {
|
|
1096
1221
|
continue;
|
|
1097
1222
|
}
|
|
1098
|
-
|
|
1099
1223
|
/* Remove attribute */
|
|
1100
|
-
_removeAttribute(name, currentNode);
|
|
1101
1224
|
|
|
1225
|
+
|
|
1226
|
+
_removeAttribute(name, currentNode);
|
|
1102
1227
|
/* Did the hooks approve of the attribute? */
|
|
1228
|
+
|
|
1229
|
+
|
|
1103
1230
|
if (!hookEvent.keepAttr) {
|
|
1104
1231
|
continue;
|
|
1105
1232
|
}
|
|
1106
|
-
|
|
1107
1233
|
/* Work around a security issue in jQuery 3.0 */
|
|
1234
|
+
|
|
1235
|
+
|
|
1108
1236
|
if (regExpTest(/\/>/i, value)) {
|
|
1109
1237
|
_removeAttribute(name, currentNode);
|
|
1238
|
+
|
|
1110
1239
|
continue;
|
|
1111
1240
|
}
|
|
1112
|
-
|
|
1113
1241
|
/* Sanitize attribute content to be template-safe */
|
|
1242
|
+
|
|
1243
|
+
|
|
1114
1244
|
if (SAFE_FOR_TEMPLATES) {
|
|
1115
|
-
value = stringReplace(value, MUSTACHE_EXPR
|
|
1116
|
-
value = stringReplace(value, ERB_EXPR
|
|
1245
|
+
value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
|
|
1246
|
+
value = stringReplace(value, ERB_EXPR$1, ' ');
|
|
1117
1247
|
}
|
|
1118
|
-
|
|
1119
1248
|
/* Is `value` valid for this attribute? */
|
|
1249
|
+
|
|
1250
|
+
|
|
1120
1251
|
var lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1252
|
+
|
|
1121
1253
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1122
1254
|
continue;
|
|
1123
1255
|
}
|
|
1124
|
-
|
|
1125
1256
|
/* Handle invalid data-* attribute set by try-catching it */
|
|
1257
|
+
|
|
1258
|
+
|
|
1126
1259
|
try {
|
|
1127
1260
|
if (namespaceURI) {
|
|
1128
1261
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1134,45 +1267,52 @@
|
|
|
1134
1267
|
arrayPop(DOMPurify.removed);
|
|
1135
1268
|
} catch (_) {}
|
|
1136
1269
|
}
|
|
1137
|
-
|
|
1138
1270
|
/* Execute a hook if present */
|
|
1271
|
+
|
|
1272
|
+
|
|
1139
1273
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1140
1274
|
};
|
|
1141
|
-
|
|
1142
1275
|
/**
|
|
1143
1276
|
* _sanitizeShadowDOM
|
|
1144
1277
|
*
|
|
1145
1278
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1146
1279
|
*/
|
|
1280
|
+
|
|
1281
|
+
|
|
1147
1282
|
var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1148
|
-
var shadowNode
|
|
1149
|
-
var shadowIterator = _createIterator(fragment);
|
|
1283
|
+
var shadowNode;
|
|
1150
1284
|
|
|
1285
|
+
var shadowIterator = _createIterator(fragment);
|
|
1151
1286
|
/* Execute a hook if present */
|
|
1287
|
+
|
|
1288
|
+
|
|
1152
1289
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1153
1290
|
|
|
1154
1291
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1155
1292
|
/* Execute a hook if present */
|
|
1156
1293
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1157
|
-
|
|
1158
1294
|
/* Sanitize tags and elements */
|
|
1295
|
+
|
|
1296
|
+
|
|
1159
1297
|
if (_sanitizeElements(shadowNode)) {
|
|
1160
1298
|
continue;
|
|
1161
1299
|
}
|
|
1162
|
-
|
|
1163
1300
|
/* Deep shadow DOM detected */
|
|
1301
|
+
|
|
1302
|
+
|
|
1164
1303
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1165
1304
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1166
1305
|
}
|
|
1167
|
-
|
|
1168
1306
|
/* Check attributes, sanitize if necessary */
|
|
1307
|
+
|
|
1308
|
+
|
|
1169
1309
|
_sanitizeAttributes(shadowNode);
|
|
1170
1310
|
}
|
|
1171
|
-
|
|
1172
1311
|
/* Execute a hook if present */
|
|
1312
|
+
|
|
1313
|
+
|
|
1173
1314
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1174
1315
|
};
|
|
1175
|
-
|
|
1176
1316
|
/**
|
|
1177
1317
|
* Sanitize
|
|
1178
1318
|
* Public method providing core sanitation functionality
|
|
@@ -1181,34 +1321,41 @@
|
|
|
1181
1321
|
* @param {Object} configuration object
|
|
1182
1322
|
*/
|
|
1183
1323
|
// eslint-disable-next-line complexity
|
|
1324
|
+
|
|
1325
|
+
|
|
1184
1326
|
DOMPurify.sanitize = function (dirty, cfg) {
|
|
1185
|
-
var body
|
|
1186
|
-
var importedNode
|
|
1187
|
-
var currentNode
|
|
1188
|
-
var oldNode
|
|
1189
|
-
var returnNode
|
|
1327
|
+
var body;
|
|
1328
|
+
var importedNode;
|
|
1329
|
+
var currentNode;
|
|
1330
|
+
var oldNode;
|
|
1331
|
+
var returnNode;
|
|
1190
1332
|
/* Make sure we have a string to sanitize.
|
|
1191
1333
|
DO NOT return early, as this will return the wrong type if
|
|
1192
1334
|
the user has requested a DOM object rather than a string */
|
|
1335
|
+
|
|
1193
1336
|
IS_EMPTY_INPUT = !dirty;
|
|
1337
|
+
|
|
1194
1338
|
if (IS_EMPTY_INPUT) {
|
|
1195
1339
|
dirty = '<!-->';
|
|
1196
1340
|
}
|
|
1197
|
-
|
|
1198
1341
|
/* Stringify, in case dirty is an object */
|
|
1342
|
+
|
|
1343
|
+
|
|
1199
1344
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1200
1345
|
// eslint-disable-next-line no-negated-condition
|
|
1201
1346
|
if (typeof dirty.toString !== 'function') {
|
|
1202
1347
|
throw typeErrorCreate('toString is not a function');
|
|
1203
1348
|
} else {
|
|
1204
1349
|
dirty = dirty.toString();
|
|
1350
|
+
|
|
1205
1351
|
if (typeof dirty !== 'string') {
|
|
1206
1352
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1207
1353
|
}
|
|
1208
1354
|
}
|
|
1209
1355
|
}
|
|
1210
|
-
|
|
1211
1356
|
/* Check we can run. Otherwise fall back or ignore */
|
|
1357
|
+
|
|
1358
|
+
|
|
1212
1359
|
if (!DOMPurify.isSupported) {
|
|
1213
1360
|
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1214
1361
|
if (typeof dirty === 'string') {
|
|
@@ -1222,16 +1369,18 @@
|
|
|
1222
1369
|
|
|
1223
1370
|
return dirty;
|
|
1224
1371
|
}
|
|
1225
|
-
|
|
1226
1372
|
/* Assign config vars */
|
|
1373
|
+
|
|
1374
|
+
|
|
1227
1375
|
if (!SET_CONFIG) {
|
|
1228
1376
|
_parseConfig(cfg);
|
|
1229
1377
|
}
|
|
1230
|
-
|
|
1231
1378
|
/* Clean up removed elements */
|
|
1232
|
-
DOMPurify.removed = [];
|
|
1233
1379
|
|
|
1380
|
+
|
|
1381
|
+
DOMPurify.removed = [];
|
|
1234
1382
|
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1383
|
+
|
|
1235
1384
|
if (typeof dirty === 'string') {
|
|
1236
1385
|
IN_PLACE = false;
|
|
1237
1386
|
}
|
|
@@ -1240,6 +1389,7 @@
|
|
|
1240
1389
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1241
1390
|
if (dirty.nodeName) {
|
|
1242
1391
|
var tagName = transformCaseFunc(dirty.nodeName);
|
|
1392
|
+
|
|
1243
1393
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1244
1394
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1245
1395
|
}
|
|
@@ -1249,6 +1399,7 @@
|
|
|
1249
1399
|
elements being stripped by the parser */
|
|
1250
1400
|
body = _initDocument('<!---->');
|
|
1251
1401
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1402
|
+
|
|
1252
1403
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1253
1404
|
/* Node is already a body, use as is */
|
|
1254
1405
|
body = importedNode;
|
|
@@ -1260,60 +1411,67 @@
|
|
|
1260
1411
|
}
|
|
1261
1412
|
} else {
|
|
1262
1413
|
/* Exit directly if we have nothing to do */
|
|
1263
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1264
|
-
// eslint-disable-next-line unicorn/prefer-includes
|
|
1414
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
|
|
1265
1415
|
dirty.indexOf('<') === -1) {
|
|
1266
1416
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1267
1417
|
}
|
|
1268
|
-
|
|
1269
1418
|
/* Initialize the document to work on */
|
|
1270
|
-
body = _initDocument(dirty);
|
|
1271
1419
|
|
|
1420
|
+
|
|
1421
|
+
body = _initDocument(dirty);
|
|
1272
1422
|
/* Check we have a DOM node from the data */
|
|
1423
|
+
|
|
1273
1424
|
if (!body) {
|
|
1274
1425
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1275
1426
|
}
|
|
1276
1427
|
}
|
|
1277
|
-
|
|
1278
1428
|
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1429
|
+
|
|
1430
|
+
|
|
1279
1431
|
if (body && FORCE_BODY) {
|
|
1280
1432
|
_forceRemove(body.firstChild);
|
|
1281
1433
|
}
|
|
1282
|
-
|
|
1283
1434
|
/* Get node iterator */
|
|
1284
|
-
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1285
1435
|
|
|
1436
|
+
|
|
1437
|
+
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1286
1438
|
/* Now start iterating over the created document */
|
|
1439
|
+
|
|
1440
|
+
|
|
1287
1441
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1288
1442
|
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1289
1443
|
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1290
1444
|
continue;
|
|
1291
1445
|
}
|
|
1292
|
-
|
|
1293
1446
|
/* Sanitize tags and elements */
|
|
1447
|
+
|
|
1448
|
+
|
|
1294
1449
|
if (_sanitizeElements(currentNode)) {
|
|
1295
1450
|
continue;
|
|
1296
1451
|
}
|
|
1297
|
-
|
|
1298
1452
|
/* Shadow DOM detected, sanitize it */
|
|
1453
|
+
|
|
1454
|
+
|
|
1299
1455
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1300
1456
|
_sanitizeShadowDOM(currentNode.content);
|
|
1301
1457
|
}
|
|
1302
|
-
|
|
1303
1458
|
/* Check attributes, sanitize if necessary */
|
|
1459
|
+
|
|
1460
|
+
|
|
1304
1461
|
_sanitizeAttributes(currentNode);
|
|
1305
1462
|
|
|
1306
1463
|
oldNode = currentNode;
|
|
1307
1464
|
}
|
|
1308
1465
|
|
|
1309
1466
|
oldNode = null;
|
|
1310
|
-
|
|
1311
1467
|
/* If we sanitized `dirty` in-place, return it. */
|
|
1468
|
+
|
|
1312
1469
|
if (IN_PLACE) {
|
|
1313
1470
|
return dirty;
|
|
1314
1471
|
}
|
|
1315
|
-
|
|
1316
1472
|
/* Return sanitized string or DOM */
|
|
1473
|
+
|
|
1474
|
+
|
|
1317
1475
|
if (RETURN_DOM) {
|
|
1318
1476
|
if (RETURN_DOM_FRAGMENT) {
|
|
1319
1477
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
@@ -1341,42 +1499,45 @@
|
|
|
1341
1499
|
}
|
|
1342
1500
|
|
|
1343
1501
|
var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1344
|
-
|
|
1345
1502
|
/* Serialize doctype if allowed */
|
|
1503
|
+
|
|
1346
1504
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1347
1505
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1348
1506
|
}
|
|
1349
|
-
|
|
1350
1507
|
/* Sanitize final string template-safe */
|
|
1508
|
+
|
|
1509
|
+
|
|
1351
1510
|
if (SAFE_FOR_TEMPLATES) {
|
|
1352
|
-
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR
|
|
1353
|
-
serializedHTML = stringReplace(serializedHTML, ERB_EXPR
|
|
1511
|
+
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
|
|
1512
|
+
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
|
|
1354
1513
|
}
|
|
1355
1514
|
|
|
1356
1515
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1357
1516
|
};
|
|
1358
|
-
|
|
1359
1517
|
/**
|
|
1360
1518
|
* Public method to set the configuration once
|
|
1361
1519
|
* setConfig
|
|
1362
1520
|
*
|
|
1363
1521
|
* @param {Object} cfg configuration object
|
|
1364
1522
|
*/
|
|
1523
|
+
|
|
1524
|
+
|
|
1365
1525
|
DOMPurify.setConfig = function (cfg) {
|
|
1366
1526
|
_parseConfig(cfg);
|
|
1527
|
+
|
|
1367
1528
|
SET_CONFIG = true;
|
|
1368
1529
|
};
|
|
1369
|
-
|
|
1370
1530
|
/**
|
|
1371
1531
|
* Public method to remove the configuration
|
|
1372
1532
|
* clearConfig
|
|
1373
1533
|
*
|
|
1374
1534
|
*/
|
|
1535
|
+
|
|
1536
|
+
|
|
1375
1537
|
DOMPurify.clearConfig = function () {
|
|
1376
1538
|
CONFIG = null;
|
|
1377
1539
|
SET_CONFIG = false;
|
|
1378
1540
|
};
|
|
1379
|
-
|
|
1380
1541
|
/**
|
|
1381
1542
|
* Public method to check if an attribute value is valid.
|
|
1382
1543
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
@@ -1387,6 +1548,8 @@
|
|
|
1387
1548
|
* @param {string} value Attribute value.
|
|
1388
1549
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1389
1550
|
*/
|
|
1551
|
+
|
|
1552
|
+
|
|
1390
1553
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1391
1554
|
/* Initialize shared config vars if necessary. */
|
|
1392
1555
|
if (!CONFIG) {
|
|
@@ -1397,7 +1560,6 @@
|
|
|
1397
1560
|
var lcName = transformCaseFunc(attr);
|
|
1398
1561
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1399
1562
|
};
|
|
1400
|
-
|
|
1401
1563
|
/**
|
|
1402
1564
|
* AddHook
|
|
1403
1565
|
* Public method to add DOMPurify hooks
|
|
@@ -1405,6 +1567,8 @@
|
|
|
1405
1567
|
* @param {String} entryPoint entry point for the hook to add
|
|
1406
1568
|
* @param {Function} hookFunction function to execute
|
|
1407
1569
|
*/
|
|
1570
|
+
|
|
1571
|
+
|
|
1408
1572
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1409
1573
|
if (typeof hookFunction !== 'function') {
|
|
1410
1574
|
return;
|
|
@@ -1413,37 +1577,41 @@
|
|
|
1413
1577
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1414
1578
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1415
1579
|
};
|
|
1416
|
-
|
|
1417
1580
|
/**
|
|
1418
1581
|
* RemoveHook
|
|
1419
1582
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
1420
1583
|
* (pops it from the stack of hooks if more are present)
|
|
1421
1584
|
*
|
|
1422
1585
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1586
|
+
* @return {Function} removed(popped) hook
|
|
1423
1587
|
*/
|
|
1588
|
+
|
|
1589
|
+
|
|
1424
1590
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1425
1591
|
if (hooks[entryPoint]) {
|
|
1426
|
-
arrayPop(hooks[entryPoint]);
|
|
1592
|
+
return arrayPop(hooks[entryPoint]);
|
|
1427
1593
|
}
|
|
1428
1594
|
};
|
|
1429
|
-
|
|
1430
1595
|
/**
|
|
1431
1596
|
* RemoveHooks
|
|
1432
1597
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1433
1598
|
*
|
|
1434
1599
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1435
1600
|
*/
|
|
1601
|
+
|
|
1602
|
+
|
|
1436
1603
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1437
1604
|
if (hooks[entryPoint]) {
|
|
1438
1605
|
hooks[entryPoint] = [];
|
|
1439
1606
|
}
|
|
1440
1607
|
};
|
|
1441
|
-
|
|
1442
1608
|
/**
|
|
1443
1609
|
* RemoveAllHooks
|
|
1444
1610
|
* Public method to remove all DOMPurify hooks
|
|
1445
1611
|
*
|
|
1446
1612
|
*/
|
|
1613
|
+
|
|
1614
|
+
|
|
1447
1615
|
DOMPurify.removeAllHooks = function () {
|
|
1448
1616
|
hooks = {};
|
|
1449
1617
|
};
|