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