dompurify 2.4.9 → 2.5.0
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 +1 -1
- package/dist/purify.cjs.js +229 -420
- package/dist/purify.cjs.js.map +1 -1
- package/dist/purify.es.js +229 -420
- package/dist/purify.es.js.map +1 -1
- package/dist/purify.js +229 -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.0 | (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.0/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.0';
|
|
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,98 @@ 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
|
+
/* Ideally, do not touch anything below this line */
|
|
557
511
|
/* ______________________________________________ */
|
|
558
512
|
|
|
559
513
|
var formElement = document.createElement('form');
|
|
560
|
-
|
|
561
514
|
var isRegexOrFunction = function isRegexOrFunction(testValue) {
|
|
562
515
|
return testValue instanceof RegExp || testValue instanceof Function;
|
|
563
516
|
};
|
|
517
|
+
|
|
564
518
|
/**
|
|
565
519
|
* _parseConfig
|
|
566
520
|
*
|
|
567
521
|
* @param {Object} cfg optional config literal
|
|
568
522
|
*/
|
|
569
523
|
// eslint-disable-next-line complexity
|
|
570
|
-
|
|
571
|
-
|
|
572
524
|
var _parseConfig = function _parseConfig(cfg) {
|
|
573
525
|
if (CONFIG && CONFIG === cfg) {
|
|
574
526
|
return;
|
|
575
527
|
}
|
|
576
|
-
/* Shield configuration object from tampering */
|
|
577
|
-
|
|
578
528
|
|
|
529
|
+
/* Shield configuration object from tampering */
|
|
579
530
|
if (!cfg || _typeof(cfg) !== 'object') {
|
|
580
531
|
cfg = {};
|
|
581
532
|
}
|
|
582
|
-
/* Shield configuration object from prototype pollution */
|
|
583
|
-
|
|
584
533
|
|
|
534
|
+
/* Shield configuration object from prototype pollution */
|
|
585
535
|
cfg = clone(cfg);
|
|
586
|
-
PARSER_MEDIA_TYPE =
|
|
587
|
-
|
|
536
|
+
PARSER_MEDIA_TYPE =
|
|
537
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
538
|
+
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
539
|
|
|
540
|
+
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
|
|
589
541
|
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
|
|
590
|
-
/* Set configuration parameters */
|
|
591
542
|
|
|
543
|
+
/* Set configuration parameters */
|
|
592
544
|
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
|
|
593
545
|
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
|
|
594
546
|
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
|
-
|
|
547
|
+
URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES),
|
|
548
|
+
// eslint-disable-line indent
|
|
549
|
+
cfg.ADD_URI_SAFE_ATTR,
|
|
550
|
+
// eslint-disable-line indent
|
|
597
551
|
transformCaseFunc // eslint-disable-line indent
|
|
598
552
|
) // eslint-disable-line indent
|
|
599
553
|
: DEFAULT_URI_SAFE_ATTRIBUTES;
|
|
600
|
-
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
601
|
-
|
|
554
|
+
DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS),
|
|
555
|
+
// eslint-disable-line indent
|
|
556
|
+
cfg.ADD_DATA_URI_TAGS,
|
|
557
|
+
// eslint-disable-line indent
|
|
602
558
|
transformCaseFunc // eslint-disable-line indent
|
|
603
559
|
) // eslint-disable-line indent
|
|
604
560
|
: DEFAULT_DATA_URI_TAGS;
|
|
@@ -607,161 +563,128 @@ function createDOMPurify() {
|
|
|
607
563
|
FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {};
|
|
608
564
|
USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
|
|
609
565
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
|
|
610
|
-
|
|
611
566
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
|
|
612
|
-
|
|
613
567
|
ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
|
|
614
|
-
|
|
615
568
|
ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true
|
|
616
|
-
|
|
617
569
|
SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
|
|
618
|
-
|
|
570
|
+
SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true
|
|
619
571
|
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
|
|
620
|
-
|
|
621
572
|
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
|
|
622
|
-
|
|
623
573
|
RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
|
|
624
|
-
|
|
625
574
|
RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
|
|
626
|
-
|
|
627
575
|
FORCE_BODY = cfg.FORCE_BODY || false; // Default false
|
|
628
|
-
|
|
629
576
|
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
|
|
630
|
-
|
|
631
577
|
SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
|
|
632
|
-
|
|
633
578
|
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
|
|
634
|
-
|
|
635
579
|
IN_PLACE = cfg.IN_PLACE || false; // Default false
|
|
636
|
-
|
|
637
580
|
IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1;
|
|
638
581
|
NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
|
|
639
582
|
CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {};
|
|
640
|
-
|
|
641
583
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
|
|
642
584
|
CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
|
|
643
585
|
}
|
|
644
|
-
|
|
645
586
|
if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
|
|
646
587
|
CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
|
|
647
588
|
}
|
|
648
|
-
|
|
649
589
|
if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
|
|
650
590
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
|
|
651
591
|
}
|
|
652
|
-
|
|
653
592
|
if (SAFE_FOR_TEMPLATES) {
|
|
654
593
|
ALLOW_DATA_ATTR = false;
|
|
655
594
|
}
|
|
656
|
-
|
|
657
595
|
if (RETURN_DOM_FRAGMENT) {
|
|
658
596
|
RETURN_DOM = true;
|
|
659
597
|
}
|
|
660
|
-
/* Parse profile info */
|
|
661
|
-
|
|
662
598
|
|
|
599
|
+
/* Parse profile info */
|
|
663
600
|
if (USE_PROFILES) {
|
|
664
601
|
ALLOWED_TAGS = addToSet({}, _toConsumableArray(text));
|
|
665
602
|
ALLOWED_ATTR = [];
|
|
666
|
-
|
|
667
603
|
if (USE_PROFILES.html === true) {
|
|
668
604
|
addToSet(ALLOWED_TAGS, html$1);
|
|
669
605
|
addToSet(ALLOWED_ATTR, html);
|
|
670
606
|
}
|
|
671
|
-
|
|
672
607
|
if (USE_PROFILES.svg === true) {
|
|
673
608
|
addToSet(ALLOWED_TAGS, svg$1);
|
|
674
609
|
addToSet(ALLOWED_ATTR, svg);
|
|
675
610
|
addToSet(ALLOWED_ATTR, xml);
|
|
676
611
|
}
|
|
677
|
-
|
|
678
612
|
if (USE_PROFILES.svgFilters === true) {
|
|
679
613
|
addToSet(ALLOWED_TAGS, svgFilters);
|
|
680
614
|
addToSet(ALLOWED_ATTR, svg);
|
|
681
615
|
addToSet(ALLOWED_ATTR, xml);
|
|
682
616
|
}
|
|
683
|
-
|
|
684
617
|
if (USE_PROFILES.mathMl === true) {
|
|
685
618
|
addToSet(ALLOWED_TAGS, mathMl$1);
|
|
686
619
|
addToSet(ALLOWED_ATTR, mathMl);
|
|
687
620
|
addToSet(ALLOWED_ATTR, xml);
|
|
688
621
|
}
|
|
689
622
|
}
|
|
690
|
-
/* Merge configuration parameters */
|
|
691
|
-
|
|
692
623
|
|
|
624
|
+
/* Merge configuration parameters */
|
|
693
625
|
if (cfg.ADD_TAGS) {
|
|
694
626
|
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
|
|
695
627
|
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
696
628
|
}
|
|
697
|
-
|
|
698
629
|
addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
|
|
699
630
|
}
|
|
700
|
-
|
|
701
631
|
if (cfg.ADD_ATTR) {
|
|
702
632
|
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
|
|
703
633
|
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
704
634
|
}
|
|
705
|
-
|
|
706
635
|
addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
|
|
707
636
|
}
|
|
708
|
-
|
|
709
637
|
if (cfg.ADD_URI_SAFE_ATTR) {
|
|
710
638
|
addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
|
|
711
639
|
}
|
|
712
|
-
|
|
713
640
|
if (cfg.FORBID_CONTENTS) {
|
|
714
641
|
if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
|
|
715
642
|
FORBID_CONTENTS = clone(FORBID_CONTENTS);
|
|
716
643
|
}
|
|
717
|
-
|
|
718
644
|
addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
|
|
719
645
|
}
|
|
720
|
-
/* Add #text in case KEEP_CONTENT is set to true */
|
|
721
|
-
|
|
722
646
|
|
|
647
|
+
/* Add #text in case KEEP_CONTENT is set to true */
|
|
723
648
|
if (KEEP_CONTENT) {
|
|
724
649
|
ALLOWED_TAGS['#text'] = true;
|
|
725
650
|
}
|
|
726
|
-
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
727
|
-
|
|
728
651
|
|
|
652
|
+
/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
|
|
729
653
|
if (WHOLE_DOCUMENT) {
|
|
730
654
|
addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
|
|
731
655
|
}
|
|
732
|
-
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
733
|
-
|
|
734
656
|
|
|
657
|
+
/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
|
|
735
658
|
if (ALLOWED_TAGS.table) {
|
|
736
659
|
addToSet(ALLOWED_TAGS, ['tbody']);
|
|
737
660
|
delete FORBID_TAGS.tbody;
|
|
738
|
-
}
|
|
739
|
-
// Not available in IE8, Safari 5, etc.
|
|
740
|
-
|
|
661
|
+
}
|
|
741
662
|
|
|
663
|
+
// Prevent further manipulation of configuration.
|
|
664
|
+
// Not available in IE8, Safari 5, etc.
|
|
742
665
|
if (freeze) {
|
|
743
666
|
freeze(cfg);
|
|
744
667
|
}
|
|
745
|
-
|
|
746
668
|
CONFIG = cfg;
|
|
747
669
|
};
|
|
748
|
-
|
|
749
670
|
var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
|
|
750
|
-
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
671
|
+
var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
|
|
672
|
+
|
|
673
|
+
// Certain elements are allowed in both SVG and HTML
|
|
751
674
|
// namespace. We need to specify them explicitly
|
|
752
675
|
// so that they don't get erroneously deleted from
|
|
753
676
|
// HTML namespace.
|
|
754
|
-
|
|
755
677
|
var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
|
|
678
|
+
|
|
756
679
|
/* Keep track of all possible SVG and MathML tags
|
|
757
680
|
* so that we can perform the namespace checks
|
|
758
681
|
* correctly. */
|
|
759
|
-
|
|
760
682
|
var ALL_SVG_TAGS = addToSet({}, svg$1);
|
|
761
683
|
addToSet(ALL_SVG_TAGS, svgFilters);
|
|
762
684
|
addToSet(ALL_SVG_TAGS, svgDisallowed);
|
|
763
685
|
var ALL_MATHML_TAGS = addToSet({}, mathMl$1);
|
|
764
686
|
addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
|
|
687
|
+
|
|
765
688
|
/**
|
|
766
689
|
*
|
|
767
690
|
*
|
|
@@ -770,64 +693,59 @@ function createDOMPurify() {
|
|
|
770
693
|
* namespace that a spec-compliant parser would never
|
|
771
694
|
* return. Return true otherwise.
|
|
772
695
|
*/
|
|
773
|
-
|
|
774
696
|
var _checkValidNamespace = function _checkValidNamespace(element) {
|
|
775
|
-
var parent = getParentNode(element);
|
|
776
|
-
// can be null. We just simulate parent in this case.
|
|
697
|
+
var parent = getParentNode(element);
|
|
777
698
|
|
|
699
|
+
// In JSDOM, if we're inside shadow DOM, then parentNode
|
|
700
|
+
// can be null. We just simulate parent in this case.
|
|
778
701
|
if (!parent || !parent.tagName) {
|
|
779
702
|
parent = {
|
|
780
703
|
namespaceURI: NAMESPACE,
|
|
781
704
|
tagName: 'template'
|
|
782
705
|
};
|
|
783
706
|
}
|
|
784
|
-
|
|
785
707
|
var tagName = stringToLowerCase(element.tagName);
|
|
786
708
|
var parentTagName = stringToLowerCase(parent.tagName);
|
|
787
|
-
|
|
788
709
|
if (!ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
789
710
|
return false;
|
|
790
711
|
}
|
|
791
|
-
|
|
792
712
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
793
713
|
// The only way to switch from HTML namespace to SVG
|
|
794
714
|
// is via <svg>. If it happens via any other tag, then
|
|
795
715
|
// it should be killed.
|
|
796
716
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
797
717
|
return tagName === 'svg';
|
|
798
|
-
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// The only way to switch from MathML to SVG is via`
|
|
799
721
|
// svg if parent is either <annotation-xml> or MathML
|
|
800
722
|
// text integration points.
|
|
801
|
-
|
|
802
|
-
|
|
803
723
|
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
804
724
|
return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
805
|
-
}
|
|
806
|
-
// spec. All others are disallowed in SVG namespace.
|
|
807
|
-
|
|
725
|
+
}
|
|
808
726
|
|
|
727
|
+
// We only allow elements that are defined in SVG
|
|
728
|
+
// spec. All others are disallowed in SVG namespace.
|
|
809
729
|
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
810
730
|
}
|
|
811
|
-
|
|
812
731
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
813
732
|
// The only way to switch from HTML namespace to MathML
|
|
814
733
|
// is via <math>. If it happens via any other tag, then
|
|
815
734
|
// it should be killed.
|
|
816
735
|
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
817
736
|
return tagName === 'math';
|
|
818
|
-
}
|
|
819
|
-
// <math> and HTML integration points
|
|
820
|
-
|
|
737
|
+
}
|
|
821
738
|
|
|
739
|
+
// The only way to switch from SVG to MathML is via
|
|
740
|
+
// <math> and HTML integration points
|
|
822
741
|
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
823
742
|
return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
|
|
824
|
-
}
|
|
825
|
-
// spec. All others are disallowed in MathML namespace.
|
|
826
|
-
|
|
743
|
+
}
|
|
827
744
|
|
|
745
|
+
// We only allow elements that are defined in MathML
|
|
746
|
+
// spec. All others are disallowed in MathML namespace.
|
|
828
747
|
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
829
748
|
}
|
|
830
|
-
|
|
831
749
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
832
750
|
// The only way to switch from SVG to HTML is via
|
|
833
751
|
// HTML integration points, and from MathML to HTML
|
|
@@ -835,39 +753,36 @@ function createDOMPurify() {
|
|
|
835
753
|
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
836
754
|
return false;
|
|
837
755
|
}
|
|
838
|
-
|
|
839
756
|
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
840
757
|
return false;
|
|
841
|
-
}
|
|
842
|
-
// or SVG and should never appear in HTML namespace
|
|
843
|
-
|
|
758
|
+
}
|
|
844
759
|
|
|
760
|
+
// We disallow tags that are specific for MathML
|
|
761
|
+
// or SVG and should never appear in HTML namespace
|
|
845
762
|
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
846
|
-
}
|
|
847
|
-
|
|
763
|
+
}
|
|
848
764
|
|
|
765
|
+
// For XHTML and XML documents that support custom namespaces
|
|
849
766
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
850
767
|
return true;
|
|
851
|
-
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// The code should never reach this place (this means
|
|
852
771
|
// that the element somehow got namespace that is not
|
|
853
772
|
// HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES).
|
|
854
773
|
// Return false just in case.
|
|
855
|
-
|
|
856
|
-
|
|
857
774
|
return false;
|
|
858
775
|
};
|
|
776
|
+
|
|
859
777
|
/**
|
|
860
778
|
* _forceRemove
|
|
861
779
|
*
|
|
862
780
|
* @param {Node} node a DOM node
|
|
863
781
|
*/
|
|
864
|
-
|
|
865
|
-
|
|
866
782
|
var _forceRemove = function _forceRemove(node) {
|
|
867
783
|
arrayPush(DOMPurify.removed, {
|
|
868
784
|
element: node
|
|
869
785
|
});
|
|
870
|
-
|
|
871
786
|
try {
|
|
872
787
|
// eslint-disable-next-line unicorn/prefer-dom-node-remove
|
|
873
788
|
node.parentNode.removeChild(node);
|
|
@@ -879,14 +794,13 @@ function createDOMPurify() {
|
|
|
879
794
|
}
|
|
880
795
|
}
|
|
881
796
|
};
|
|
797
|
+
|
|
882
798
|
/**
|
|
883
799
|
* _removeAttribute
|
|
884
800
|
*
|
|
885
801
|
* @param {String} name an Attribute name
|
|
886
802
|
* @param {Node} node a DOM node
|
|
887
803
|
*/
|
|
888
|
-
|
|
889
|
-
|
|
890
804
|
var _removeAttribute = function _removeAttribute(name, node) {
|
|
891
805
|
try {
|
|
892
806
|
arrayPush(DOMPurify.removed, {
|
|
@@ -899,9 +813,9 @@ function createDOMPurify() {
|
|
|
899
813
|
from: node
|
|
900
814
|
});
|
|
901
815
|
}
|
|
816
|
+
node.removeAttribute(name);
|
|
902
817
|
|
|
903
|
-
|
|
904
|
-
|
|
818
|
+
// We void attribute values for unremovable "is"" attributes
|
|
905
819
|
if (name === 'is' && !ALLOWED_ATTR[name]) {
|
|
906
820
|
if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
|
|
907
821
|
try {
|
|
@@ -914,19 +828,17 @@ function createDOMPurify() {
|
|
|
914
828
|
}
|
|
915
829
|
}
|
|
916
830
|
};
|
|
831
|
+
|
|
917
832
|
/**
|
|
918
833
|
* _initDocument
|
|
919
834
|
*
|
|
920
835
|
* @param {String} dirty a string of dirty markup
|
|
921
836
|
* @return {Document} a DOM, filled with the dirty markup
|
|
922
837
|
*/
|
|
923
|
-
|
|
924
|
-
|
|
925
838
|
var _initDocument = function _initDocument(dirty) {
|
|
926
839
|
/* Create a HTML document */
|
|
927
840
|
var doc;
|
|
928
841
|
var leadingWhitespace;
|
|
929
|
-
|
|
930
842
|
if (FORCE_BODY) {
|
|
931
843
|
dirty = '<remove></remove>' + dirty;
|
|
932
844
|
} else {
|
|
@@ -934,83 +846,74 @@ function createDOMPurify() {
|
|
|
934
846
|
var matches = stringMatch(dirty, /^[\r\n\t ]+/);
|
|
935
847
|
leadingWhitespace = matches && matches[0];
|
|
936
848
|
}
|
|
937
|
-
|
|
938
849
|
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) {
|
|
939
850
|
// Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
|
|
940
851
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
|
|
941
852
|
}
|
|
942
|
-
|
|
943
853
|
var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
944
854
|
/*
|
|
945
855
|
* Use the DOMParser API by default, fallback later if needs be
|
|
946
856
|
* DOMParser not work for svg when has multiple root element.
|
|
947
857
|
*/
|
|
948
|
-
|
|
949
858
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
950
859
|
try {
|
|
951
860
|
doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
952
861
|
} catch (_) {}
|
|
953
862
|
}
|
|
954
|
-
/* Use createHTMLDocument in case DOMParser is not available */
|
|
955
|
-
|
|
956
863
|
|
|
864
|
+
/* Use createHTMLDocument in case DOMParser is not available */
|
|
957
865
|
if (!doc || !doc.documentElement) {
|
|
958
866
|
doc = implementation.createDocument(NAMESPACE, 'template', null);
|
|
959
|
-
|
|
960
867
|
try {
|
|
961
868
|
doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload;
|
|
962
|
-
} catch (_) {
|
|
869
|
+
} catch (_) {
|
|
870
|
+
// Syntax error if dirtyPayload is invalid xml
|
|
963
871
|
}
|
|
964
872
|
}
|
|
965
|
-
|
|
966
873
|
var body = doc.body || doc.documentElement;
|
|
967
|
-
|
|
968
874
|
if (dirty && leadingWhitespace) {
|
|
969
875
|
body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null);
|
|
970
876
|
}
|
|
971
|
-
/* Work on whole document or just its body */
|
|
972
|
-
|
|
973
877
|
|
|
878
|
+
/* Work on whole document or just its body */
|
|
974
879
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
975
880
|
return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
|
|
976
881
|
}
|
|
977
|
-
|
|
978
882
|
return WHOLE_DOCUMENT ? doc.documentElement : body;
|
|
979
883
|
};
|
|
884
|
+
|
|
980
885
|
/**
|
|
981
886
|
* _createIterator
|
|
982
887
|
*
|
|
983
888
|
* @param {Document} root document/fragment to create iterator for
|
|
984
889
|
* @return {Iterator} iterator instance
|
|
985
890
|
*/
|
|
986
|
-
|
|
987
|
-
|
|
988
891
|
var _createIterator = function _createIterator(root) {
|
|
989
|
-
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
892
|
+
return createNodeIterator.call(root.ownerDocument || root, root,
|
|
893
|
+
// eslint-disable-next-line no-bitwise
|
|
990
894
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false);
|
|
991
895
|
};
|
|
896
|
+
|
|
992
897
|
/**
|
|
993
898
|
* _isClobbered
|
|
994
899
|
*
|
|
995
900
|
* @param {Node} elm element to check for clobbering attacks
|
|
996
901
|
* @return {Boolean} true if clobbered, false if safe
|
|
997
902
|
*/
|
|
998
|
-
|
|
999
|
-
|
|
1000
903
|
var _isClobbered = function _isClobbered(elm) {
|
|
1001
904
|
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');
|
|
1002
905
|
};
|
|
906
|
+
|
|
1003
907
|
/**
|
|
1004
908
|
* _isNode
|
|
1005
909
|
*
|
|
1006
910
|
* @param {Node} obj object to check whether it's a DOM node
|
|
1007
911
|
* @return {Boolean} true is object is a DOM node
|
|
1008
912
|
*/
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
913
|
var _isNode = function _isNode(object) {
|
|
1012
914
|
return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
|
|
1013
915
|
};
|
|
916
|
+
|
|
1014
917
|
/**
|
|
1015
918
|
* _executeHook
|
|
1016
919
|
* Execute user configurable hooks
|
|
@@ -1019,17 +922,15 @@ function createDOMPurify() {
|
|
|
1019
922
|
* @param {Node} currentNode node to work on with the hook
|
|
1020
923
|
* @param {Object} data additional hook parameters
|
|
1021
924
|
*/
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
925
|
var _executeHook = function _executeHook(entryPoint, currentNode, data) {
|
|
1025
926
|
if (!hooks[entryPoint]) {
|
|
1026
927
|
return;
|
|
1027
928
|
}
|
|
1028
|
-
|
|
1029
929
|
arrayForEach(hooks[entryPoint], function (hook) {
|
|
1030
930
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
1031
931
|
});
|
|
1032
932
|
};
|
|
933
|
+
|
|
1033
934
|
/**
|
|
1034
935
|
* _sanitizeElements
|
|
1035
936
|
*
|
|
@@ -1040,118 +941,99 @@ function createDOMPurify() {
|
|
|
1040
941
|
* @param {Node} currentNode to check for permission to exist
|
|
1041
942
|
* @return {Boolean} true if node was killed, false if left alive
|
|
1042
943
|
*/
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
944
|
var _sanitizeElements = function _sanitizeElements(currentNode) {
|
|
1046
945
|
var content;
|
|
1047
|
-
/* Execute a hook if present */
|
|
1048
946
|
|
|
947
|
+
/* Execute a hook if present */
|
|
1049
948
|
_executeHook('beforeSanitizeElements', currentNode, null);
|
|
1050
|
-
/* Check if element is clobbered or can clobber */
|
|
1051
|
-
|
|
1052
949
|
|
|
950
|
+
/* Check if element is clobbered or can clobber */
|
|
1053
951
|
if (_isClobbered(currentNode)) {
|
|
1054
952
|
_forceRemove(currentNode);
|
|
1055
|
-
|
|
1056
953
|
return true;
|
|
1057
954
|
}
|
|
1058
|
-
/* Check if tagname contains Unicode */
|
|
1059
|
-
|
|
1060
955
|
|
|
956
|
+
/* Check if tagname contains Unicode */
|
|
1061
957
|
if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) {
|
|
1062
958
|
_forceRemove(currentNode);
|
|
1063
|
-
|
|
1064
959
|
return true;
|
|
1065
960
|
}
|
|
1066
|
-
/* Now let's check the element's type and name */
|
|
1067
|
-
|
|
1068
961
|
|
|
962
|
+
/* Now let's check the element's type and name */
|
|
1069
963
|
var tagName = transformCaseFunc(currentNode.nodeName);
|
|
1070
|
-
/* Execute a hook if present */
|
|
1071
964
|
|
|
965
|
+
/* Execute a hook if present */
|
|
1072
966
|
_executeHook('uponSanitizeElement', currentNode, {
|
|
1073
967
|
tagName: tagName,
|
|
1074
968
|
allowedTags: ALLOWED_TAGS
|
|
1075
969
|
});
|
|
1076
|
-
/* Detect mXSS attempts abusing namespace confusion */
|
|
1077
|
-
|
|
1078
970
|
|
|
971
|
+
/* Detect mXSS attempts abusing namespace confusion */
|
|
1079
972
|
if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
|
|
1080
973
|
_forceRemove(currentNode);
|
|
1081
|
-
|
|
1082
974
|
return true;
|
|
1083
975
|
}
|
|
1084
|
-
/* Mitigate a problem with templates inside select */
|
|
1085
|
-
|
|
1086
976
|
|
|
977
|
+
/* Mitigate a problem with templates inside select */
|
|
1087
978
|
if (tagName === 'select' && regExpTest(/<template/i, currentNode.innerHTML)) {
|
|
1088
979
|
_forceRemove(currentNode);
|
|
1089
|
-
|
|
1090
980
|
return true;
|
|
1091
981
|
}
|
|
1092
|
-
/* Remove any ocurrence of processing instructions */
|
|
1093
|
-
|
|
1094
982
|
|
|
983
|
+
/* Remove any ocurrence of processing instructions */
|
|
1095
984
|
if (currentNode.nodeType === 7) {
|
|
1096
985
|
_forceRemove(currentNode);
|
|
1097
|
-
|
|
1098
986
|
return true;
|
|
1099
987
|
}
|
|
1100
|
-
/* Remove element if anything forbids its presence */
|
|
1101
988
|
|
|
989
|
+
/* Remove any kind of possibly harmful comments */
|
|
990
|
+
if (SAFE_FOR_XML && currentNode.nodeType === 8 && regExpTest(/<[/\w]/g, currentNode.data)) {
|
|
991
|
+
_forceRemove(currentNode);
|
|
992
|
+
return true;
|
|
993
|
+
}
|
|
1102
994
|
|
|
995
|
+
/* Remove element if anything forbids its presence */
|
|
1103
996
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1104
997
|
/* Check if we have a custom element to handle */
|
|
1105
998
|
if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
|
|
1106
999
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
1107
1000
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
|
|
1108
1001
|
}
|
|
1109
|
-
/* Keep content except for bad-listed elements */
|
|
1110
|
-
|
|
1111
1002
|
|
|
1003
|
+
/* Keep content except for bad-listed elements */
|
|
1112
1004
|
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
1113
1005
|
var parentNode = getParentNode(currentNode) || currentNode.parentNode;
|
|
1114
1006
|
var childNodes = getChildNodes(currentNode) || currentNode.childNodes;
|
|
1115
|
-
|
|
1116
1007
|
if (childNodes && parentNode) {
|
|
1117
1008
|
var childCount = childNodes.length;
|
|
1118
|
-
|
|
1119
1009
|
for (var i = childCount - 1; i >= 0; --i) {
|
|
1120
1010
|
parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
|
|
1121
1011
|
}
|
|
1122
1012
|
}
|
|
1123
1013
|
}
|
|
1124
|
-
|
|
1125
1014
|
_forceRemove(currentNode);
|
|
1126
|
-
|
|
1127
1015
|
return true;
|
|
1128
1016
|
}
|
|
1129
|
-
/* Check whether element has a valid namespace */
|
|
1130
|
-
|
|
1131
1017
|
|
|
1018
|
+
/* Check whether element has a valid namespace */
|
|
1132
1019
|
if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
|
|
1133
1020
|
_forceRemove(currentNode);
|
|
1134
|
-
|
|
1135
1021
|
return true;
|
|
1136
1022
|
}
|
|
1137
|
-
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1138
|
-
|
|
1139
1023
|
|
|
1024
|
+
/* Make sure that older browsers don't get fallback-tag mXSS */
|
|
1140
1025
|
if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
|
|
1141
1026
|
_forceRemove(currentNode);
|
|
1142
|
-
|
|
1143
1027
|
return true;
|
|
1144
1028
|
}
|
|
1145
|
-
/* Sanitize element content to be template-safe */
|
|
1146
|
-
|
|
1147
1029
|
|
|
1030
|
+
/* Sanitize element content to be template-safe */
|
|
1148
1031
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
|
|
1149
1032
|
/* Get the element's text content */
|
|
1150
1033
|
content = currentNode.textContent;
|
|
1151
1034
|
content = stringReplace(content, MUSTACHE_EXPR$1, ' ');
|
|
1152
1035
|
content = stringReplace(content, ERB_EXPR$1, ' ');
|
|
1153
1036
|
content = stringReplace(content, TMPLIT_EXPR$1, ' ');
|
|
1154
|
-
|
|
1155
1037
|
if (currentNode.textContent !== content) {
|
|
1156
1038
|
arrayPush(DOMPurify.removed, {
|
|
1157
1039
|
element: currentNode.cloneNode()
|
|
@@ -1159,13 +1041,12 @@ function createDOMPurify() {
|
|
|
1159
1041
|
currentNode.textContent = content;
|
|
1160
1042
|
}
|
|
1161
1043
|
}
|
|
1162
|
-
/* Execute a hook if present */
|
|
1163
|
-
|
|
1164
1044
|
|
|
1045
|
+
/* Execute a hook if present */
|
|
1165
1046
|
_executeHook('afterSanitizeElements', currentNode, null);
|
|
1166
|
-
|
|
1167
1047
|
return false;
|
|
1168
1048
|
};
|
|
1049
|
+
|
|
1169
1050
|
/**
|
|
1170
1051
|
* _isValidAttribute
|
|
1171
1052
|
*
|
|
@@ -1175,47 +1056,44 @@ function createDOMPurify() {
|
|
|
1175
1056
|
* @return {Boolean} Returns true if `value` is valid, otherwise false.
|
|
1176
1057
|
*/
|
|
1177
1058
|
// eslint-disable-next-line complexity
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
1059
|
var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
|
|
1181
1060
|
/* Make sure attribute cannot clobber */
|
|
1182
1061
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
1183
1062
|
return false;
|
|
1184
1063
|
}
|
|
1064
|
+
|
|
1185
1065
|
/* Allow valid data-* attributes: At least one character after "-"
|
|
1186
1066
|
(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
|
|
1187
1067
|
XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
|
|
1188
1068
|
We don't need to check the value; it's always URI safe. */
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
1069
|
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 (
|
|
1070
|
+
if (
|
|
1071
|
+
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
1193
1072
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1194
1073
|
// 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)) ||
|
|
1074
|
+
_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)) ||
|
|
1075
|
+
// Alternative, second condition checks if it's an `is`-attribute, AND
|
|
1196
1076
|
// the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
1197
1077
|
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
1078
|
return false;
|
|
1199
1079
|
}
|
|
1200
1080
|
/* Check value is safe. First, is attr inert? If so, is safe */
|
|
1201
|
-
|
|
1202
1081
|
} 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
1082
|
return false;
|
|
1204
1083
|
} else ;
|
|
1205
|
-
|
|
1206
1084
|
return true;
|
|
1207
1085
|
};
|
|
1086
|
+
|
|
1208
1087
|
/**
|
|
1209
1088
|
* _basicCustomElementCheck
|
|
1210
1089
|
* checks if at least one dash is included in tagName, and it's not the first char
|
|
1211
1090
|
* for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
|
|
1212
1091
|
* @param {string} tagName name of the tag of the node to sanitize
|
|
1213
1092
|
*/
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
1093
|
var _basicCustomElementTest = function _basicCustomElementTest(tagName) {
|
|
1217
1094
|
return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT$1);
|
|
1218
1095
|
};
|
|
1096
|
+
|
|
1219
1097
|
/**
|
|
1220
1098
|
* _sanitizeAttributes
|
|
1221
1099
|
*
|
|
@@ -1226,24 +1104,19 @@ function createDOMPurify() {
|
|
|
1226
1104
|
*
|
|
1227
1105
|
* @param {Node} currentNode to sanitize
|
|
1228
1106
|
*/
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
1107
|
var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
|
|
1232
1108
|
var attr;
|
|
1233
1109
|
var value;
|
|
1234
1110
|
var lcName;
|
|
1235
1111
|
var l;
|
|
1236
1112
|
/* Execute a hook if present */
|
|
1237
|
-
|
|
1238
1113
|
_executeHook('beforeSanitizeAttributes', currentNode, null);
|
|
1239
|
-
|
|
1240
1114
|
var attributes = currentNode.attributes;
|
|
1241
|
-
/* Check if we have attributes; if not we might have a text node */
|
|
1242
1115
|
|
|
1116
|
+
/* Check if we have attributes; if not we might have a text node */
|
|
1243
1117
|
if (!attributes) {
|
|
1244
1118
|
return;
|
|
1245
1119
|
}
|
|
1246
|
-
|
|
1247
1120
|
var hookEvent = {
|
|
1248
1121
|
attrName: '',
|
|
1249
1122
|
attrValue: '',
|
|
@@ -1251,79 +1124,67 @@ function createDOMPurify() {
|
|
|
1251
1124
|
allowedAttributes: ALLOWED_ATTR
|
|
1252
1125
|
};
|
|
1253
1126
|
l = attributes.length;
|
|
1254
|
-
/* Go backwards over all attributes; safely remove bad ones */
|
|
1255
1127
|
|
|
1128
|
+
/* Go backwards over all attributes; safely remove bad ones */
|
|
1256
1129
|
while (l--) {
|
|
1257
1130
|
attr = attributes[l];
|
|
1258
1131
|
var _attr = attr,
|
|
1259
|
-
|
|
1260
|
-
|
|
1132
|
+
name = _attr.name,
|
|
1133
|
+
namespaceURI = _attr.namespaceURI;
|
|
1261
1134
|
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
|
1262
1135
|
lcName = transformCaseFunc(name);
|
|
1263
|
-
/* Execute a hook if present */
|
|
1264
1136
|
|
|
1137
|
+
/* Execute a hook if present */
|
|
1265
1138
|
hookEvent.attrName = lcName;
|
|
1266
1139
|
hookEvent.attrValue = value;
|
|
1267
1140
|
hookEvent.keepAttr = true;
|
|
1268
1141
|
hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
|
|
1269
|
-
|
|
1270
1142
|
_executeHook('uponSanitizeAttribute', currentNode, hookEvent);
|
|
1271
|
-
|
|
1272
1143
|
value = hookEvent.attrValue;
|
|
1273
1144
|
/* Did the hooks approve of the attribute? */
|
|
1274
|
-
|
|
1275
1145
|
if (hookEvent.forceKeepAttr) {
|
|
1276
1146
|
continue;
|
|
1277
1147
|
}
|
|
1278
|
-
/* Remove attribute */
|
|
1279
|
-
|
|
1280
1148
|
|
|
1149
|
+
/* Remove attribute */
|
|
1281
1150
|
_removeAttribute(name, currentNode);
|
|
1282
|
-
/* Did the hooks approve of the attribute? */
|
|
1283
|
-
|
|
1284
1151
|
|
|
1152
|
+
/* Did the hooks approve of the attribute? */
|
|
1285
1153
|
if (!hookEvent.keepAttr) {
|
|
1286
1154
|
continue;
|
|
1287
1155
|
}
|
|
1288
|
-
/* Work around a security issue in jQuery 3.0 */
|
|
1289
|
-
|
|
1290
1156
|
|
|
1157
|
+
/* Work around a security issue in jQuery 3.0 */
|
|
1291
1158
|
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
|
|
1292
1159
|
_removeAttribute(name, currentNode);
|
|
1293
|
-
|
|
1294
1160
|
continue;
|
|
1295
1161
|
}
|
|
1296
|
-
/* Sanitize attribute content to be template-safe */
|
|
1297
|
-
|
|
1298
1162
|
|
|
1163
|
+
/* Sanitize attribute content to be template-safe */
|
|
1299
1164
|
if (SAFE_FOR_TEMPLATES) {
|
|
1300
1165
|
value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
|
|
1301
1166
|
value = stringReplace(value, ERB_EXPR$1, ' ');
|
|
1302
1167
|
value = stringReplace(value, TMPLIT_EXPR$1, ' ');
|
|
1303
1168
|
}
|
|
1304
|
-
/* Is `value` valid for this attribute? */
|
|
1305
|
-
|
|
1306
1169
|
|
|
1170
|
+
/* Is `value` valid for this attribute? */
|
|
1307
1171
|
var lcTag = transformCaseFunc(currentNode.nodeName);
|
|
1308
|
-
|
|
1309
1172
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
1310
1173
|
continue;
|
|
1311
1174
|
}
|
|
1175
|
+
|
|
1312
1176
|
/* Full DOM Clobbering protection via namespace isolation,
|
|
1313
1177
|
* Prefix id and name attributes with `user-content-`
|
|
1314
1178
|
*/
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
1179
|
if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
|
|
1318
1180
|
// Remove the attribute with this value
|
|
1319
|
-
_removeAttribute(name, currentNode);
|
|
1320
|
-
|
|
1181
|
+
_removeAttribute(name, currentNode);
|
|
1321
1182
|
|
|
1183
|
+
// Prefix the value and later re-create the attribute with the sanitized value
|
|
1322
1184
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
1323
1185
|
}
|
|
1324
|
-
/* Handle attributes that require Trusted Types */
|
|
1325
|
-
|
|
1326
1186
|
|
|
1187
|
+
/* Handle attributes that require Trusted Types */
|
|
1327
1188
|
if (trustedTypesPolicy && _typeof(trustedTypes) === 'object' && typeof trustedTypes.getAttributeType === 'function') {
|
|
1328
1189
|
if (namespaceURI) ; else {
|
|
1329
1190
|
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
@@ -1332,7 +1193,6 @@ function createDOMPurify() {
|
|
|
1332
1193
|
value = trustedTypesPolicy.createHTML(value);
|
|
1333
1194
|
break;
|
|
1334
1195
|
}
|
|
1335
|
-
|
|
1336
1196
|
case 'TrustedScriptURL':
|
|
1337
1197
|
{
|
|
1338
1198
|
value = trustedTypesPolicy.createScriptURL(value);
|
|
@@ -1341,9 +1201,8 @@ function createDOMPurify() {
|
|
|
1341
1201
|
}
|
|
1342
1202
|
}
|
|
1343
1203
|
}
|
|
1344
|
-
/* Handle invalid data-* attribute set by try-catching it */
|
|
1345
|
-
|
|
1346
1204
|
|
|
1205
|
+
/* Handle invalid data-* attribute set by try-catching it */
|
|
1347
1206
|
try {
|
|
1348
1207
|
if (namespaceURI) {
|
|
1349
1208
|
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
@@ -1351,56 +1210,47 @@ function createDOMPurify() {
|
|
|
1351
1210
|
/* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
|
|
1352
1211
|
currentNode.setAttribute(name, value);
|
|
1353
1212
|
}
|
|
1354
|
-
|
|
1355
1213
|
arrayPop(DOMPurify.removed);
|
|
1356
1214
|
} catch (_) {}
|
|
1357
1215
|
}
|
|
1358
|
-
/* Execute a hook if present */
|
|
1359
|
-
|
|
1360
1216
|
|
|
1217
|
+
/* Execute a hook if present */
|
|
1361
1218
|
_executeHook('afterSanitizeAttributes', currentNode, null);
|
|
1362
1219
|
};
|
|
1220
|
+
|
|
1363
1221
|
/**
|
|
1364
1222
|
* _sanitizeShadowDOM
|
|
1365
1223
|
*
|
|
1366
1224
|
* @param {DocumentFragment} fragment to iterate over recursively
|
|
1367
1225
|
*/
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
1226
|
var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
|
|
1371
1227
|
var shadowNode;
|
|
1372
|
-
|
|
1373
1228
|
var shadowIterator = _createIterator(fragment);
|
|
1374
|
-
/* Execute a hook if present */
|
|
1375
|
-
|
|
1376
1229
|
|
|
1230
|
+
/* Execute a hook if present */
|
|
1377
1231
|
_executeHook('beforeSanitizeShadowDOM', fragment, null);
|
|
1378
|
-
|
|
1379
1232
|
while (shadowNode = shadowIterator.nextNode()) {
|
|
1380
1233
|
/* Execute a hook if present */
|
|
1381
1234
|
_executeHook('uponSanitizeShadowNode', shadowNode, null);
|
|
1382
|
-
/* Sanitize tags and elements */
|
|
1383
|
-
|
|
1384
1235
|
|
|
1236
|
+
/* Sanitize tags and elements */
|
|
1385
1237
|
if (_sanitizeElements(shadowNode)) {
|
|
1386
1238
|
continue;
|
|
1387
1239
|
}
|
|
1388
|
-
/* Deep shadow DOM detected */
|
|
1389
|
-
|
|
1390
1240
|
|
|
1241
|
+
/* Deep shadow DOM detected */
|
|
1391
1242
|
if (shadowNode.content instanceof DocumentFragment) {
|
|
1392
1243
|
_sanitizeShadowDOM(shadowNode.content);
|
|
1393
1244
|
}
|
|
1394
|
-
/* Check attributes, sanitize if necessary */
|
|
1395
|
-
|
|
1396
1245
|
|
|
1246
|
+
/* Check attributes, sanitize if necessary */
|
|
1397
1247
|
_sanitizeAttributes(shadowNode);
|
|
1398
1248
|
}
|
|
1399
|
-
/* Execute a hook if present */
|
|
1400
|
-
|
|
1401
1249
|
|
|
1250
|
+
/* Execute a hook if present */
|
|
1402
1251
|
_executeHook('afterSanitizeShadowDOM', fragment, null);
|
|
1403
1252
|
};
|
|
1253
|
+
|
|
1404
1254
|
/**
|
|
1405
1255
|
* Sanitize
|
|
1406
1256
|
* Public method providing core sanitation functionality
|
|
@@ -1409,8 +1259,6 @@ function createDOMPurify() {
|
|
|
1409
1259
|
* @param {Object} configuration object
|
|
1410
1260
|
*/
|
|
1411
1261
|
// eslint-disable-next-line complexity
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
1262
|
DOMPurify.sanitize = function (dirty) {
|
|
1415
1263
|
var cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1416
1264
|
var body;
|
|
@@ -1421,19 +1269,15 @@ function createDOMPurify() {
|
|
|
1421
1269
|
/* Make sure we have a string to sanitize.
|
|
1422
1270
|
DO NOT return early, as this will return the wrong type if
|
|
1423
1271
|
the user has requested a DOM object rather than a string */
|
|
1424
|
-
|
|
1425
1272
|
IS_EMPTY_INPUT = !dirty;
|
|
1426
|
-
|
|
1427
1273
|
if (IS_EMPTY_INPUT) {
|
|
1428
1274
|
dirty = '<!-->';
|
|
1429
1275
|
}
|
|
1430
|
-
/* Stringify, in case dirty is an object */
|
|
1431
|
-
|
|
1432
1276
|
|
|
1277
|
+
/* Stringify, in case dirty is an object */
|
|
1433
1278
|
if (typeof dirty !== 'string' && !_isNode(dirty)) {
|
|
1434
1279
|
if (typeof dirty.toString === 'function') {
|
|
1435
1280
|
dirty = dirty.toString();
|
|
1436
|
-
|
|
1437
1281
|
if (typeof dirty !== 'string') {
|
|
1438
1282
|
throw typeErrorCreate('dirty is not a string, aborting');
|
|
1439
1283
|
}
|
|
@@ -1441,43 +1285,36 @@ function createDOMPurify() {
|
|
|
1441
1285
|
throw typeErrorCreate('toString is not a function');
|
|
1442
1286
|
}
|
|
1443
1287
|
}
|
|
1444
|
-
/* Check we can run. Otherwise fall back or ignore */
|
|
1445
|
-
|
|
1446
1288
|
|
|
1289
|
+
/* Check we can run. Otherwise fall back or ignore */
|
|
1447
1290
|
if (!DOMPurify.isSupported) {
|
|
1448
1291
|
if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
|
|
1449
1292
|
if (typeof dirty === 'string') {
|
|
1450
1293
|
return window.toStaticHTML(dirty);
|
|
1451
1294
|
}
|
|
1452
|
-
|
|
1453
1295
|
if (_isNode(dirty)) {
|
|
1454
1296
|
return window.toStaticHTML(dirty.outerHTML);
|
|
1455
1297
|
}
|
|
1456
1298
|
}
|
|
1457
|
-
|
|
1458
1299
|
return dirty;
|
|
1459
1300
|
}
|
|
1460
|
-
/* Assign config vars */
|
|
1461
|
-
|
|
1462
1301
|
|
|
1302
|
+
/* Assign config vars */
|
|
1463
1303
|
if (!SET_CONFIG) {
|
|
1464
1304
|
_parseConfig(cfg);
|
|
1465
1305
|
}
|
|
1466
|
-
/* Clean up removed elements */
|
|
1467
|
-
|
|
1468
1306
|
|
|
1307
|
+
/* Clean up removed elements */
|
|
1469
1308
|
DOMPurify.removed = [];
|
|
1470
|
-
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1471
1309
|
|
|
1310
|
+
/* Check if dirty is correctly typed for IN_PLACE */
|
|
1472
1311
|
if (typeof dirty === 'string') {
|
|
1473
1312
|
IN_PLACE = false;
|
|
1474
1313
|
}
|
|
1475
|
-
|
|
1476
1314
|
if (IN_PLACE) {
|
|
1477
1315
|
/* Do some early pre-sanitization to avoid unsafe root nodes */
|
|
1478
1316
|
if (dirty.nodeName) {
|
|
1479
1317
|
var tagName = transformCaseFunc(dirty.nodeName);
|
|
1480
|
-
|
|
1481
1318
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
1482
1319
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
1483
1320
|
}
|
|
@@ -1487,7 +1324,6 @@ function createDOMPurify() {
|
|
|
1487
1324
|
elements being stripped by the parser */
|
|
1488
1325
|
body = _initDocument('<!---->');
|
|
1489
1326
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
1490
|
-
|
|
1491
1327
|
if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
|
|
1492
1328
|
/* Node is already a body, use as is */
|
|
1493
1329
|
body = importedNode;
|
|
@@ -1499,71 +1335,61 @@ function createDOMPurify() {
|
|
|
1499
1335
|
}
|
|
1500
1336
|
} else {
|
|
1501
1337
|
/* Exit directly if we have nothing to do */
|
|
1502
|
-
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1338
|
+
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
|
|
1339
|
+
// eslint-disable-next-line unicorn/prefer-includes
|
|
1503
1340
|
dirty.indexOf('<') === -1) {
|
|
1504
1341
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
|
|
1505
1342
|
}
|
|
1506
|
-
/* Initialize the document to work on */
|
|
1507
|
-
|
|
1508
1343
|
|
|
1344
|
+
/* Initialize the document to work on */
|
|
1509
1345
|
body = _initDocument(dirty);
|
|
1510
|
-
/* Check we have a DOM node from the data */
|
|
1511
1346
|
|
|
1347
|
+
/* Check we have a DOM node from the data */
|
|
1512
1348
|
if (!body) {
|
|
1513
1349
|
return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : '';
|
|
1514
1350
|
}
|
|
1515
1351
|
}
|
|
1516
|
-
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1517
|
-
|
|
1518
1352
|
|
|
1353
|
+
/* Remove first element node (ours) if FORCE_BODY is set */
|
|
1519
1354
|
if (body && FORCE_BODY) {
|
|
1520
1355
|
_forceRemove(body.firstChild);
|
|
1521
1356
|
}
|
|
1522
|
-
/* Get node iterator */
|
|
1523
|
-
|
|
1524
1357
|
|
|
1358
|
+
/* Get node iterator */
|
|
1525
1359
|
var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
|
|
1526
|
-
/* Now start iterating over the created document */
|
|
1527
|
-
|
|
1528
1360
|
|
|
1361
|
+
/* Now start iterating over the created document */
|
|
1529
1362
|
while (currentNode = nodeIterator.nextNode()) {
|
|
1530
1363
|
/* Fix IE's strange behavior with manipulated textNodes #89 */
|
|
1531
1364
|
if (currentNode.nodeType === 3 && currentNode === oldNode) {
|
|
1532
1365
|
continue;
|
|
1533
1366
|
}
|
|
1534
|
-
/* Sanitize tags and elements */
|
|
1535
|
-
|
|
1536
1367
|
|
|
1368
|
+
/* Sanitize tags and elements */
|
|
1537
1369
|
if (_sanitizeElements(currentNode)) {
|
|
1538
1370
|
continue;
|
|
1539
1371
|
}
|
|
1540
|
-
/* Shadow DOM detected, sanitize it */
|
|
1541
|
-
|
|
1542
1372
|
|
|
1373
|
+
/* Shadow DOM detected, sanitize it */
|
|
1543
1374
|
if (currentNode.content instanceof DocumentFragment) {
|
|
1544
1375
|
_sanitizeShadowDOM(currentNode.content);
|
|
1545
1376
|
}
|
|
1546
|
-
/* Check attributes, sanitize if necessary */
|
|
1547
|
-
|
|
1548
1377
|
|
|
1378
|
+
/* Check attributes, sanitize if necessary */
|
|
1549
1379
|
_sanitizeAttributes(currentNode);
|
|
1550
|
-
|
|
1551
1380
|
oldNode = currentNode;
|
|
1552
1381
|
}
|
|
1553
|
-
|
|
1554
1382
|
oldNode = null;
|
|
1555
|
-
/* If we sanitized `dirty` in-place, return it. */
|
|
1556
1383
|
|
|
1384
|
+
/* If we sanitized `dirty` in-place, return it. */
|
|
1557
1385
|
if (IN_PLACE) {
|
|
1558
1386
|
return dirty;
|
|
1559
1387
|
}
|
|
1560
|
-
/* Return sanitized string or DOM */
|
|
1561
|
-
|
|
1562
1388
|
|
|
1389
|
+
/* Return sanitized string or DOM */
|
|
1563
1390
|
if (RETURN_DOM) {
|
|
1564
1391
|
if (RETURN_DOM_FRAGMENT) {
|
|
1565
1392
|
returnNode = createDocumentFragment.call(body.ownerDocument);
|
|
1566
|
-
|
|
1567
1393
|
while (body.firstChild) {
|
|
1568
1394
|
// eslint-disable-next-line unicorn/prefer-dom-node-append
|
|
1569
1395
|
returnNode.appendChild(body.firstChild);
|
|
@@ -1571,7 +1397,6 @@ function createDOMPurify() {
|
|
|
1571
1397
|
} else {
|
|
1572
1398
|
returnNode = body;
|
|
1573
1399
|
}
|
|
1574
|
-
|
|
1575
1400
|
if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmod) {
|
|
1576
1401
|
/*
|
|
1577
1402
|
AdoptNode() is not used because internal state is not reset
|
|
@@ -1582,51 +1407,45 @@ function createDOMPurify() {
|
|
|
1582
1407
|
*/
|
|
1583
1408
|
returnNode = importNode.call(originalDocument, returnNode, true);
|
|
1584
1409
|
}
|
|
1585
|
-
|
|
1586
1410
|
return returnNode;
|
|
1587
1411
|
}
|
|
1588
|
-
|
|
1589
1412
|
var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
|
|
1590
|
-
/* Serialize doctype if allowed */
|
|
1591
1413
|
|
|
1414
|
+
/* Serialize doctype if allowed */
|
|
1592
1415
|
if (WHOLE_DOCUMENT && ALLOWED_TAGS['!doctype'] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
|
|
1593
1416
|
serializedHTML = '<!DOCTYPE ' + body.ownerDocument.doctype.name + '>\n' + serializedHTML;
|
|
1594
1417
|
}
|
|
1595
|
-
/* Sanitize final string template-safe */
|
|
1596
|
-
|
|
1597
1418
|
|
|
1419
|
+
/* Sanitize final string template-safe */
|
|
1598
1420
|
if (SAFE_FOR_TEMPLATES) {
|
|
1599
1421
|
serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$1, ' ');
|
|
1600
1422
|
serializedHTML = stringReplace(serializedHTML, ERB_EXPR$1, ' ');
|
|
1601
1423
|
serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR$1, ' ');
|
|
1602
1424
|
}
|
|
1603
|
-
|
|
1604
1425
|
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
|
|
1605
1426
|
};
|
|
1427
|
+
|
|
1606
1428
|
/**
|
|
1607
1429
|
* Public method to set the configuration once
|
|
1608
1430
|
* setConfig
|
|
1609
1431
|
*
|
|
1610
1432
|
* @param {Object} cfg configuration object
|
|
1611
1433
|
*/
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
1434
|
DOMPurify.setConfig = function (cfg) {
|
|
1615
1435
|
_parseConfig(cfg);
|
|
1616
|
-
|
|
1617
1436
|
SET_CONFIG = true;
|
|
1618
1437
|
};
|
|
1438
|
+
|
|
1619
1439
|
/**
|
|
1620
1440
|
* Public method to remove the configuration
|
|
1621
1441
|
* clearConfig
|
|
1622
1442
|
*
|
|
1623
1443
|
*/
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
1444
|
DOMPurify.clearConfig = function () {
|
|
1627
1445
|
CONFIG = null;
|
|
1628
1446
|
SET_CONFIG = false;
|
|
1629
1447
|
};
|
|
1448
|
+
|
|
1630
1449
|
/**
|
|
1631
1450
|
* Public method to check if an attribute value is valid.
|
|
1632
1451
|
* Uses last set config, if any. Otherwise, uses config defaults.
|
|
@@ -1637,18 +1456,16 @@ function createDOMPurify() {
|
|
|
1637
1456
|
* @param {string} value Attribute value.
|
|
1638
1457
|
* @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
|
|
1639
1458
|
*/
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
1459
|
DOMPurify.isValidAttribute = function (tag, attr, value) {
|
|
1643
1460
|
/* Initialize shared config vars if necessary. */
|
|
1644
1461
|
if (!CONFIG) {
|
|
1645
1462
|
_parseConfig({});
|
|
1646
1463
|
}
|
|
1647
|
-
|
|
1648
1464
|
var lcTag = transformCaseFunc(tag);
|
|
1649
1465
|
var lcName = transformCaseFunc(attr);
|
|
1650
1466
|
return _isValidAttribute(lcTag, lcName, value);
|
|
1651
1467
|
};
|
|
1468
|
+
|
|
1652
1469
|
/**
|
|
1653
1470
|
* AddHook
|
|
1654
1471
|
* Public method to add DOMPurify hooks
|
|
@@ -1656,16 +1473,14 @@ function createDOMPurify() {
|
|
|
1656
1473
|
* @param {String} entryPoint entry point for the hook to add
|
|
1657
1474
|
* @param {Function} hookFunction function to execute
|
|
1658
1475
|
*/
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
1476
|
DOMPurify.addHook = function (entryPoint, hookFunction) {
|
|
1662
1477
|
if (typeof hookFunction !== 'function') {
|
|
1663
1478
|
return;
|
|
1664
1479
|
}
|
|
1665
|
-
|
|
1666
1480
|
hooks[entryPoint] = hooks[entryPoint] || [];
|
|
1667
1481
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
1668
1482
|
};
|
|
1483
|
+
|
|
1669
1484
|
/**
|
|
1670
1485
|
* RemoveHook
|
|
1671
1486
|
* Public method to remove a DOMPurify hook at a given entryPoint
|
|
@@ -1674,40 +1489,34 @@ function createDOMPurify() {
|
|
|
1674
1489
|
* @param {String} entryPoint entry point for the hook to remove
|
|
1675
1490
|
* @return {Function} removed(popped) hook
|
|
1676
1491
|
*/
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
1492
|
DOMPurify.removeHook = function (entryPoint) {
|
|
1680
1493
|
if (hooks[entryPoint]) {
|
|
1681
1494
|
return arrayPop(hooks[entryPoint]);
|
|
1682
1495
|
}
|
|
1683
1496
|
};
|
|
1497
|
+
|
|
1684
1498
|
/**
|
|
1685
1499
|
* RemoveHooks
|
|
1686
1500
|
* Public method to remove all DOMPurify hooks at a given entryPoint
|
|
1687
1501
|
*
|
|
1688
1502
|
* @param {String} entryPoint entry point for the hooks to remove
|
|
1689
1503
|
*/
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
1504
|
DOMPurify.removeHooks = function (entryPoint) {
|
|
1693
1505
|
if (hooks[entryPoint]) {
|
|
1694
1506
|
hooks[entryPoint] = [];
|
|
1695
1507
|
}
|
|
1696
1508
|
};
|
|
1509
|
+
|
|
1697
1510
|
/**
|
|
1698
1511
|
* RemoveAllHooks
|
|
1699
1512
|
* Public method to remove all DOMPurify hooks
|
|
1700
1513
|
*
|
|
1701
1514
|
*/
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
1515
|
DOMPurify.removeAllHooks = function () {
|
|
1705
1516
|
hooks = {};
|
|
1706
1517
|
};
|
|
1707
|
-
|
|
1708
1518
|
return DOMPurify;
|
|
1709
1519
|
}
|
|
1710
|
-
|
|
1711
1520
|
var purify = createDOMPurify();
|
|
1712
1521
|
|
|
1713
1522
|
export { purify as default };
|