Test Failed
Push — master ( b103a7...4c2e75 )
by
unknown
17:01 queued 05:50
created

purify.js ➔ _iterableToArray   A

Complexity

Conditions 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
/*! @license DOMPurify 2.3.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.8/LICENSE */
2
3
(function (global, factory) {
4
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
5
  typeof define === 'function' && define.amd ? define(factory) :
6
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.DOMPurify = factory());
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable self is declared in the current environment, consider using typeof self === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
7
})(this, (function () { 'use strict';
8
9 View Code Duplication
  function _typeof(obj) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
10
    "@babel/helpers - typeof";
0 ignored issues
show
Unused Code introduced by
The expression "@babel/helpers - typeof" has no effects. Consider removing it.
Loading history...
11
12
    return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
0 ignored issues
show
Comprehensibility introduced by
It seems like you are trying to overwrite a function name here. _typeof is already defined in line 9 as a function. While this will work, it can be very confusing.
Loading history...
13
      return typeof obj;
14
    } : function (obj) {
15
      return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
16
    }, _typeof(obj);
17
  }
18
19
  function _setPrototypeOf(o, p) {
20
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
21
      o.__proto__ = p;
22
      return o;
23
    };
24
25
    return _setPrototypeOf(o, p);
26
  }
27
28 View Code Duplication
  function _isNativeReflectConstruct() {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
29
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
30
    if (Reflect.construct.sham) return false;
31
    if (typeof Proxy === "function") return true;
32
33
    try {
34
      Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
35
      return true;
36
    } catch (e) {
37
      return false;
38
    }
39
  }
40
41
  function _construct(Parent, args, Class) {
42
    if (_isNativeReflectConstruct()) {
43
      _construct = Reflect.construct;
44
    } else {
45
      _construct = function _construct(Parent, args, Class) {
46
        var a = [null];
47
        a.push.apply(a, args);
48
        var Constructor = Function.bind.apply(Parent, a);
49
        var instance = new Constructor();
50
        if (Class) _setPrototypeOf(instance, Class.prototype);
51
        return instance;
52
      };
53
    }
54
55
    return _construct.apply(null, arguments);
56
  }
57
58
  function _toConsumableArray(arr) {
59
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
60
  }
61
62
  function _arrayWithoutHoles(arr) {
63
    if (Array.isArray(arr)) return _arrayLikeToArray(arr);
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if Array.isArray(arr) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
64
  }
65
66
  function _iterableToArray(iter) {
67
    if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if typeof Symbol !== "unde...er."@@iterator" != null is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Best Practice introduced by
Comparing iter.Symbol.iterator to null using the != operator is not safe. Consider using !== instead.
Loading history...
Best Practice introduced by
Comparing iter."@@iterator" to null using the != operator is not safe. Consider using !== instead.
Loading history...
68
  }
69
70
  function _unsupportedIterableToArray(o, minLen) {
71
    if (!o) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
72
    if (typeof o === "string") return _arrayLikeToArray(o, minLen);
73
    var n = Object.prototype.toString.call(o).slice(8, -1);
74
    if (n === "Object" && o.constructor) n = o.constructor.name;
75
    if (n === "Map" || n === "Set") return Array.from(o);
76
    if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if n === "Arguments" || ^(?...Clamped)?Array$.test(n) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

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

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
1583
        return arrayPop(hooks[entryPoint]);
1584
      }
1585
    };
1586
    /**
1587
     * RemoveHooks
1588
     * Public method to remove all DOMPurify hooks at a given entryPoint
1589
     *
1590
     * @param  {String} entryPoint entry point for the hooks to remove
1591
     */
1592
1593
1594
    DOMPurify.removeHooks = function (entryPoint) {
1595
      if (hooks[entryPoint]) {
1596
        hooks[entryPoint] = [];
1597
      }
1598
    };
1599
    /**
1600
     * RemoveAllHooks
1601
     * Public method to remove all DOMPurify hooks
1602
     *
1603
     */
1604
1605
1606
    DOMPurify.removeAllHooks = function () {
1607
      hooks = {};
1608
    };
1609
1610
    return DOMPurify;
1611
  }
1612
1613
  var purify = createDOMPurify();
1614
1615
  return purify;
1616
1617
}));
1618
//# sourceMappingURL=purify.js.map
1619