dompurify 2.4.9 → 2.5.1
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/LICENSE +1 -1
- package/README.md +2 -2
- package/dist/purify.cjs.js +272 -420
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +272 -420
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +272 -420
- 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 +1 -1
package/dist/purify.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @license DOMPurify 2.
|
|
1
|
+
/*! @license DOMPurify 2.5.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.1/LICENSE */
|
|
2
2
|
|
|
3
3
|
(function (global, factory) {
|
|
4
4
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
@@ -15,21 +15,17 @@
|
|
|
15
15
|
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
16
16
|
}, _typeof(obj);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
18
|
function _setPrototypeOf(o, p) {
|
|
20
19
|
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
21
20
|
o.__proto__ = p;
|
|
22
21
|
return o;
|
|
23
22
|
};
|
|
24
|
-
|
|
25
23
|
return _setPrototypeOf(o, p);
|
|
26
24
|
}
|
|
27
|
-
|
|
28
25
|
function _isNativeReflectConstruct() {
|
|
29
26
|
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
30
27
|
if (Reflect.construct.sham) return false;
|
|
31
28
|
if (typeof Proxy === "function") return true;
|
|
32
|
-
|
|
33
29
|
try {
|
|
34
30
|
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
|
35
31
|
return true;
|
|
@@ -37,7 +33,6 @@
|
|
|
37
33
|
return false;
|
|
38
34
|
}
|
|
39
35
|
}
|
|
40
|
-
|
|
41
36
|
function _construct(Parent, args, Class) {
|
|
42
37
|
if (_isNativeReflectConstruct()) {
|
|
43
38
|
_construct = Reflect.construct;
|
|
@@ -51,22 +46,17 @@
|
|
|
51
46
|
return instance;
|
|
52
47
|
};
|
|
53
48
|
}
|
|
54
|
-
|
|
55
49
|
return _construct.apply(null, arguments);
|
|
56
50
|
}
|
|
57
|
-
|
|
58
51
|
function _toConsumableArray(arr) {
|
|
59
52
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
60
53
|
}
|
|
61
|
-
|
|
62
54
|
function _arrayWithoutHoles(arr) {
|
|
63
55
|
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
64
56
|
}
|
|
65
|
-
|
|
66
57
|
function _iterableToArray(iter) {
|
|
67
58
|
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
68
59
|
}
|
|
69
|
-
|
|
70
60
|
function _unsupportedIterableToArray(o, minLen) {
|
|
71
61
|
if (!o) return;
|
|
72
62
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -75,56 +65,46 @@
|
|
|
75
65
|
if (n === "Map" || n === "Set") return Array.from(o);
|
|
76
66
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
77
67
|
}
|
|
78
|
-
|
|
79
68
|
function _arrayLikeToArray(arr, len) {
|
|
80
69
|
if (len == null || len > arr.length) len = arr.length;
|
|
81
|
-
|
|
82
70
|
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
|
83
|
-
|
|
84
71
|
return arr2;
|
|
85
72
|
}
|
|
86
|
-
|
|
87
73
|
function _nonIterableSpread() {
|
|
88
74
|
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
75
|
}
|
|
90
76
|
|
|
91
77
|
var hasOwnProperty = Object.hasOwnProperty,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
setPrototypeOf = Object.setPrototypeOf,
|
|
79
|
+
isFrozen = Object.isFrozen,
|
|
80
|
+
getPrototypeOf = Object.getPrototypeOf,
|
|
81
|
+
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
96
82
|
var freeze = Object.freeze,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
seal = Object.seal,
|
|
84
|
+
create = Object.create; // eslint-disable-line import/no-mutable-exports
|
|
100
85
|
var _ref = typeof Reflect !== 'undefined' && Reflect,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
86
|
+
apply = _ref.apply,
|
|
87
|
+
construct = _ref.construct;
|
|
104
88
|
if (!apply) {
|
|
105
89
|
apply = function apply(fun, thisValue, args) {
|
|
106
90
|
return fun.apply(thisValue, args);
|
|
107
91
|
};
|
|
108
92
|
}
|
|
109
|
-
|
|
110
93
|
if (!freeze) {
|
|
111
94
|
freeze = function freeze(x) {
|
|
112
95
|
return x;
|
|
113
96
|
};
|
|
114
97
|
}
|
|
115
|
-
|
|
116
98
|
if (!seal) {
|
|
117
99
|
seal = function seal(x) {
|
|
118
100
|
return x;
|
|
119
101
|
};
|
|
120
102
|
}
|
|
121
|
-
|
|
122
103
|
if (!construct) {
|
|
123
104
|
construct = function construct(Func, args) {
|
|
124
105
|
return _construct(Func, _toConsumableArray(args));
|
|
125
106
|
};
|
|
126
107
|
}
|
|
127
|
-
|
|
128
108
|
var arrayForEach = unapply(Array.prototype.forEach);
|
|
129
109
|
var arrayPop = unapply(Array.prototype.pop);
|
|
130
110
|
var arrayPush = unapply(Array.prototype.push);
|
|
@@ -141,7 +121,6 @@
|
|
|
141
121
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
142
122
|
args[_key - 1] = arguments[_key];
|
|
143
123
|
}
|
|
144
|
-
|
|
145
124
|
return apply(func, thisArg, args);
|
|
146
125
|
};
|
|
147
126
|
}
|
|
@@ -150,103 +129,89 @@
|
|
|
150
129
|
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
151
130
|
args[_key2] = arguments[_key2];
|
|
152
131
|
}
|
|
153
|
-
|
|
154
132
|
return construct(func, args);
|
|
155
133
|
};
|
|
156
134
|
}
|
|
157
|
-
/* Add properties to a lookup table */
|
|
158
135
|
|
|
136
|
+
/* Add properties to a lookup table */
|
|
159
137
|
function addToSet(set, array, transformCaseFunc) {
|
|
160
138
|
var _transformCaseFunc;
|
|
161
|
-
|
|
162
139
|
transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
|
|
163
|
-
|
|
164
140
|
if (setPrototypeOf) {
|
|
165
141
|
// Make 'in' and truthy checks like Boolean(set.constructor)
|
|
166
142
|
// independent of any properties defined on Object.prototype.
|
|
167
143
|
// Prevent prototype setters from intercepting set as a this value.
|
|
168
144
|
setPrototypeOf(set, null);
|
|
169
145
|
}
|
|
170
|
-
|
|
171
146
|
var l = array.length;
|
|
172
|
-
|
|
173
147
|
while (l--) {
|
|
174
148
|
var element = array[l];
|
|
175
|
-
|
|
176
149
|
if (typeof element === 'string') {
|
|
177
150
|
var lcElement = transformCaseFunc(element);
|
|
178
|
-
|
|
179
151
|
if (lcElement !== element) {
|
|
180
152
|
// Config presets (e.g. tags.js, attrs.js) are immutable.
|
|
181
153
|
if (!isFrozen(array)) {
|
|
182
154
|
array[l] = lcElement;
|
|
183
155
|
}
|
|
184
|
-
|
|
185
156
|
element = lcElement;
|
|
186
157
|
}
|
|
187
158
|
}
|
|
188
|
-
|
|
189
159
|
set[element] = true;
|
|
190
160
|
}
|
|
191
|
-
|
|
192
161
|
return set;
|
|
193
162
|
}
|
|
194
|
-
/* Shallow clone an object */
|
|
195
163
|
|
|
164
|
+
/* Shallow clone an object */
|
|
196
165
|
function clone(object) {
|
|
197
166
|
var newObject = create(null);
|
|
198
167
|
var property;
|
|
199
|
-
|
|
200
168
|
for (property in object) {
|
|
201
169
|
if (apply(hasOwnProperty, object, [property]) === true) {
|
|
202
170
|
newObject[property] = object[property];
|
|
203
171
|
}
|
|
204
172
|
}
|
|
205
|
-
|
|
206
173
|
return newObject;
|
|
207
174
|
}
|
|
175
|
+
|
|
208
176
|
/* IE10 doesn't support __lookupGetter__ so lets'
|
|
209
177
|
* simulate it. It also automatically checks
|
|
210
178
|
* if the prop is function or getter and behaves
|
|
211
179
|
* accordingly. */
|
|
212
|
-
|
|
213
180
|
function lookupGetter(object, prop) {
|
|
214
181
|
while (object !== null) {
|
|
215
182
|
var desc = getOwnPropertyDescriptor(object, prop);
|
|
216
|
-
|
|
217
183
|
if (desc) {
|
|
218
184
|
if (desc.get) {
|
|
219
185
|
return unapply(desc.get);
|
|
220
186
|
}
|
|
221
|
-
|
|
222
187
|
if (typeof desc.value === 'function') {
|
|
223
188
|
return unapply(desc.value);
|
|
224
189
|
}
|
|
225
190
|
}
|
|
226
|
-
|
|
227
191
|
object = getPrototypeOf(object);
|
|
228
192
|
}
|
|
229
|
-
|
|
230
193
|
function fallbackValue(element) {
|
|
231
194
|
console.warn('fallback value for', element);
|
|
232
195
|
return null;
|
|
233
196
|
}
|
|
234
|
-
|
|
235
197
|
return fallbackValue;
|
|
236
198
|
}
|
|
237
199
|
|
|
238
|
-
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']);
|
|
200
|
+
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']);
|
|
239
201
|
|
|
202
|
+
// SVG
|
|
240
203
|
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']);
|
|
241
|
-
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']);
|
|
204
|
+
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']);
|
|
205
|
+
|
|
206
|
+
// List of SVG elements that are disallowed by default.
|
|
242
207
|
// We still need to know them so that we can do namespace
|
|
243
208
|
// checks properly in case one wants to add them to
|
|
244
209
|
// allow-list.
|
|
245
|
-
|
|
246
210
|
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']);
|
|
247
|
-
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']);
|
|
248
|
-
// even those that we disallow by default.
|
|
211
|
+
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']);
|
|
249
212
|
|
|
213
|
+
// Similarly to SVG, we want to know all MathML elements,
|
|
214
|
+
// even those that we disallow by default.
|
|
250
215
|
var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
|
|
251
216
|
var text = freeze(['#text']);
|
|
252
217
|
|
|
@@ -255,14 +220,12 @@
|
|
|
255
220
|
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']);
|
|
256
221
|
var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
257
222
|
|
|
223
|
+
// eslint-disable-next-line unicorn/better-regex
|
|
258
224
|
var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
|
|
259
|
-
|
|
260
225
|
var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
|
|
261
226
|
var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm);
|
|
262
227
|
var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
|
|
263
|
-
|
|
264
228
|
var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
|
|
265
|
-
|
|
266
229
|
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
|
|
267
230
|
);
|
|
268
231
|
var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
|
|
@@ -274,6 +237,7 @@
|
|
|
274
237
|
var getGlobal = function getGlobal() {
|
|
275
238
|
return typeof window === 'undefined' ? null : window;
|
|
276
239
|
};
|
|
240
|
+
|
|
277
241
|
/**
|
|
278
242
|
* Creates a no-op policy for internal use only.
|
|
279
243
|
* Don't export this function outside this module!
|
|
@@ -282,25 +246,20 @@
|
|
|
282
246
|
* @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
|
|
283
247
|
* are not supported).
|
|
284
248
|
*/
|
|
285
|
-
|
|
286
|
-
|
|
287
249
|
var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
|
|
288
250
|
if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
|
|
289
251
|
return null;
|
|
290
|
-
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Allow the callers to control the unique policy name
|
|
291
255
|
// by adding a data-tt-policy-suffix to the script element with the DOMPurify.
|
|
292
256
|
// Policy creation with duplicate names throws in Trusted Types.
|
|
293
|
-
|
|
294
|
-
|
|
295
257
|
var suffix = null;
|
|
296
258
|
var ATTR_NAME = 'data-tt-policy-suffix';
|
|
297
|
-
|
|
298
259
|
if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
|
|
299
260
|
suffix = document.currentScript.getAttribute(ATTR_NAME);
|
|
300
261
|
}
|
|
301
|
-
|
|
302
262
|
var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
|
|
303
|
-
|
|
304
263
|
try {
|
|
305
264
|
return trustedTypes.createPolicy(policyName, {
|
|
306
265
|
createHTML: function createHTML(html) {
|
|
@@ -318,115 +277,106 @@
|
|
|
318
277
|
return null;
|
|
319
278
|
}
|
|
320
279
|
};
|
|
321
|
-
|
|
322
280
|
function createDOMPurify() {
|
|
323
281
|
var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
324
|
-
|
|
325
282
|
var DOMPurify = function DOMPurify(root) {
|
|
326
283
|
return createDOMPurify(root);
|
|
327
284
|
};
|
|
285
|
+
|
|
328
286
|
/**
|
|
329
287
|
* Version label, exposed for easier checks
|
|
330
288
|
* if DOMPurify is up to date or not
|
|
331
289
|
*/
|
|
290
|
+
DOMPurify.version = '2.5.1';
|
|
332
291
|
|
|
333
|
-
|
|
334
|
-
DOMPurify.version = '2.4.9';
|
|
335
292
|
/**
|
|
336
293
|
* Array of elements that DOMPurify removed during sanitation.
|
|
337
294
|
* Empty if nothing was removed.
|
|
338
295
|
*/
|
|
339
|
-
|
|
340
296
|
DOMPurify.removed = [];
|
|
341
|
-
|
|
342
297
|
if (!window || !window.document || window.document.nodeType !== 9) {
|
|
343
298
|
// Not running in a browser, provide a factory function
|
|
344
299
|
// so that you can pass your own Window
|
|
345
300
|
DOMPurify.isSupported = false;
|
|
346
301
|
return DOMPurify;
|
|
347
302
|
}
|
|
348
|
-
|
|
349
303
|
var originalDocument = window.document;
|
|
350
304
|
var document = window.document;
|
|
351
305
|
var DocumentFragment = window.DocumentFragment,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
306
|
+
HTMLTemplateElement = window.HTMLTemplateElement,
|
|
307
|
+
Node = window.Node,
|
|
308
|
+
Element = window.Element,
|
|
309
|
+
NodeFilter = window.NodeFilter,
|
|
310
|
+
_window$NamedNodeMap = window.NamedNodeMap,
|
|
311
|
+
NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
|
|
312
|
+
HTMLFormElement = window.HTMLFormElement,
|
|
313
|
+
DOMParser = window.DOMParser,
|
|
314
|
+
trustedTypes = window.trustedTypes;
|
|
361
315
|
var ElementPrototype = Element.prototype;
|
|
362
316
|
var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
|
|
363
317
|
var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
|
|
364
318
|
var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
|
|
365
|
-
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
319
|
+
var getParentNode = lookupGetter(ElementPrototype, 'parentNode');
|
|
320
|
+
|
|
321
|
+
// As per issue #47, the web-components registry is inherited by a
|
|
366
322
|
// new document created via createHTMLDocument. As per the spec
|
|
367
323
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
368
324
|
// a new empty registry is used when creating a template contents owner
|
|
369
325
|
// document, so we use that as our parent document to ensure nothing
|
|
370
326
|
// is inherited.
|
|
371
|
-
|
|
372
327
|
if (typeof HTMLTemplateElement === 'function') {
|
|
373
328
|
var template = document.createElement('template');
|
|
374
|
-
|
|
375
329
|
if (template.content && template.content.ownerDocument) {
|
|
376
330
|
document = template.content.ownerDocument;
|
|
377
331
|
}
|
|
378
332
|
}
|
|
379
|
-
|
|
380
333
|
var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
|
|
381
|
-
|
|
382
334
|
var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : '';
|
|
383
335
|
var _document = document,
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
336
|
+
implementation = _document.implementation,
|
|
337
|
+
createNodeIterator = _document.createNodeIterator,
|
|
338
|
+
createDocumentFragment = _document.createDocumentFragment,
|
|
339
|
+
getElementsByTagName = _document.getElementsByTagName;
|
|
388
340
|
var importNode = originalDocument.importNode;
|
|
389
341
|
var documentMode = {};
|
|
390
|
-
|
|
391
342
|
try {
|
|
392
343
|
documentMode = clone(document).documentMode ? document.documentMode : {};
|
|
393
344
|
} catch (_) {}
|
|
394
|
-
|
|
395
345
|
var hooks = {};
|
|
346
|
+
|
|
396
347
|
/**
|
|
397
348
|
* Expose whether this browser supports running the full DOMPurify.
|
|
398
349
|
*/
|
|
399
|
-
|
|
400
350
|
DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined && documentMode !== 9;
|
|
401
351
|
var MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
352
|
+
ERB_EXPR$1 = ERB_EXPR,
|
|
353
|
+
TMPLIT_EXPR$1 = TMPLIT_EXPR,
|
|
354
|
+
DATA_ATTR$1 = DATA_ATTR,
|
|
355
|
+
ARIA_ATTR$1 = ARIA_ATTR,
|
|
356
|
+
IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
|
|
357
|
+
ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
|
|
358
|
+
CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
|
|
409
359
|
var IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
|
|
360
|
+
|
|
410
361
|
/**
|
|
411
362
|
* We consider the elements and attributes below to be safe. Ideally
|
|
412
363
|
* don't add any new ones but feel free to remove unwanted ones.
|
|
413
364
|
*/
|
|
414
365
|
|
|
415
366
|
/* allowed element names */
|
|
416
|
-
|
|
417
367
|
var ALLOWED_TAGS = null;
|
|
418
368
|
var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text)));
|
|
419
|
-
/* Allowed attribute names */
|
|
420
369
|
|
|
370
|
+
/* Allowed attribute names */
|
|
421
371
|
var ALLOWED_ATTR = null;
|
|
422
372
|
var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml)));
|
|
373
|
+
|
|
423
374
|
/*
|
|
424
375
|
* Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements.
|
|
425
376
|
* @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements)
|
|
426
377
|
* @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list)
|
|
427
378
|
* @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
|
|
428
379
|
*/
|
|
429
|
-
|
|
430
380
|
var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
|
|
431
381
|
tagNameCheck: {
|
|
432
382
|
writable: true,
|
|
@@ -447,59 +397,65 @@
|
|
|
447
397
|
value: false
|
|
448
398
|
}
|
|
449
399
|
}));
|
|
450
|
-
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
451
400
|
|
|
401
|
+
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
|
|
452
402
|
var FORBID_TAGS = null;
|
|
453
|
-
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
454
403
|
|
|
404
|
+
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
|
|
455
405
|
var FORBID_ATTR = null;
|
|
456
|
-
/* Decide if ARIA attributes are okay */
|
|
457
406
|
|
|
407
|
+
/* Decide if ARIA attributes are okay */
|
|
458
408
|
var ALLOW_ARIA_ATTR = true;
|
|
459
|
-
/* Decide if custom data attributes are okay */
|
|
460
409
|
|
|
410
|
+
/* Decide if custom data attributes are okay */
|
|
461
411
|
var ALLOW_DATA_ATTR = true;
|
|
462
|
-
/* Decide if unknown protocols are okay */
|
|
463
412
|
|
|
413
|
+
/* Decide if unknown protocols are okay */
|
|
464
414
|
var ALLOW_UNKNOWN_PROTOCOLS = false;
|
|
415
|
+
|
|
465
416
|
/* Decide if self-closing tags in attributes are allowed.
|
|
466
417
|
* Usually removed due to a mXSS issue in jQuery 3.0 */
|
|
467
|
-
|
|
468
418
|
var ALLOW_SELF_CLOSE_IN_ATTR = true;
|
|
419
|
+
|
|
469
420
|
/* Output should be safe for common template engines.
|
|
470
421
|
* This means, DOMPurify removes data attributes, mustaches and ERB
|
|
471
422
|
*/
|
|
472
|
-
|
|
473
423
|
var SAFE_FOR_TEMPLATES = false;
|
|
474
|
-
/* Decide if document with <html>... should be returned */
|
|
475
424
|
|
|
425
|
+
/* Output should be safe even for XML used within HTML and alike.
|
|
426
|
+
* This means, DOMPurify removes comments when containing risky content.
|
|
427
|
+
*/
|
|
428
|
+
var SAFE_FOR_XML = true;
|
|
429
|
+
|
|
430
|
+
/* Decide if document with <html>... should be returned */
|
|
476
431
|
var WHOLE_DOCUMENT = false;
|
|
477
|
-
/* Track whether config is already set on this instance of DOMPurify. */
|
|
478
432
|
|
|
433
|
+
/* Track whether config is already set on this instance of DOMPurify. */
|
|
479
434
|
var SET_CONFIG = false;
|
|
435
|
+
|
|
480
436
|
/* Decide if all elements (e.g. style, script) must be children of
|
|
481
437
|
* document.body. By default, browsers might move them to document.head */
|
|
482
|
-
|
|
483
438
|
var FORCE_BODY = false;
|
|
439
|
+
|
|
484
440
|
/* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
|
|
485
441
|
* string (or a TrustedHTML object if Trusted Types are supported).
|
|
486
442
|
* If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
|
|
487
443
|
*/
|
|
488
|
-
|
|
489
444
|
var RETURN_DOM = false;
|
|
445
|
+
|
|
490
446
|
/* Decide if a DOM `DocumentFragment` should be returned, instead of a html
|
|
491
447
|
* string (or a TrustedHTML object if Trusted Types are supported) */
|
|
492
|
-
|
|
493
448
|
var RETURN_DOM_FRAGMENT = false;
|
|
449
|
+
|
|
494
450
|
/* Try to return a Trusted Type object instead of a string, return a string in
|
|
495
451
|
* case Trusted Types are not supported */
|
|
496
|
-
|
|
497
452
|
var RETURN_TRUSTED_TYPE = false;
|
|
453
|
+
|
|
498
454
|
/* Output should be free from DOM clobbering attacks?
|
|
499
455
|
* This sanitizes markups named with colliding, clobberable built-in DOM APIs.
|
|
500
456
|
*/
|
|
501
|
-
|
|
502
457
|
var SANITIZE_DOM = true;
|
|
458
|
+
|
|
503
459
|
/* Achieve full DOM Clobbering protection by isolating the namespace of named
|
|
504
460
|
* properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules.
|
|
505
461
|
*
|
|
@@ -513,98 +469,101 @@
|
|
|
513
469
|
* Namespace isolation is implemented by prefixing `id` and `name` attributes
|
|
514
470
|
* with a constant string, i.e., `user-content-`
|
|
515
471
|
*/
|
|
516
|
-
|
|
517
472
|
var SANITIZE_NAMED_PROPS = false;
|
|
518
473
|
var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-';
|
|
519
|
-
/* Keep element content when removing element? */
|
|
520
474
|
|
|
475
|
+
/* Keep element content when removing element? */
|
|
521
476
|
var KEEP_CONTENT = true;
|
|
477
|
+
|
|
522
478
|
/* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
|
|
523
479
|
* of importing it into a new Document and returning a sanitized copy */
|
|
524
|
-
|
|
525
480
|
var IN_PLACE = false;
|
|
526
|
-
/* Allow usage of profiles like html, svg and mathMl */
|
|
527
481
|
|
|
482
|
+
/* Allow usage of profiles like html, svg and mathMl */
|
|
528
483
|
var USE_PROFILES = {};
|
|
529
|
-
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
530
484
|
|
|
485
|
+
/* Tags to ignore content of when KEEP_CONTENT is true */
|
|
531
486
|
var FORBID_CONTENTS = null;
|
|
532
487
|
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']);
|
|
533
|
-
/* Tags that are safe for data: URIs */
|
|
534
488
|
|
|
489
|
+
/* Tags that are safe for data: URIs */
|
|
535
490
|
var DATA_URI_TAGS = null;
|
|
536
491
|
var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
|
|
537
|
-
/* Attributes safe for values like "javascript:" */
|
|
538
492
|
|
|
493
|
+
/* Attributes safe for values like "javascript:" */
|
|
539
494
|
var URI_SAFE_ATTRIBUTES = null;
|
|
540
495
|
var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']);
|
|
541
496
|
var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
542
497
|
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
543
498
|
var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
544
499
|
/* Document namespace */
|
|
545
|
-
|
|
546
500
|
var NAMESPACE = HTML_NAMESPACE;
|
|
547
501
|
var IS_EMPTY_INPUT = false;
|
|
548
|
-
/* Allowed XHTML+XML namespaces */
|
|
549
502
|
|
|
503
|
+
/* Allowed XHTML+XML namespaces */
|
|
550
504
|
var ALLOWED_NAMESPACES = null;
|
|
551
505
|
var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
552
|
-
/* Parsing of strict XHTML documents */
|
|
553
506
|
|
|
507
|
+
/* Parsing of strict XHTML documents */
|
|
554
508
|
var PARSER_MEDIA_TYPE;
|
|
555
509
|
var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
|
|
556
510
|
var DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
|
|
557
511
|
var transformCaseFunc;
|
|
558
|
-
/* Keep a reference to config to pass to hooks */
|
|
559
512
|
|
|
513
|
+
/* Keep a reference to config to pass to hooks */
|
|
560
514
|
var CONFIG = null;
|
|
561
|
-
/* Ideally, do not touch anything below this line */
|
|
562
515
|
|
|
516
|
+
/* Specify the maximum element nesting depth to prevent mXSS */
|
|
517
|
+
var MAX_NESTING_DEPTH = 255;
|
|
518
|
+
|
|
519
|
+
/* Ideally, do not touch anything below this line */
|
|
563
520
|
/* ______________________________________________ */
|
|
564
521
|
|
|
565
522
|
var formElement = document.createElement('form');
|
|
566
|
-
|
|
567
523
|
var isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
568
524
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
569
525
|
};
|
|
526
|
+
|
|
570
527
|
/**
|
|
571
528
|
* _parseConfig
|
|
572
529
|
*
|
|
573
530
|
* @param {Object} cfg optional config literal
|
|
574
531
|
*/
|
|
575
532
|
// eslint-disable-next-line complexity
|
|
576
|
-
|
|
577
|
-
|
|
578
533
|
var _parseConfig = function _parseConfig(cfg) {
|
|
579
534
|
if (CONFIG && CONFIG === cfg) {
|
|
580
535
|
return;
|
|
581
536
|
}
|
|
582
|
-
/* Shield configuration object from tampering */
|
|
583
|
-
|
|
584
537
|
|
|
538
|
+
/* Shield configuration object from tampering */
|
|
585
539
|
if (!cfg || _typeof(cfg) !== 'object') {
|
|
586
540
|
cfg = {};
|
|
587
541
|
}
|
|
588
|
-
/* Shield configuration object from prototype pollution */
|
|
589
|
-
|
|
590
542
|
|
|
543
|
+
/* Shield configuration object from prototype pollution */
|
|
591
544
|
cfg = clone(cfg);
|
|
592
|
-
PARSER_MEDIA_TYPE =
|
|
593
|
-
|
|
545
|
+
PARSER_MEDIA_TYPE =
|
|
546
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
547
|
+
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? PARSER_MEDIA_TYPE = DEFAULT_PARSER_MEDIA_TYPE : PARSER_MEDIA_TYPE = cfg.PARSER_MEDIA_TYPE;
|
|
594
548
|
|
|
549
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
595
550
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
596
|
-
/* Set configuration parameters */
|
|
597
551
|
|
|
552
|
+
/* Set configuration parameters */
|
|
598
553
|
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
599
554
|
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
600
555
|
ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
|
|
601
|
-
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
602
|
-
|
|
556
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
557
|
+
// eslint-disable-line indent
|
|
558
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
559
|
+
// eslint-disable-line indent
|
|
603
560
|
transformCaseFunc // eslint-disable-line indent
|
|
604
561
|
) // eslint-disable-line indent
|
|
605
562
|
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
606
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
607
|
-
|
|
563
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
564
|
+
// eslint-disable-line indent
|
|
565
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
566
|
+
// eslint-disable-line indent
|
|
608
567
|
transformCaseFunc // eslint-disable-line indent
|
|
609
568
|
) // eslint-disable-line indent
|
|
610
569
|
: DEFAULT_DATA_URI_TAGS;
|
|
@@ -613,161 +572,128 @@
|
|
|
613
572
|
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
614
573
|
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
615
574
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
616
|
-
|
|
617
575
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
618
|
-
|
|
619
576
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
620
|
-
|
|
621
577
|
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
622
|
-
|
|
623
578
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
624
|
-
|
|
579
|
+
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
|
|
625
580
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
626
|
-
|
|
627
581
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
628
|
-
|
|
629
582
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
630
|
-
|
|
631
583
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
632
|
-
|
|
633
584
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
634
|
-
|
|
635
585
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
636
|
-
|
|
637
586
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
638
|
-
|
|
639
587
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
640
|
-
|
|
641
588
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
642
|
-
|
|
643
589
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
|
|
644
590
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
645
591
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
646
|
-
|
|
647
592
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
648
593
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
649
594
|
}
|
|
650
|
-
|
|
651
595
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
652
596
|
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
653
597
|
}
|
|
654
|
-
|
|
655
598
|
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
656
599
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
657
600
|
}
|
|
658
|
-
|
|
659
601
|
if (SAFE_FOR_TEMPLATES) {
|
|
660
602
|
ALLOW_DATA_ATTR = false;
|
|
661
603
|
}
|
|
662
|
-
|
|
663
604
|
if (RETURN_DOM_FRAGMENT) {
|
|
664
605
|
RETURN_DOM = true;
|
|
665
606
|
}
|
|
666
|
-
/* Parse profile info */
|
|
667
|
-
|
|
668
607
|
|
|
608
|
+
/* Parse profile info */
|
|
669
609
|
if (USE_PROFILES) {
|
|
670
610
|
ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
|
|
671
611
|
ALLOWED_ATTR = [];
|
|
672
|
-
|
|
673
612
|
if (USE_PROFILES.html === true) {
|
|
674
613
|
addToSet(ALLOWED_TAGS, html$1);
|
|
675
614
|
addToSet(ALLOWED_ATTR, html);
|
|
676
615
|
}
|
|
677
|
-
|
|
678
616
|
if (USE_PROFILES.svg === true) {
|
|
679
617
|
addToSet(ALLOWED_TAGS, svg$1);
|
|
680
618
|
addToSet(ALLOWED_ATTR, svg);
|
|
681
619
|
addToSet(ALLOWED_ATTR, xml);
|
|
682
620
|
}
|
|
683
|
-
|
|
684
621
|
if (USE_PROFILES.svgFilters === true) {
|
|
685
622
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
686
623
|
addToSet(ALLOWED_ATTR, svg);
|
|
687
624
|
addToSet(ALLOWED_ATTR, xml);
|
|
688
625
|
}
|
|
689
|
-
|
|
690
626
|
if (USE_PROFILES.mathMl === true) {
|
|
691
627
|
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
692
628
|
addToSet(ALLOWED_ATTR, mathMl);
|
|
693
629
|
addToSet(ALLOWED_ATTR, xml);
|
|
694
630
|
}
|
|
695
631
|
}
|
|
696
|
-
/* Merge configuration parameters */
|
|
697
|
-
|
|
698
632
|
|
|
633
|
+
/* Merge configuration parameters */
|
|
699
634
|
if (cfg.ADD_TAGS) {
|
|
700
635
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
701
636
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
702
637
|
}
|
|
703
|
-
|
|
704
638
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
705
639
|
}
|
|
706
|
-
|
|
707
640
|
if (cfg.ADD_ATTR) {
|
|
708
641
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
709
642
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
710
643
|
}
|
|
711
|
-
|
|
712
644
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
713
645
|
}
|
|
714
|
-
|
|
715
646
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
716
647
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
717
648
|
}
|
|
718
|
-
|
|
719
649
|
if (cfg.FORBID_CONTENTS) {
|
|
720
650
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
721
651
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
722
652
|
}
|
|
723
|
-
|
|
724
653
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
725
654
|
}
|
|
726
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
727
|
-
|
|
728
655
|
|
|
656
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
729
657
|
if (KEEP_CONTENT) {
|
|
730
658
|
ALLOWED_TAGS['#text'] = true;
|
|
731
659
|
}
|
|
732
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
733
|
-
|
|
734
660
|
|
|
661
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
735
662
|
if (WHOLE_DOCUMENT) {
|
|
736
663
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
737
664
|
}
|
|
738
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
739
|
-
|
|
740
665
|
|
|
666
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
741
667
|
if (ALLOWED_TAGS.table) {
|
|
742
668
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
743
669
|
delete FORBID_TAGS.tbody;
|
|
744
|
-
}
|
|
745
|
-
// Not available in IE8, Safari 5, etc.
|
|
746
|
-
|
|
670
|
+
}
|
|
747
671
|
|
|
672
|
+
// Prevent further manipulation of configuration.
|
|
673
|
+
// Not available in IE8, Safari 5, etc.
|
|
748
674
|
if (freeze) {
|
|
749
675
|
freeze(cfg);
|
|
750
676
|
}
|
|
751
|
-
|
|
752
677
|
CONFIG = cfg;
|
|
753
678
|
};
|
|
754
|
-
|
|
755
679
|
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
756
|
-
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
680
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
681
|
+
|
|
682
|
+
// Certain elements are allowed in both SVG and HTML
|
|
757
683
|
// namespace. We need to specify them explicitly
|
|
758
684
|
// so that they don't get erroneously deleted from
|
|
759
685
|
// HTML namespace.
|
|
760
|
-
|
|
761
686
|
var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
687
|
+
|
|
762
688
|
/* Keep track of all possible SVG and MathML tags
|
|
763
689
|
* so that we can perform the namespace checks
|
|
764
690
|
* correctly. */
|
|
765
|
-
|
|
766
691
|
var ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
767
692
|
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
768
693
|
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
769
694
|
var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
770
695
|
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
696
|
+
|
|
771
697
|
/**
|
|
772
698
|
*
|
|
773
699
|
*
|
|
@@ -776,64 +702,59 @@
|
|
|
776
702
|
* namespace that a spec-compliant parser would never
|
|
777
703
|
* return. Return true otherwise.
|
|
778
704
|
*/
|
|
779
|
-
|
|
780
705
|
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
781
|
-
var parent = getParentNode(element);
|
|
782
|
-
// can be null. We just simulate parent in this case.
|
|
706
|
+
var parent = getParentNode(element);
|
|
783
707
|
|
|
708
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
709
|
+
// can be null. We just simulate parent in this case.
|
|
784
710
|
if (!parent || !parent.tagName) {
|
|
785
711
|
parent = {
|
|
786
712
|
namespaceURI: NAMESPACE,
|
|
787
713
|
tagName: 'template'
|
|
788
714
|
};
|
|
789
715
|
}
|
|
790
|
-
|
|
791
716
|
var tagName = stringToLowerCase(element.tagName);
|
|
792
717
|
var parentTagName = stringToLowerCase(parent.tagName);
|
|
793
|
-
|
|
794
718
|
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
795
719
|
return false;
|
|
796
720
|
}
|
|
797
|
-
|
|
798
721
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
799
722
|
// The only way to switch from HTML namespace to SVG
|
|
800
723
|
// is via <svg>. If it happens via any other tag, then
|
|
801
724
|
// it should be killed.
|
|
802
725
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
803
726
|
return tagName === 'svg';
|
|
804
|
-
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// The only way to switch from MathML to SVG is via`
|
|
805
730
|
// svg if parent is either <annotation-xml> or MathML
|
|
806
731
|
// text integration points.
|
|
807
|
-
|
|
808
|
-
|
|
809
732
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
810
733
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
811
|
-
}
|
|
812
|
-
// spec. All others are disallowed in SVG namespace.
|
|
813
|
-
|
|
734
|
+
}
|
|
814
735
|
|
|
736
|
+
// We only allow elements that are defined in SVG
|
|
737
|
+
// spec. All others are disallowed in SVG namespace.
|
|
815
738
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
816
739
|
}
|
|
817
|
-
|
|
818
740
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
819
741
|
// The only way to switch from HTML namespace to MathML
|
|
820
742
|
// is via <math>. If it happens via any other tag, then
|
|
821
743
|
// it should be killed.
|
|
822
744
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
823
745
|
return tagName === 'math';
|
|
824
|
-
}
|
|
825
|
-
// <math> and HTML integration points
|
|
826
|
-
|
|
746
|
+
}
|
|
827
747
|
|
|
748
|
+
// The only way to switch from SVG to MathML is via
|
|
749
|
+
// <math> and HTML integration points
|
|
828
750
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
829
751
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
830
|
-
}
|
|
831
|
-
// spec. All others are disallowed in MathML namespace.
|
|
832
|
-
|
|
752
|
+
}
|
|
833
753
|
|
|
754
|
+
// We only allow elements that are defined in MathML
|
|
755
|
+
// spec. All others are disallowed in MathML namespace.
|
|
834
756
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
835
757
|
}
|
|
836
|
-
|
|
837
758
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
838
759
|
// The only way to switch from SVG to HTML is via
|
|
839
760
|
// HTML integration points, and from MathML to HTML
|
|
@@ -841,39 +762,36 @@
|
|
|
841
762
|
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
842
763
|
return false;
|
|
843
764
|
}
|
|
844
|
-
|
|
845
765
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
846
766
|
return false;
|
|
847
|
-
}
|
|
848
|
-
// or SVG and should never appear in HTML namespace
|
|
849
|
-
|
|
767
|
+
}
|
|
850
768
|
|
|
769
|
+
// We disallow tags that are specific for MathML
|
|
770
|
+
// or SVG and should never appear in HTML namespace
|
|
851
771
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
852
|
-
}
|
|
853
|
-
|
|
772
|
+
}
|
|
854
773
|
|
|
774
|
+
// For XHTML and XML documents that support custom namespaces
|
|
855
775
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
856
776
|
return true;
|
|
857
|
-
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// The code should never reach this place (this means
|
|
858
780
|
// that the element somehow got namespace that is not
|
|
859
781
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
860
782
|
// Return false just in case.
|
|
861
|
-
|
|
862
|
-
|
|
863
783
|
return false;
|
|
864
784
|
};
|
|
785
|
+
|
|
865
786
|
/**
|
|
866
787
|
* _forceRemove
|
|
867
788
|
*
|
|
868
789
|
* @param {Node} node a DOM node
|
|
869
790
|
*/
|
|
870
|
-
|
|
871
|
-
|
|
872
791
|
var _forceRemove = function _forceRemove(node) {
|
|
873
792
|
arrayPush(DOMPurify.removed, {
|
|
874
793
|
element: node
|
|
875
794
|
});
|
|
876
|
-
|
|
877
795
|
try {
|
|
878
796
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
879
797
|
node.parentNode.removeChild(node);
|
|
@@ -885,14 +803,13 @@
|
|
|
885
803
|
}
|
|
886
804
|
}
|
|
887
805
|
};
|
|
806
|
+
|
|
888
807
|
/**
|
|
889
808
|
* _removeAttribute
|
|
890
809
|
*
|
|
891
810
|
* @param {String} name an Attribute name
|
|
892
811
|
* @param {Node} node a DOM node
|
|
893
812
|
*/
|
|
894
|
-
|
|
895
|
-
|
|
896
813
|
var _removeAttribute = function _removeAttribute(name, node) {
|
|
897
814
|
try {
|
|
898
815
|
arrayPush(DOMPurify.removed, {
|
|
@@ -905,9 +822,9 @@
|
|
|
905
822
|
from: node
|
|
906
823
|
});
|
|
907
824
|
}
|
|
825
|
+
node.removeAttribute(name);
|
|
908
826
|
|
|
909
|
-
|
|
910
|
-
|
|
827
|
+
// We void attribute values for unremovable "is"" attributes
|
|
911
828
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
912
829
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
913
830
|
try {
|
|
@@ -920,19 +837,17 @@
|
|
|
920
837
|
}
|
|
921
838
|
}
|
|
922
839
|
};
|
|
840
|
+
|
|
923
841
|
/**
|
|
924
842
|
* _initDocument
|
|
925
843
|
*
|
|
926
844
|
* @param {String} dirty a string of dirty markup
|
|
927
845
|
* @return {Document} a DOM, filled with the dirty markup
|
|
928
846
|
*/
|
|
929
|
-
|
|
930
|
-
|
|
931
847
|
var _initDocument = function _initDocument(dirty) {
|
|
932
848
|
/* Create a HTML document */
|
|
933
849
|
var doc;
|
|
934
850
|
var leadingWhitespace;
|
|
935
|
-
|
|
936
851
|
if (FORCE_BODY) {
|
|
937
852
|
dirty = '<remove></remove>' + dirty;
|
|
938
853
|
} else {
|
|
@@ -940,83 +855,74 @@
|
|
|
940
855
|
var matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
941
856
|
leadingWhitespace = matches && matches[0];
|
|
942
857
|
}
|
|
943
|
-
|
|
944
858
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
945
859
|
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
946
860
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
947
861
|
}
|
|
948
|
-
|
|
949
862
|
var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
950
863
|
/*
|
|
951
864
|
* Use the DOMParser API by default, fallback later if needs be
|
|
952
865
|
* DOMParser not work for svg when has multiple root element.
|
|
953
866
|
*/
|
|
954
|
-
|
|
955
867
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
956
868
|
try {
|
|
957
869
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
958
870
|
} catch (_) {}
|
|
959
871
|
}
|
|
960
|
-
/* Use createHTMLDocument in case DOMParser is not available */
|
|
961
|
-
|
|
962
872
|
|
|
873
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
963
874
|
if (!doc || !doc.documentElement) {
|
|
964
875
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
965
|
-
|
|
966
876
|
try {
|
|
967
877
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
968
|
-
} catch (_) {
|
|
878
|
+
} catch (_) {
|
|
879
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
969
880
|
}
|
|
970
881
|
}
|
|
971
|
-
|
|
972
882
|
var body = doc.body || doc.documentElement;
|
|
973
|
-
|
|
974
883
|
if (dirty && leadingWhitespace) {
|
|
975
884
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
976
885
|
}
|
|
977
|
-
/* Work on whole document or just its body */
|
|
978
|
-
|
|
979
886
|
|
|
887
|
+
/* Work on whole document or just its body */
|
|
980
888
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
981
889
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
982
890
|
}
|
|
983
|
-
|
|
984
891
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
985
892
|
};
|
|
893
|
+
|
|
986
894
|
/**
|
|
987
895
|
* _createIterator
|
|
988
896
|
*
|
|
989
897
|
* @param {Document} root document/fragment to create iterator for
|
|
990
898
|
* @return {Iterator} iterator instance
|
|
991
899
|
*/
|
|
992
|
-
|
|
993
|
-
|
|
994
900
|
var _createIterator = function _createIterator(root) {
|
|
995
|
-
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
901
|
+
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
902
|
+
// eslint-disable-next-line no-bitwise
|
|
996
903
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false);
|
|
997
904
|
};
|
|
905
|
+
|
|
998
906
|
/**
|
|
999
907
|
* _isClobbered
|
|
1000
908
|
*
|
|
1001
909
|
* @param {Node} elm element to check for clobbering attacks
|
|
1002
910
|
* @return {Boolean} true if clobbered, false if safe
|
|
1003
911
|
*/
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
912
|
var _isClobbered = function _isClobbered(elm) {
|
|
1007
|
-
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' || typeof elm.hasChildNodes !== 'function');
|
|
913
|
+
return elm instanceof HTMLFormElement && (typeof elm.__depth !== 'undefined' && typeof elm.__depth !== 'number' || typeof elm.__removalCount !== 'undefined' && typeof elm.__removalCount !== 'number' || 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' || typeof elm.hasChildNodes !== 'function');
|
|
1008
914
|
};
|
|
915
|
+
|
|
1009
916
|
/**
|
|
1010
917
|
* _isNode
|
|
1011
918
|
*
|
|
1012
919
|
* @param {Node} obj object to check whether it's a DOM node
|
|
1013
920
|
* @return {Boolean} true is object is a DOM node
|
|
1014
921
|
*/
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
922
|
var _isNode = function _isNode(object) {
|
|
1018
923
|
return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1019
924
|
};
|
|
925
|
+
|
|
1020
926
|
/**
|
|
1021
927
|
* _executeHook
|
|
1022
928
|
* Execute user configurable hooks
|
|
@@ -1025,17 +931,15 @@
|
|
|
1025
931
|
* @param {Node} currentNode node to work on with the hook
|
|
1026
932
|
* @param {Object} data additional hook parameters
|
|
1027
933
|
*/
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
934
|
var _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1031
935
|
if (!hooks[entryPoint]) {
|
|
1032
936
|
return;
|
|
1033
937
|
}
|
|
1034
|
-
|
|
1035
938
|
arrayForEach(hooks[entryPoint], function (hook) {
|
|
1036
939
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1037
940
|
});
|
|
1038
941
|
};
|
|
942
|
+
|
|
1039
943
|
/**
|
|
1040
944
|
* _sanitizeElements
|
|
1041
945
|
*
|
|
@@ -1046,118 +950,101 @@
|
|
|
1046
950
|
* @param {Node} currentNode to check for permission to exist
|
|
1047
951
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1048
952
|
*/
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
953
|
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1052
954
|
var content;
|
|
1053
|
-
/* Execute a hook if present */
|
|
1054
955
|
|
|
956
|
+
/* Execute a hook if present */
|
|
1055
957
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1056
|
-
/* Check if element is clobbered or can clobber */
|
|
1057
|
-
|
|
1058
958
|
|
|
959
|
+
/* Check if element is clobbered or can clobber */
|
|
1059
960
|
if (_isClobbered(currentNode)) {
|
|
1060
961
|
_forceRemove(currentNode);
|
|
1061
|
-
|
|
1062
962
|
return true;
|
|
1063
963
|
}
|
|
1064
|
-
/* Check if tagname contains Unicode */
|
|
1065
|
-
|
|
1066
964
|
|
|
965
|
+
/* Check if tagname contains Unicode */
|
|
1067
966
|
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
1068
967
|
_forceRemove(currentNode);
|
|
1069
|
-
|
|
1070
968
|
return true;
|
|
1071
969
|
}
|
|
1072
|
-
/* Now let's check the element's type and name */
|
|
1073
|
-
|
|
1074
970
|
|
|
971
|
+
/* Now let's check the element's type and name */
|
|
1075
972
|
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
1076
|
-
/* Execute a hook if present */
|
|
1077
973
|
|
|
974
|
+
/* Execute a hook if present */
|
|
1078
975
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1079
976
|
tagName: tagName,
|
|
1080
977
|
allowedTags: ALLOWED_TAGS
|
|
1081
978
|
});
|
|
1082
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1083
|
-
|
|
1084
979
|
|
|
980
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1085
981
|
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1086
982
|
_forceRemove(currentNode);
|
|
1087
|
-
|
|
1088
983
|
return true;
|
|
1089
984
|
}
|
|
1090
|
-
/* Mitigate a problem with templates inside select */
|
|
1091
|
-
|
|
1092
985
|
|
|
986
|
+
/* Mitigate a problem with templates inside select */
|
|
1093
987
|
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
1094
988
|
_forceRemove(currentNode);
|
|
1095
|
-
|
|
1096
989
|
return true;
|
|
1097
990
|
}
|
|
1098
|
-
/* Remove any ocurrence of processing instructions */
|
|
1099
|
-
|
|
1100
991
|
|
|
992
|
+
/* Remove any ocurrence of processing instructions */
|
|
1101
993
|
if (currentNode.nodeType === 7) {
|
|
1102
994
|
_forceRemove(currentNode);
|
|
1103
|
-
|
|
1104
995
|
return true;
|
|
1105
996
|
}
|
|
1106
|
-
/* Remove element if anything forbids its presence */
|
|
1107
997
|
|
|
998
|
+
/* Remove any kind of possibly harmful comments */
|
|
999
|
+
if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
1000
|
+
_forceRemove(currentNode);
|
|
1001
|
+
return true;
|
|
1002
|
+
}
|
|
1108
1003
|
|
|
1004
|
+
/* Remove element if anything forbids its presence */
|
|
1109
1005
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1110
1006
|
/* Check if we have a custom element to handle */
|
|
1111
1007
|
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
1112
1008
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1113
1009
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1114
1010
|
}
|
|
1115
|
-
/* Keep content except for bad-listed elements */
|
|
1116
|
-
|
|
1117
1011
|
|
|
1012
|
+
/* Keep content except for bad-listed elements */
|
|
1118
1013
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1119
1014
|
var parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1120
1015
|
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1121
|
-
|
|
1122
1016
|
if (childNodes && parentNode) {
|
|
1123
1017
|
var childCount = childNodes.length;
|
|
1124
|
-
|
|
1125
1018
|
for (var i = childCount - 1; i >= 0; --i) {
|
|
1126
|
-
|
|
1019
|
+
var childClone = cloneNode(childNodes[i], true);
|
|
1020
|
+
childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
|
|
1021
|
+
parentNode.insertBefore(childClone, getNextSibling(currentNode));
|
|
1127
1022
|
}
|
|
1128
1023
|
}
|
|
1129
1024
|
}
|
|
1130
|
-
|
|
1131
1025
|
_forceRemove(currentNode);
|
|
1132
|
-
|
|
1133
1026
|
return true;
|
|
1134
1027
|
}
|
|
1135
|
-
/* Check whether element has a valid namespace */
|
|
1136
|
-
|
|
1137
1028
|
|
|
1029
|
+
/* Check whether element has a valid namespace */
|
|
1138
1030
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1139
1031
|
_forceRemove(currentNode);
|
|
1140
|
-
|
|
1141
1032
|
return true;
|
|
1142
1033
|
}
|
|
1143
|
-
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1144
|
-
|
|
1145
1034
|
|
|
1035
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1146
1036
|
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1147
1037
|
_forceRemove(currentNode);
|
|
1148
|
-
|
|
1149
1038
|
return true;
|
|
1150
1039
|
}
|
|
1151
|
-
/* Sanitize element content to be template-safe */
|
|
1152
|
-
|
|
1153
1040
|
|
|
1041
|
+
/* Sanitize element content to be template-safe */
|
|
1154
1042
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1155
1043
|
/* Get the element's text content */
|
|
1156
1044
|
content = currentNode.textContent;
|
|
1157
1045
|
content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
|
|
1158
1046
|
content = stringReplace(content, ERB_EXPR$1, ' ');
|
|
1159
1047
|
content = stringReplace(content, TMPLIT_EXPR$1, ' ');
|
|
1160
|
-
|
|
1161
1048
|
if (currentNode.textContent !== content) {
|
|
1162
1049
|
arrayPush(DOMPurify.removed, {
|
|
1163
1050
|
element: currentNode.cloneNode()
|
|
@@ -1165,13 +1052,12 @@
|
|
|
1165
1052
|
currentNode.textContent = content;
|
|
1166
1053
|
}
|
|
1167
1054
|
}
|
|
1168
|
-
/* Execute a hook if present */
|
|
1169
|
-
|
|
1170
1055
|
|
|
1056
|
+
/* Execute a hook if present */
|
|
1171
1057
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1172
|
-
|
|
1173
1058
|
return false;
|
|
1174
1059
|
};
|
|
1060
|
+
|
|
1175
1061
|
/**
|
|
1176
1062
|
* _isValidAttribute
|
|
1177
1063
|
*
|
|
@@ -1181,47 +1067,44 @@
|
|
|
1181
1067
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1182
1068
|
*/
|
|
1183
1069
|
// eslint-disable-next-line complexity
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
1070
|
var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1187
1071
|
/* Make sure attribute cannot clobber */
|
|
1188
1072
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1189
1073
|
return false;
|
|
1190
1074
|
}
|
|
1075
|
+
|
|
1191
1076
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1192
1077
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1193
1078
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1194
1079
|
We don't need to check the value; it's always URI safe. */
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
1080
|
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]) {
|
|
1198
|
-
if (
|
|
1081
|
+
if (
|
|
1082
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1199
1083
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1200
1084
|
// and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
|
|
1201
|
-
_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)) ||
|
|
1085
|
+
_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)) ||
|
|
1086
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1202
1087
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1203
1088
|
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 {
|
|
1204
1089
|
return false;
|
|
1205
1090
|
}
|
|
1206
1091
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1207
|
-
|
|
1208
1092
|
} 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) {
|
|
1209
1093
|
return false;
|
|
1210
1094
|
} else ;
|
|
1211
|
-
|
|
1212
1095
|
return true;
|
|
1213
1096
|
};
|
|
1097
|
+
|
|
1214
1098
|
/**
|
|
1215
1099
|
* _basicCustomElementCheck
|
|
1216
1100
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1217
1101
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1218
1102
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1219
1103
|
*/
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
1104
|
var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1223
1105
|
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT$1);
|
|
1224
1106
|
};
|
|
1107
|
+
|
|
1225
1108
|
/**
|
|
1226
1109
|
* _sanitizeAttributes
|
|
1227
1110
|
*
|
|
@@ -1232,24 +1115,19 @@
|
|
|
1232
1115
|
*
|
|
1233
1116
|
* @param {Node} currentNode to sanitize
|
|
1234
1117
|
*/
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
1118
|
var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1238
1119
|
var attr;
|
|
1239
1120
|
var value;
|
|
1240
1121
|
var lcName;
|
|
1241
1122
|
var l;
|
|
1242
1123
|
/* Execute a hook if present */
|
|
1243
|
-
|
|
1244
1124
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1245
|
-
|
|
1246
1125
|
var attributes = currentNode.attributes;
|
|
1247
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1248
1126
|
|
|
1127
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1249
1128
|
if (!attributes) {
|
|
1250
1129
|
return;
|
|
1251
1130
|
}
|
|
1252
|
-
|
|
1253
1131
|
var hookEvent = {
|
|
1254
1132
|
attrName: '',
|
|
1255
1133
|
attrValue: '',
|
|
@@ -1257,79 +1135,67 @@
|
|
|
1257
1135
|
allowedAttributes: ALLOWED_ATTR
|
|
1258
1136
|
};
|
|
1259
1137
|
l = attributes.length;
|
|
1260
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1261
1138
|
|
|
1139
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1262
1140
|
while (l--) {
|
|
1263
1141
|
attr = attributes[l];
|
|
1264
1142
|
var _attr = attr,
|
|
1265
|
-
|
|
1266
|
-
|
|
1143
|
+
name = _attr.name,
|
|
1144
|
+
namespaceURI = _attr.namespaceURI;
|
|
1267
1145
|
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1268
1146
|
lcName = transformCaseFunc(name);
|
|
1269
|
-
/* Execute a hook if present */
|
|
1270
1147
|
|
|
1148
|
+
/* Execute a hook if present */
|
|
1271
1149
|
hookEvent.attrName = lcName;
|
|
1272
1150
|
hookEvent.attrValue = value;
|
|
1273
1151
|
hookEvent.keepAttr = true;
|
|
1274
1152
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1275
|
-
|
|
1276
1153
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1277
|
-
|
|
1278
1154
|
value = hookEvent.attrValue;
|
|
1279
1155
|
/* Did the hooks approve of the attribute? */
|
|
1280
|
-
|
|
1281
1156
|
if (hookEvent.forceKeepAttr) {
|
|
1282
1157
|
continue;
|
|
1283
1158
|
}
|
|
1284
|
-
/* Remove attribute */
|
|
1285
|
-
|
|
1286
1159
|
|
|
1160
|
+
/* Remove attribute */
|
|
1287
1161
|
_removeAttribute(name, currentNode);
|
|
1288
|
-
/* Did the hooks approve of the attribute? */
|
|
1289
|
-
|
|
1290
1162
|
|
|
1163
|
+
/* Did the hooks approve of the attribute? */
|
|
1291
1164
|
if (!hookEvent.keepAttr) {
|
|
1292
1165
|
continue;
|
|
1293
1166
|
}
|
|
1294
|
-
/* Work around a security issue in jQuery 3.0 */
|
|
1295
|
-
|
|
1296
1167
|
|
|
1168
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1297
1169
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1298
1170
|
_removeAttribute(name, currentNode);
|
|
1299
|
-
|
|
1300
1171
|
continue;
|
|
1301
1172
|
}
|
|
1302
|
-
/* Sanitize attribute content to be template-safe */
|
|
1303
|
-
|
|
1304
1173
|
|
|
1174
|
+
/* Sanitize attribute content to be template-safe */
|
|
1305
1175
|
if (SAFE_FOR_TEMPLATES) {
|
|
1306
1176
|
value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
|
|
1307
1177
|
value = stringReplace(value, ERB_EXPR$1, ' ');
|
|
1308
1178
|
value = stringReplace(value, TMPLIT_EXPR$1, ' ');
|
|
1309
1179
|
}
|
|
1310
|
-
/* Is `value` valid for this attribute? */
|
|
1311
|
-
|
|
1312
1180
|
|
|
1181
|
+
/* Is `value` valid for this attribute? */
|
|
1313
1182
|
var lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1314
|
-
|
|
1315
1183
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1316
1184
|
continue;
|
|
1317
1185
|
}
|
|
1186
|
+
|
|
1318
1187
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1319
1188
|
* Prefix id and name attributes with `user-content-`
|
|
1320
1189
|
*/
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
1190
|
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1324
1191
|
// Remove the attribute with this value
|
|
1325
|
-
_removeAttribute(name, currentNode);
|
|
1326
|
-
|
|
1192
|
+
_removeAttribute(name, currentNode);
|
|
1327
1193
|
|
|
1194
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1328
1195
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1329
1196
|
}
|
|
1330
|
-
/* Handle attributes that require Trusted Types */
|
|
1331
|
-
|
|
1332
1197
|
|
|
1198
|
+
/* Handle attributes that require Trusted Types */
|
|
1333
1199
|
if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1334
1200
|
if (namespaceURI) ; else {
|
|
1335
1201
|
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
@@ -1338,7 +1204,6 @@
|
|
|
1338
1204
|
value = trustedTypesPolicy.createHTML(value);
|
|
1339
1205
|
break;
|
|
1340
1206
|
}
|
|
1341
|
-
|
|
1342
1207
|
case 'TrustedScriptURL':
|
|
1343
1208
|
{
|
|
1344
1209
|
value = trustedTypesPolicy.createScriptURL(value);
|
|
@@ -1347,9 +1212,8 @@
|
|
|
1347
1212
|
}
|
|
1348
1213
|
}
|
|
1349
1214
|
}
|
|
1350
|
-
/* Handle invalid data-* attribute set by try-catching it */
|
|
1351
|
-
|
|
1352
1215
|
|
|
1216
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
1353
1217
|
try {
|
|
1354
1218
|
if (namespaceURI) {
|
|
1355
1219
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1357,56 +1221,66 @@
|
|
|
1357
1221
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1358
1222
|
currentNode.setAttribute(name, value);
|
|
1359
1223
|
}
|
|
1360
|
-
|
|
1361
1224
|
arrayPop(DOMPurify.removed);
|
|
1362
1225
|
} catch (_) {}
|
|
1363
1226
|
}
|
|
1364
|
-
/* Execute a hook if present */
|
|
1365
|
-
|
|
1366
1227
|
|
|
1228
|
+
/* Execute a hook if present */
|
|
1367
1229
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1368
1230
|
};
|
|
1231
|
+
|
|
1369
1232
|
/**
|
|
1370
1233
|
* _sanitizeShadowDOM
|
|
1371
1234
|
*
|
|
1372
1235
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1373
1236
|
*/
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
1237
|
var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1377
1238
|
var shadowNode;
|
|
1378
|
-
|
|
1379
1239
|
var shadowIterator = _createIterator(fragment);
|
|
1380
|
-
/* Execute a hook if present */
|
|
1381
|
-
|
|
1382
1240
|
|
|
1241
|
+
/* Execute a hook if present */
|
|
1383
1242
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1384
|
-
|
|
1385
1243
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1386
1244
|
/* Execute a hook if present */
|
|
1387
1245
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1388
|
-
/* Sanitize tags and elements */
|
|
1389
|
-
|
|
1390
1246
|
|
|
1247
|
+
/* Sanitize tags and elements */
|
|
1391
1248
|
if (_sanitizeElements(shadowNode)) {
|
|
1392
1249
|
continue;
|
|
1393
1250
|
}
|
|
1394
|
-
/* Deep shadow DOM detected */
|
|
1395
1251
|
|
|
1252
|
+
/* Set the nesting depth of an element */
|
|
1253
|
+
if (shadowNode.nodeType === 1) {
|
|
1254
|
+
if (shadowNode.parentNode && shadowNode.parentNode.__depth) {
|
|
1255
|
+
/*
|
|
1256
|
+
We want the depth of the node in the original tree, which can
|
|
1257
|
+
change when it's removed from its parent.
|
|
1258
|
+
*/
|
|
1259
|
+
shadowNode.__depth = (shadowNode.__removalCount || 0) + shadowNode.parentNode.__depth + 1;
|
|
1260
|
+
} else {
|
|
1261
|
+
shadowNode.__depth = 1;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1266
|
+
if (shadowNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1267
|
+
_forceRemove(shadowNode);
|
|
1268
|
+
}
|
|
1396
1269
|
|
|
1270
|
+
/* Deep shadow DOM detected */
|
|
1397
1271
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1272
|
+
shadowNode.content.__depth = shadowNode.__depth;
|
|
1398
1273
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1399
1274
|
}
|
|
1400
|
-
/* Check attributes, sanitize if necessary */
|
|
1401
|
-
|
|
1402
1275
|
|
|
1276
|
+
/* Check attributes, sanitize if necessary */
|
|
1403
1277
|
_sanitizeAttributes(shadowNode);
|
|
1404
1278
|
}
|
|
1405
|
-
/* Execute a hook if present */
|
|
1406
|
-
|
|
1407
1279
|
|
|
1280
|
+
/* Execute a hook if present */
|
|
1408
1281
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1409
1282
|
};
|
|
1283
|
+
|
|
1410
1284
|
/**
|
|
1411
1285
|
* Sanitize
|
|
1412
1286
|
* Public method providing core sanitation functionality
|
|
@@ -1415,8 +1289,6 @@
|
|
|
1415
1289
|
* @param {Object} configuration object
|
|
1416
1290
|
*/
|
|
1417
1291
|
// eslint-disable-next-line complexity
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
1292
|
DOMPurify.sanitize = function (dirty) {
|
|
1421
1293
|
var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1422
1294
|
var body;
|
|
@@ -1427,19 +1299,15 @@
|
|
|
1427
1299
|
/* Make sure we have a string to sanitize.
|
|
1428
1300
|
DO NOT return early, as this will return the wrong type if
|
|
1429
1301
|
the user has requested a DOM object rather than a string */
|
|
1430
|
-
|
|
1431
1302
|
IS_EMPTY_INPUT = !dirty;
|
|
1432
|
-
|
|
1433
1303
|
if (IS_EMPTY_INPUT) {
|
|
1434
1304
|
dirty = '<!-->';
|
|
1435
1305
|
}
|
|
1436
|
-
/* Stringify, in case dirty is an object */
|
|
1437
|
-
|
|
1438
1306
|
|
|
1307
|
+
/* Stringify, in case dirty is an object */
|
|
1439
1308
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1440
1309
|
if (typeof dirty.toString === 'function') {
|
|
1441
1310
|
dirty = dirty.toString();
|
|
1442
|
-
|
|
1443
1311
|
if (typeof dirty !== 'string') {
|
|
1444
1312
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1445
1313
|
}
|
|
@@ -1447,43 +1315,36 @@
|
|
|
1447
1315
|
throw typeErrorCreate('toString is not a function');
|
|
1448
1316
|
}
|
|
1449
1317
|
}
|
|
1450
|
-
/* Check we can run. Otherwise fall back or ignore */
|
|
1451
|
-
|
|
1452
1318
|
|
|
1319
|
+
/* Check we can run. Otherwise fall back or ignore */
|
|
1453
1320
|
if (!DOMPurify.isSupported) {
|
|
1454
1321
|
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1455
1322
|
if (typeof dirty === 'string') {
|
|
1456
1323
|
return window.toStaticHTML(dirty);
|
|
1457
1324
|
}
|
|
1458
|
-
|
|
1459
1325
|
if (_isNode(dirty)) {
|
|
1460
1326
|
return window.toStaticHTML(dirty.outerHTML);
|
|
1461
1327
|
}
|
|
1462
1328
|
}
|
|
1463
|
-
|
|
1464
1329
|
return dirty;
|
|
1465
1330
|
}
|
|
1466
|
-
/* Assign config vars */
|
|
1467
|
-
|
|
1468
1331
|
|
|
1332
|
+
/* Assign config vars */
|
|
1469
1333
|
if (!SET_CONFIG) {
|
|
1470
1334
|
_parseConfig(cfg);
|
|
1471
1335
|
}
|
|
1472
|
-
/* Clean up removed elements */
|
|
1473
|
-
|
|
1474
1336
|
|
|
1337
|
+
/* Clean up removed elements */
|
|
1475
1338
|
DOMPurify.removed = [];
|
|
1476
|
-
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1477
1339
|
|
|
1340
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1478
1341
|
if (typeof dirty === 'string') {
|
|
1479
1342
|
IN_PLACE = false;
|
|
1480
1343
|
}
|
|
1481
|
-
|
|
1482
1344
|
if (IN_PLACE) {
|
|
1483
1345
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1484
1346
|
if (dirty.nodeName) {
|
|
1485
1347
|
var tagName = transformCaseFunc(dirty.nodeName);
|
|
1486
|
-
|
|
1487
1348
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1488
1349
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1489
1350
|
}
|
|
@@ -1493,7 +1354,6 @@
|
|
|
1493
1354
|
elements being stripped by the parser */
|
|
1494
1355
|
body = _initDocument('<!---->');
|
|
1495
1356
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1496
|
-
|
|
1497
1357
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1498
1358
|
/* Node is already a body, use as is */
|
|
1499
1359
|
body = importedNode;
|
|
@@ -1505,71 +1365,80 @@
|
|
|
1505
1365
|
}
|
|
1506
1366
|
} else {
|
|
1507
1367
|
/* Exit directly if we have nothing to do */
|
|
1508
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1368
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1369
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1509
1370
|
dirty.indexOf('<') === -1) {
|
|
1510
1371
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1511
1372
|
}
|
|
1512
|
-
/* Initialize the document to work on */
|
|
1513
|
-
|
|
1514
1373
|
|
|
1374
|
+
/* Initialize the document to work on */
|
|
1515
1375
|
body = _initDocument(dirty);
|
|
1516
|
-
/* Check we have a DOM node from the data */
|
|
1517
1376
|
|
|
1377
|
+
/* Check we have a DOM node from the data */
|
|
1518
1378
|
if (!body) {
|
|
1519
1379
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1520
1380
|
}
|
|
1521
1381
|
}
|
|
1522
|
-
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1523
|
-
|
|
1524
1382
|
|
|
1383
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1525
1384
|
if (body && FORCE_BODY) {
|
|
1526
1385
|
_forceRemove(body.firstChild);
|
|
1527
1386
|
}
|
|
1528
|
-
/* Get node iterator */
|
|
1529
|
-
|
|
1530
1387
|
|
|
1388
|
+
/* Get node iterator */
|
|
1531
1389
|
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1532
|
-
/* Now start iterating over the created document */
|
|
1533
|
-
|
|
1534
1390
|
|
|
1391
|
+
/* Now start iterating over the created document */
|
|
1535
1392
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1536
1393
|
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1537
1394
|
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1538
1395
|
continue;
|
|
1539
1396
|
}
|
|
1540
|
-
/* Sanitize tags and elements */
|
|
1541
|
-
|
|
1542
1397
|
|
|
1398
|
+
/* Sanitize tags and elements */
|
|
1543
1399
|
if (_sanitizeElements(currentNode)) {
|
|
1544
1400
|
continue;
|
|
1545
1401
|
}
|
|
1546
|
-
/* Shadow DOM detected, sanitize it */
|
|
1547
1402
|
|
|
1403
|
+
/* Set the nesting depth of an element */
|
|
1404
|
+
if (currentNode.nodeType === 1) {
|
|
1405
|
+
if (currentNode.parentNode && currentNode.parentNode.__depth) {
|
|
1406
|
+
/*
|
|
1407
|
+
We want the depth of the node in the original tree, which can
|
|
1408
|
+
change when it's removed from its parent.
|
|
1409
|
+
*/
|
|
1410
|
+
currentNode.__depth = (currentNode.__removalCount || 0) + currentNode.parentNode.__depth + 1;
|
|
1411
|
+
} else {
|
|
1412
|
+
currentNode.__depth = 1;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/* Remove an element if nested too deeply to avoid mXSS */
|
|
1417
|
+
if (currentNode.__depth >= MAX_NESTING_DEPTH) {
|
|
1418
|
+
_forceRemove(currentNode);
|
|
1419
|
+
}
|
|
1548
1420
|
|
|
1421
|
+
/* Shadow DOM detected, sanitize it */
|
|
1549
1422
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1423
|
+
currentNode.content.__depth = currentNode.__depth;
|
|
1550
1424
|
_sanitizeShadowDOM(currentNode.content);
|
|
1551
1425
|
}
|
|
1552
|
-
/* Check attributes, sanitize if necessary */
|
|
1553
|
-
|
|
1554
1426
|
|
|
1427
|
+
/* Check attributes, sanitize if necessary */
|
|
1555
1428
|
_sanitizeAttributes(currentNode);
|
|
1556
|
-
|
|
1557
1429
|
oldNode = currentNode;
|
|
1558
1430
|
}
|
|
1559
|
-
|
|
1560
1431
|
oldNode = null;
|
|
1561
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1562
1432
|
|
|
1433
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
1563
1434
|
if (IN_PLACE) {
|
|
1564
1435
|
return dirty;
|
|
1565
1436
|
}
|
|
1566
|
-
/* Return sanitized string or DOM */
|
|
1567
|
-
|
|
1568
1437
|
|
|
1438
|
+
/* Return sanitized string or DOM */
|
|
1569
1439
|
if (RETURN_DOM) {
|
|
1570
1440
|
if (RETURN_DOM_FRAGMENT) {
|
|
1571
1441
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1572
|
-
|
|
1573
1442
|
while (body.firstChild) {
|
|
1574
1443
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1575
1444
|
returnNode.appendChild(body.firstChild);
|
|
@@ -1577,7 +1446,6 @@
|
|
|
1577
1446
|
} else {
|
|
1578
1447
|
returnNode = body;
|
|
1579
1448
|
}
|
|
1580
|
-
|
|
1581
1449
|
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1582
1450
|
/*
|
|
1583
1451
|
AdoptNode() is not used because internal state is not reset
|
|
@@ -1588,51 +1456,45 @@
|
|
|
1588
1456
|
*/
|
|
1589
1457
|
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1590
1458
|
}
|
|
1591
|
-
|
|
1592
1459
|
return returnNode;
|
|
1593
1460
|
}
|
|
1594
|
-
|
|
1595
1461
|
var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1596
|
-
/* Serialize doctype if allowed */
|
|
1597
1462
|
|
|
1463
|
+
/* Serialize doctype if allowed */
|
|
1598
1464
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1599
1465
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1600
1466
|
}
|
|
1601
|
-
/* Sanitize final string template-safe */
|
|
1602
|
-
|
|
1603
1467
|
|
|
1468
|
+
/* Sanitize final string template-safe */
|
|
1604
1469
|
if (SAFE_FOR_TEMPLATES) {
|
|
1605
1470
|
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
|
|
1606
1471
|
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
|
|
1607
1472
|
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
|
|
1608
1473
|
}
|
|
1609
|
-
|
|
1610
1474
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1611
1475
|
};
|
|
1476
|
+
|
|
1612
1477
|
/**
|
|
1613
1478
|
* Public method to set the configuration once
|
|
1614
1479
|
* setConfig
|
|
1615
1480
|
*
|
|
1616
1481
|
* @param {Object} cfg configuration object
|
|
1617
1482
|
*/
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
1483
|
DOMPurify.setConfig = function (cfg) {
|
|
1621
1484
|
_parseConfig(cfg);
|
|
1622
|
-
|
|
1623
1485
|
SET_CONFIG = true;
|
|
1624
1486
|
};
|
|
1487
|
+
|
|
1625
1488
|
/**
|
|
1626
1489
|
* Public method to remove the configuration
|
|
1627
1490
|
* clearConfig
|
|
1628
1491
|
*
|
|
1629
1492
|
*/
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
1493
|
DOMPurify.clearConfig = function () {
|
|
1633
1494
|
CONFIG = null;
|
|
1634
1495
|
SET_CONFIG = false;
|
|
1635
1496
|
};
|
|
1497
|
+
|
|
1636
1498
|
/**
|
|
1637
1499
|
* Public method to check if an attribute value is valid.
|
|
1638
1500
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
@@ -1643,18 +1505,16 @@
|
|
|
1643
1505
|
* @param {string} value Attribute value.
|
|
1644
1506
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1645
1507
|
*/
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
1508
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1649
1509
|
/* Initialize shared config vars if necessary. */
|
|
1650
1510
|
if (!CONFIG) {
|
|
1651
1511
|
_parseConfig({});
|
|
1652
1512
|
}
|
|
1653
|
-
|
|
1654
1513
|
var lcTag = transformCaseFunc(tag);
|
|
1655
1514
|
var lcName = transformCaseFunc(attr);
|
|
1656
1515
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1657
1516
|
};
|
|
1517
|
+
|
|
1658
1518
|
/**
|
|
1659
1519
|
* AddHook
|
|
1660
1520
|
* Public method to add DOMPurify hooks
|
|
@@ -1662,16 +1522,14 @@
|
|
|
1662
1522
|
* @param {String} entryPoint entry point for the hook to add
|
|
1663
1523
|
* @param {Function} hookFunction function to execute
|
|
1664
1524
|
*/
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
1525
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1668
1526
|
if (typeof hookFunction !== 'function') {
|
|
1669
1527
|
return;
|
|
1670
1528
|
}
|
|
1671
|
-
|
|
1672
1529
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1673
1530
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1674
1531
|
};
|
|
1532
|
+
|
|
1675
1533
|
/**
|
|
1676
1534
|
* RemoveHook
|
|
1677
1535
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
@@ -1680,40 +1538,34 @@
|
|
|
1680
1538
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1681
1539
|
* @return {Function} removed(popped) hook
|
|
1682
1540
|
*/
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
1541
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1686
1542
|
if (hooks[entryPoint]) {
|
|
1687
1543
|
return arrayPop(hooks[entryPoint]);
|
|
1688
1544
|
}
|
|
1689
1545
|
};
|
|
1546
|
+
|
|
1690
1547
|
/**
|
|
1691
1548
|
* RemoveHooks
|
|
1692
1549
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1693
1550
|
*
|
|
1694
1551
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1695
1552
|
*/
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
1553
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1699
1554
|
if (hooks[entryPoint]) {
|
|
1700
1555
|
hooks[entryPoint] = [];
|
|
1701
1556
|
}
|
|
1702
1557
|
};
|
|
1558
|
+
|
|
1703
1559
|
/**
|
|
1704
1560
|
* RemoveAllHooks
|
|
1705
1561
|
* Public method to remove all DOMPurify hooks
|
|
1706
1562
|
*
|
|
1707
1563
|
*/
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
1564
|
DOMPurify.removeAllHooks = function () {
|
|
1711
1565
|
hooks = {};
|
|
1712
1566
|
};
|
|
1713
|
-
|
|
1714
1567
|
return DOMPurify;
|
|
1715
1568
|
}
|
|
1716
|
-
|
|
1717
1569
|
var purify = createDOMPurify();
|
|
1718
1570
|
|
|
1719
1571
|
return purify;
|