purify.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. var html = ['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', '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', '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'];
  2. // SVG
  3. var svg = ['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'audio', 'canvas', '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', 'video', 'view', 'vkern'];
  4. var svgFilters = ['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence'];
  5. var mathMl = ['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmuliscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mpspace', 'msqrt', 'mystyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover'];
  6. var text = ['#text'];
  7. var html$1 = ['accept', 'action', 'align', 'alt', 'autocomplete', 'background', 'bgcolor', 'border', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'coords', 'crossorigin', 'datetime', 'default', 'dir', 'disabled', 'download', 'enctype', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'integrity', 'ismap', 'label', 'lang', 'list', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', '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', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns'];
  8. var svg$1 = ['accent-height', 'accumulate', 'additivive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', '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', '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', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', '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'];
  9. var mathMl$1 = ['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', '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'];
  10. var xml = ['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink'];
  11. /* Add properties to a lookup table */
  12. function addToSet(set, array) {
  13. var l = array.length;
  14. while (l--) {
  15. if (typeof array[l] === 'string') {
  16. array[l] = array[l].toLowerCase();
  17. }
  18. set[array[l]] = true;
  19. }
  20. return set;
  21. }
  22. /* Shallow clone an object */
  23. function clone(object) {
  24. var newObject = {};
  25. var property = void 0;
  26. for (property in object) {
  27. if (Object.prototype.hasOwnProperty.call(object, property)) {
  28. newObject[property] = object[property];
  29. }
  30. }
  31. return newObject;
  32. }
  33. var MUSTACHE_EXPR = /\{\{[\s\S]*|[\s\S]*\}\}/gm; // Specify template detection regex for SAFE_FOR_TEMPLATES mode
  34. var ERB_EXPR = /<%[\s\S]*|[\s\S]*%>/gm;
  35. var DATA_ATTR = /^data-[\-\w.\u00B7-\uFFFF]/; // eslint-disable-line no-useless-escape
  36. var ARIA_ATTR = /^aria-[\-\w]+$/; // eslint-disable-line no-useless-escape
  37. var IS_ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i; // eslint-disable-line no-useless-escape
  38. var IS_SCRIPT_OR_DATA = /^(?:\w+script|data):/i;
  39. var ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
  40. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  41. function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
  42. var getGlobal = function getGlobal() {
  43. return typeof window === 'undefined' ? null : window;
  44. };
  45. function createDOMPurify() {
  46. var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
  47. var DOMPurify = function DOMPurify(root) {
  48. return createDOMPurify(root);
  49. };
  50. /**
  51. * Version label, exposed for easier checks
  52. * if DOMPurify is up to date or not
  53. */
  54. DOMPurify.version = '1.0.8';
  55. /**
  56. * Array of elements that DOMPurify removed during sanitation.
  57. * Empty if nothing was removed.
  58. */
  59. DOMPurify.removed = [];
  60. if (!window || !window.document || window.document.nodeType !== 9) {
  61. // Not running in a browser, provide a factory function
  62. // so that you can pass your own Window
  63. DOMPurify.isSupported = false;
  64. return DOMPurify;
  65. }
  66. var originalDocument = window.document;
  67. var useDOMParser = false; // See comment below
  68. var removeTitle = false; // See comment below
  69. var document = window.document;
  70. var DocumentFragment = window.DocumentFragment,
  71. HTMLTemplateElement = window.HTMLTemplateElement,
  72. Node = window.Node,
  73. NodeFilter = window.NodeFilter,
  74. _window$NamedNodeMap = window.NamedNodeMap,
  75. NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
  76. Text = window.Text,
  77. Comment = window.Comment,
  78. DOMParser = window.DOMParser;
  79. // As per issue #47, the web-components registry is inherited by a
  80. // new document created via createHTMLDocument. As per the spec
  81. // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
  82. // a new empty registry is used when creating a template contents owner
  83. // document, so we use that as our parent document to ensure nothing
  84. // is inherited.
  85. if (typeof HTMLTemplateElement === 'function') {
  86. var template = document.createElement('template');
  87. if (template.content && template.content.ownerDocument) {
  88. document = template.content.ownerDocument;
  89. }
  90. }
  91. var _document = document,
  92. implementation = _document.implementation,
  93. createNodeIterator = _document.createNodeIterator,
  94. getElementsByTagName = _document.getElementsByTagName,
  95. createDocumentFragment = _document.createDocumentFragment;
  96. var importNode = originalDocument.importNode;
  97. var hooks = {};
  98. /**
  99. * Expose whether this browser supports running the full DOMPurify.
  100. */
  101. DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && document.documentMode !== 9;
  102. var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
  103. ERB_EXPR$$1 = ERB_EXPR,
  104. DATA_ATTR$$1 = DATA_ATTR,
  105. ARIA_ATTR$$1 = ARIA_ATTR,
  106. IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
  107. ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
  108. var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
  109. /**
  110. * We consider the elements and attributes below to be safe. Ideally
  111. * don't add any new ones but feel free to remove unwanted ones.
  112. */
  113. /* allowed element names */
  114. var ALLOWED_TAGS = null;
  115. var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(svgFilters), _toConsumableArray(mathMl), _toConsumableArray(text)));
  116. /* Allowed attribute names */
  117. var ALLOWED_ATTR = null;
  118. var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(mathMl$1), _toConsumableArray(xml)));
  119. /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
  120. var FORBID_TAGS = null;
  121. /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
  122. var FORBID_ATTR = null;
  123. /* Decide if ARIA attributes are okay */
  124. var ALLOW_ARIA_ATTR = true;
  125. /* Decide if custom data attributes are okay */
  126. var ALLOW_DATA_ATTR = true;
  127. /* Decide if unknown protocols are okay */
  128. var ALLOW_UNKNOWN_PROTOCOLS = false;
  129. /* Output should be safe for jQuery's $() factory? */
  130. var SAFE_FOR_JQUERY = false;
  131. /* Output should be safe for common template engines.
  132. * This means, DOMPurify removes data attributes, mustaches and ERB
  133. */
  134. var SAFE_FOR_TEMPLATES = false;
  135. /* Decide if document with <html>... should be returned */
  136. var WHOLE_DOCUMENT = false;
  137. /* Track whether config is already set on this instance of DOMPurify. */
  138. var SET_CONFIG = false;
  139. /* Decide if all elements (e.g. style, script) must be children of
  140. * document.body. By default, browsers might move them to document.head */
  141. var FORCE_BODY = false;
  142. /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html string.
  143. * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
  144. */
  145. var RETURN_DOM = false;
  146. /* Decide if a DOM `DocumentFragment` should be returned, instead of a html string */
  147. var RETURN_DOM_FRAGMENT = false;
  148. /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
  149. * `Node` is imported into the current `Document`. If this flag is not enabled the
  150. * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
  151. * DOMPurify. */
  152. var RETURN_DOM_IMPORT = false;
  153. /* Output should be free from DOM clobbering attacks? */
  154. var SANITIZE_DOM = true;
  155. /* Keep element content when removing element? */
  156. var KEEP_CONTENT = true;
  157. /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
  158. * of importing it into a new Document and returning a sanitized copy */
  159. var IN_PLACE = false;
  160. /* Allow usage of profiles like html, svg and mathMl */
  161. var USE_PROFILES = {};
  162. /* Tags to ignore content of when KEEP_CONTENT is true */
  163. var FORBID_CONTENTS = addToSet({}, ['audio', 'head', 'math', 'script', 'style', 'template', 'svg', 'video']);
  164. /* Tags that are safe for data: URIs */
  165. var DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image']);
  166. /* Attributes safe for values like "javascript:" */
  167. var URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);
  168. /* Keep a reference to config to pass to hooks */
  169. var CONFIG = null;
  170. /* Ideally, do not touch anything below this line */
  171. /* ______________________________________________ */
  172. var formElement = document.createElement('form');
  173. /**
  174. * _parseConfig
  175. *
  176. * @param {Object} cfg optional config literal
  177. */
  178. // eslint-disable-next-line complexity
  179. var _parseConfig = function _parseConfig(cfg) {
  180. /* Shield configuration object from tampering */
  181. if ((typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
  182. cfg = {};
  183. }
  184. /* Set configuration parameters */
  185. ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
  186. ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
  187. FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
  188. FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
  189. USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
  190. ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
  191. ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
  192. ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
  193. SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false
  194. SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
  195. WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
  196. RETURN_DOM = cfg.RETURN_DOM || false; // Default false
  197. RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
  198. RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT || false; // Default false
  199. FORCE_BODY = cfg.FORCE_BODY || false; // Default false
  200. SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
  201. KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
  202. IN_PLACE = cfg.IN_PLACE || false; // Default false
  203. IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
  204. if (SAFE_FOR_TEMPLATES) {
  205. ALLOW_DATA_ATTR = false;
  206. }
  207. if (RETURN_DOM_FRAGMENT) {
  208. RETURN_DOM = true;
  209. }
  210. /* Parse profile info */
  211. if (USE_PROFILES) {
  212. ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(text)));
  213. ALLOWED_ATTR = [];
  214. if (USE_PROFILES.html === true) {
  215. addToSet(ALLOWED_TAGS, html);
  216. addToSet(ALLOWED_ATTR, html$1);
  217. }
  218. if (USE_PROFILES.svg === true) {
  219. addToSet(ALLOWED_TAGS, svg);
  220. addToSet(ALLOWED_ATTR, svg$1);
  221. addToSet(ALLOWED_ATTR, xml);
  222. }
  223. if (USE_PROFILES.svgFilters === true) {
  224. addToSet(ALLOWED_TAGS, svgFilters);
  225. addToSet(ALLOWED_ATTR, svg$1);
  226. addToSet(ALLOWED_ATTR, xml);
  227. }
  228. if (USE_PROFILES.mathMl === true) {
  229. addToSet(ALLOWED_TAGS, mathMl);
  230. addToSet(ALLOWED_ATTR, mathMl$1);
  231. addToSet(ALLOWED_ATTR, xml);
  232. }
  233. }
  234. /* Merge configuration parameters */
  235. if (cfg.ADD_TAGS) {
  236. if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
  237. ALLOWED_TAGS = clone(ALLOWED_TAGS);
  238. }
  239. addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
  240. }
  241. if (cfg.ADD_ATTR) {
  242. if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
  243. ALLOWED_ATTR = clone(ALLOWED_ATTR);
  244. }
  245. addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
  246. }
  247. if (cfg.ADD_URI_SAFE_ATTR) {
  248. addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
  249. }
  250. /* Add #text in case KEEP_CONTENT is set to true */
  251. if (KEEP_CONTENT) {
  252. ALLOWED_TAGS['#text'] = true;
  253. }
  254. /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
  255. if (WHOLE_DOCUMENT) {
  256. addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
  257. }
  258. /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286 */
  259. if (ALLOWED_TAGS.table) {
  260. addToSet(ALLOWED_TAGS, ['tbody']);
  261. }
  262. // Prevent further manipulation of configuration.
  263. // Not available in IE8, Safari 5, etc.
  264. if (Object && 'freeze' in Object) {
  265. Object.freeze(cfg);
  266. }
  267. CONFIG = cfg;
  268. };
  269. /**
  270. * _forceRemove
  271. *
  272. * @param {Node} node a DOM node
  273. */
  274. var _forceRemove = function _forceRemove(node) {
  275. DOMPurify.removed.push({ element: node });
  276. try {
  277. node.parentNode.removeChild(node);
  278. } catch (err) {
  279. node.outerHTML = '';
  280. }
  281. };
  282. /**
  283. * _removeAttribute
  284. *
  285. * @param {String} name an Attribute name
  286. * @param {Node} node a DOM node
  287. */
  288. var _removeAttribute = function _removeAttribute(name, node) {
  289. try {
  290. DOMPurify.removed.push({
  291. attribute: node.getAttributeNode(name),
  292. from: node
  293. });
  294. } catch (err) {
  295. DOMPurify.removed.push({
  296. attribute: null,
  297. from: node
  298. });
  299. }
  300. node.removeAttribute(name);
  301. };
  302. /**
  303. * _initDocument
  304. *
  305. * @param {String} dirty a string of dirty markup
  306. * @return {Document} a DOM, filled with the dirty markup
  307. */
  308. var _initDocument = function _initDocument(dirty) {
  309. /* Create a HTML document */
  310. var doc = void 0;
  311. var leadingWhitespace = void 0;
  312. if (FORCE_BODY) {
  313. dirty = '<remove></remove>' + dirty;
  314. } else {
  315. /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
  316. var matches = dirty.match(/^[\s]+/);
  317. leadingWhitespace = matches && matches[0];
  318. if (leadingWhitespace) {
  319. dirty = dirty.slice(leadingWhitespace.length);
  320. }
  321. }
  322. /* Use DOMParser to workaround Firefox bug (see comment below) */
  323. if (useDOMParser) {
  324. try {
  325. doc = new DOMParser().parseFromString(dirty, 'text/html');
  326. } catch (err) {}
  327. }
  328. /* Remove title to fix an mXSS bug in older MS Edge */
  329. if (removeTitle) {
  330. addToSet(FORBID_TAGS, ['title']);
  331. }
  332. /* Otherwise use createHTMLDocument, because DOMParser is unsafe in
  333. Safari (see comment below) */
  334. if (!doc || !doc.documentElement) {
  335. doc = implementation.createHTMLDocument('');
  336. var _doc = doc,
  337. body = _doc.body;
  338. body.parentNode.removeChild(body.parentNode.firstElementChild);
  339. body.outerHTML = dirty;
  340. }
  341. if (leadingWhitespace) {
  342. doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
  343. }
  344. /* Work on whole document or just its body */
  345. return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
  346. };
  347. // Firefox uses a different parser for innerHTML rather than
  348. // DOMParser (see https://bugzilla.mozilla.org/show_bug.cgi?id=1205631)
  349. // which means that you *must* use DOMParser, otherwise the output may
  350. // not be safe if used in a document.write context later.
  351. //
  352. // So we feature detect the Firefox bug and use the DOMParser if necessary.
  353. //
  354. // MS Edge, in older versions, is affected by an mXSS behavior. The second
  355. // check tests for the behavior and fixes it if necessary.
  356. if (DOMPurify.isSupported) {
  357. (function () {
  358. try {
  359. var doc = _initDocument('<svg><p><style><img src="</style><img src=x onerror=alert(1)//">');
  360. if (doc.querySelector('svg img')) {
  361. useDOMParser = true;
  362. }
  363. } catch (err) {}
  364. })();
  365. (function () {
  366. try {
  367. var doc = _initDocument('<x/><title>&lt;/title&gt;&lt;img&gt;');
  368. if (doc.querySelector('title').textContent.match(/<\/title/)) {
  369. removeTitle = true;
  370. }
  371. } catch (err) {}
  372. })();
  373. }
  374. /**
  375. * _createIterator
  376. *
  377. * @param {Document} root document/fragment to create iterator for
  378. * @return {Iterator} iterator instance
  379. */
  380. var _createIterator = function _createIterator(root) {
  381. return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
  382. return NodeFilter.FILTER_ACCEPT;
  383. }, false);
  384. };
  385. /**
  386. * _isClobbered
  387. *
  388. * @param {Node} elm element to check for clobbering attacks
  389. * @return {Boolean} true if clobbered, false if safe
  390. */
  391. var _isClobbered = function _isClobbered(elm) {
  392. if (elm instanceof Text || elm instanceof Comment) {
  393. return false;
  394. }
  395. if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function') {
  396. return true;
  397. }
  398. return false;
  399. };
  400. /**
  401. * _isNode
  402. *
  403. * @param {Node} obj object to check whether it's a DOM node
  404. * @return {Boolean} true is object is a DOM node
  405. */
  406. var _isNode = function _isNode(obj) {
  407. return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? obj instanceof Node : obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string';
  408. };
  409. /**
  410. * _executeHook
  411. * Execute user configurable hooks
  412. *
  413. * @param {String} entryPoint Name of the hook's entry point
  414. * @param {Node} currentNode node to work on with the hook
  415. * @param {Object} data additional hook parameters
  416. */
  417. var _executeHook = function _executeHook(entryPoint, currentNode, data) {
  418. if (!hooks[entryPoint]) {
  419. return;
  420. }
  421. hooks[entryPoint].forEach(function (hook) {
  422. hook.call(DOMPurify, currentNode, data, CONFIG);
  423. });
  424. };
  425. /**
  426. * _sanitizeElements
  427. *
  428. * @protect nodeName
  429. * @protect textContent
  430. * @protect removeChild
  431. *
  432. * @param {Node} currentNode to check for permission to exist
  433. * @return {Boolean} true if node was killed, false if left alive
  434. */
  435. var _sanitizeElements = function _sanitizeElements(currentNode) {
  436. var content = void 0;
  437. /* Execute a hook if present */
  438. _executeHook('beforeSanitizeElements', currentNode, null);
  439. /* Check if element is clobbered or can clobber */
  440. if (_isClobbered(currentNode)) {
  441. _forceRemove(currentNode);
  442. return true;
  443. }
  444. /* Now let's check the element's type and name */
  445. var tagName = currentNode.nodeName.toLowerCase();
  446. /* Execute a hook if present */
  447. _executeHook('uponSanitizeElement', currentNode, {
  448. tagName: tagName,
  449. allowedTags: ALLOWED_TAGS
  450. });
  451. /* Remove element if anything forbids its presence */
  452. if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
  453. /* Keep content except for black-listed elements */
  454. if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && typeof currentNode.insertAdjacentHTML === 'function') {
  455. try {
  456. currentNode.insertAdjacentHTML('AfterEnd', currentNode.innerHTML);
  457. } catch (err) {}
  458. }
  459. _forceRemove(currentNode);
  460. return true;
  461. }
  462. /* Convert markup to cover jQuery behavior */
  463. if (SAFE_FOR_JQUERY && !currentNode.firstElementChild && (!currentNode.content || !currentNode.content.firstElementChild) && /</g.test(currentNode.textContent)) {
  464. DOMPurify.removed.push({ element: currentNode.cloneNode() });
  465. if (currentNode.innerHTML) {
  466. currentNode.innerHTML = currentNode.innerHTML.replace(/</g, '&lt;');
  467. } else {
  468. currentNode.innerHTML = currentNode.textContent.replace(/</g, '&lt;');
  469. }
  470. }
  471. /* Sanitize element content to be template-safe */
  472. if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
  473. /* Get the element's text content */
  474. content = currentNode.textContent;
  475. content = content.replace(MUSTACHE_EXPR$$1, ' ');
  476. content = content.replace(ERB_EXPR$$1, ' ');
  477. if (currentNode.textContent !== content) {
  478. DOMPurify.removed.push({ element: currentNode.cloneNode() });
  479. currentNode.textContent = content;
  480. }
  481. }
  482. /* Execute a hook if present */
  483. _executeHook('afterSanitizeElements', currentNode, null);
  484. return false;
  485. };
  486. /**
  487. * _isValidAttribute
  488. *
  489. * @param {string} lcTag Lowercase tag name of containing element.
  490. * @param {string} lcName Lowercase attribute name.
  491. * @param {string} value Attribute value.
  492. * @return {Boolean} Returns true if `value` is valid, otherwise false.
  493. */
  494. var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
  495. /* Make sure attribute cannot clobber */
  496. if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
  497. return false;
  498. }
  499. /* Sanitize attribute content to be template-safe */
  500. if (SAFE_FOR_TEMPLATES) {
  501. value = value.replace(MUSTACHE_EXPR$$1, ' ');
  502. value = value.replace(ERB_EXPR$$1, ' ');
  503. }
  504. /* Allow valid data-* attributes: At least one character after "-"
  505. (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
  506. XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
  507. We don't need to check the value; it's always URI safe. */
  508. if (ALLOW_DATA_ATTR && DATA_ATTR$$1.test(lcName)) {
  509. // This attribute is safe
  510. } else if (ALLOW_ARIA_ATTR && ARIA_ATTR$$1.test(lcName)) {
  511. // This attribute is safe
  512. /* Otherwise, check the name is permitted */
  513. } else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
  514. return false;
  515. /* Check value is safe. First, is attr inert? If so, is safe */
  516. } else if (URI_SAFE_ATTRIBUTES[lcName]) {
  517. // This attribute is safe
  518. /* Check no script, data or unknown possibly unsafe URI
  519. unless we know URI values are safe for that attribute */
  520. } else if (IS_ALLOWED_URI$$1.test(value.replace(ATTR_WHITESPACE$$1, ''))) {
  521. // This attribute is safe
  522. /* Keep image data URIs alive if src/xlink:href is allowed */
  523. /* Further prevent gadget XSS for dynamically built script tags */
  524. } else if ((lcName === 'src' || lcName === 'xlink:href') && lcTag !== 'script' && value.indexOf('data:') === 0 && DATA_URI_TAGS[lcTag]) {
  525. // This attribute is safe
  526. /* Allow unknown protocols: This provides support for links that
  527. are handled by protocol handlers which may be unknown ahead of
  528. time, e.g. fb:, spotify: */
  529. } else if (ALLOW_UNKNOWN_PROTOCOLS && !IS_SCRIPT_OR_DATA$$1.test(value.replace(ATTR_WHITESPACE$$1, ''))) {
  530. // This attribute is safe
  531. /* Check for binary attributes */
  532. // eslint-disable-next-line no-negated-condition
  533. } else if (!value) {
  534. // Binary attributes are safe at this point
  535. /* Anything else, presume unsafe, do not add it back */
  536. } else {
  537. return false;
  538. }
  539. return true;
  540. };
  541. /**
  542. * _sanitizeAttributes
  543. *
  544. * @protect attributes
  545. * @protect nodeName
  546. * @protect removeAttribute
  547. * @protect setAttribute
  548. *
  549. * @param {Node} node to sanitize
  550. */
  551. // eslint-disable-next-line complexity
  552. var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
  553. var attr = void 0;
  554. var value = void 0;
  555. var lcName = void 0;
  556. var idAttr = void 0;
  557. var l = void 0;
  558. /* Execute a hook if present */
  559. _executeHook('beforeSanitizeAttributes', currentNode, null);
  560. var attributes = currentNode.attributes;
  561. /* Check if we have attributes; if not we might have a text node */
  562. if (!attributes) {
  563. return;
  564. }
  565. var hookEvent = {
  566. attrName: '',
  567. attrValue: '',
  568. keepAttr: true,
  569. allowedAttributes: ALLOWED_ATTR
  570. };
  571. l = attributes.length;
  572. /* Go backwards over all attributes; safely remove bad ones */
  573. while (l--) {
  574. attr = attributes[l];
  575. var _attr = attr,
  576. name = _attr.name,
  577. namespaceURI = _attr.namespaceURI;
  578. value = attr.value.trim();
  579. lcName = name.toLowerCase();
  580. /* Execute a hook if present */
  581. hookEvent.attrName = lcName;
  582. hookEvent.attrValue = value;
  583. hookEvent.keepAttr = true;
  584. _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
  585. value = hookEvent.attrValue;
  586. /* Remove attribute */
  587. // Safari (iOS + Mac), last tested v8.0.5, crashes if you try to
  588. // remove a "name" attribute from an <img> tag that has an "id"
  589. // attribute at the time.
  590. if (lcName === 'name' && currentNode.nodeName === 'IMG' && attributes.id) {
  591. idAttr = attributes.id;
  592. attributes = Array.prototype.slice.apply(attributes);
  593. _removeAttribute('id', currentNode);
  594. _removeAttribute(name, currentNode);
  595. if (attributes.indexOf(idAttr) > l) {
  596. currentNode.setAttribute('id', idAttr.value);
  597. }
  598. } else if (
  599. // This works around a bug in Safari, where input[type=file]
  600. // cannot be dynamically set after type has been removed
  601. currentNode.nodeName === 'INPUT' && lcName === 'type' && value === 'file' && (ALLOWED_ATTR[lcName] || !FORBID_ATTR[lcName])) {
  602. continue;
  603. } else {
  604. // This avoids a crash in Safari v9.0 with double-ids.
  605. // The trick is to first set the id to be empty and then to
  606. // remove the attribute
  607. if (name === 'id') {
  608. currentNode.setAttribute(name, '');
  609. }
  610. _removeAttribute(name, currentNode);
  611. }
  612. /* Did the hooks approve of the attribute? */
  613. if (!hookEvent.keepAttr) {
  614. continue;
  615. }
  616. /* Is `value` valid for this attribute? */
  617. var lcTag = currentNode.nodeName.toLowerCase();
  618. if (!_isValidAttribute(lcTag, lcName, value)) {
  619. continue;
  620. }
  621. /* Handle invalid data-* attribute set by try-catching it */
  622. try {
  623. if (namespaceURI) {
  624. currentNode.setAttributeNS(namespaceURI, name, value);
  625. } else {
  626. /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
  627. currentNode.setAttribute(name, value);
  628. }
  629. DOMPurify.removed.pop();
  630. } catch (err) {}
  631. }
  632. /* Execute a hook if present */
  633. _executeHook('afterSanitizeAttributes', currentNode, null);
  634. };
  635. /**
  636. * _sanitizeShadowDOM
  637. *
  638. * @param {DocumentFragment} fragment to iterate over recursively
  639. */
  640. var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
  641. var shadowNode = void 0;
  642. var shadowIterator = _createIterator(fragment);
  643. /* Execute a hook if present */
  644. _executeHook('beforeSanitizeShadowDOM', fragment, null);
  645. while (shadowNode = shadowIterator.nextNode()) {
  646. /* Execute a hook if present */
  647. _executeHook('uponSanitizeShadowNode', shadowNode, null);
  648. /* Sanitize tags and elements */
  649. if (_sanitizeElements(shadowNode)) {
  650. continue;
  651. }
  652. /* Deep shadow DOM detected */
  653. if (shadowNode.content instanceof DocumentFragment) {
  654. _sanitizeShadowDOM(shadowNode.content);
  655. }
  656. /* Check attributes, sanitize if necessary */
  657. _sanitizeAttributes(shadowNode);
  658. }
  659. /* Execute a hook if present */
  660. _executeHook('afterSanitizeShadowDOM', fragment, null);
  661. };
  662. /**
  663. * Sanitize
  664. * Public method providing core sanitation functionality
  665. *
  666. * @param {String|Node} dirty string or DOM node
  667. * @param {Object} configuration object
  668. */
  669. // eslint-disable-next-line complexity
  670. DOMPurify.sanitize = function (dirty, cfg) {
  671. var body = void 0;
  672. var importedNode = void 0;
  673. var currentNode = void 0;
  674. var oldNode = void 0;
  675. var returnNode = void 0;
  676. /* Make sure we have a string to sanitize.
  677. DO NOT return early, as this will return the wrong type if
  678. the user has requested a DOM object rather than a string */
  679. if (!dirty) {
  680. dirty = '<!-->';
  681. }
  682. /* Stringify, in case dirty is an object */
  683. if (typeof dirty !== 'string' && !_isNode(dirty)) {
  684. // eslint-disable-next-line no-negated-condition
  685. if (typeof dirty.toString !== 'function') {
  686. throw new TypeError('toString is not a function');
  687. } else {
  688. dirty = dirty.toString();
  689. if (typeof dirty !== 'string') {
  690. throw new TypeError('dirty is not a string, aborting');
  691. }
  692. }
  693. }
  694. /* Check we can run. Otherwise fall back or ignore */
  695. if (!DOMPurify.isSupported) {
  696. if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
  697. if (typeof dirty === 'string') {
  698. return window.toStaticHTML(dirty);
  699. }
  700. if (_isNode(dirty)) {
  701. return window.toStaticHTML(dirty.outerHTML);
  702. }
  703. }
  704. return dirty;
  705. }
  706. /* Assign config vars */
  707. if (!SET_CONFIG) {
  708. _parseConfig(cfg);
  709. }
  710. /* Clean up removed elements */
  711. DOMPurify.removed = [];
  712. if (IN_PLACE) {
  713. /* No special handling necessary for in-place sanitization */
  714. } else if (dirty instanceof Node) {
  715. /* If dirty is a DOM element, append to an empty document to avoid
  716. elements being stripped by the parser */
  717. body = _initDocument('<!-->');
  718. importedNode = body.ownerDocument.importNode(dirty, true);
  719. if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
  720. /* Node is already a body, use as is */
  721. body = importedNode;
  722. } else {
  723. body.appendChild(importedNode);
  724. }
  725. } else {
  726. /* Exit directly if we have nothing to do */
  727. if (!RETURN_DOM && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
  728. return dirty;
  729. }
  730. /* Initialize the document to work on */
  731. body = _initDocument(dirty);
  732. /* Check we have a DOM node from the data */
  733. if (!body) {
  734. return RETURN_DOM ? null : '';
  735. }
  736. }
  737. /* Remove first element node (ours) if FORCE_BODY is set */
  738. if (body && FORCE_BODY) {
  739. _forceRemove(body.firstChild);
  740. }
  741. /* Get node iterator */
  742. var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
  743. /* Now start iterating over the created document */
  744. while (currentNode = nodeIterator.nextNode()) {
  745. /* Fix IE's strange behavior with manipulated textNodes #89 */
  746. if (currentNode.nodeType === 3 && currentNode === oldNode) {
  747. continue;
  748. }
  749. /* Sanitize tags and elements */
  750. if (_sanitizeElements(currentNode)) {
  751. continue;
  752. }
  753. /* Shadow DOM detected, sanitize it */
  754. if (currentNode.content instanceof DocumentFragment) {
  755. _sanitizeShadowDOM(currentNode.content);
  756. }
  757. /* Check attributes, sanitize if necessary */
  758. _sanitizeAttributes(currentNode);
  759. oldNode = currentNode;
  760. }
  761. /* If we sanitized `dirty` in-place, return it. */
  762. if (IN_PLACE) {
  763. return dirty;
  764. }
  765. /* Return sanitized string or DOM */
  766. if (RETURN_DOM) {
  767. if (RETURN_DOM_FRAGMENT) {
  768. returnNode = createDocumentFragment.call(body.ownerDocument);
  769. while (body.firstChild) {
  770. returnNode.appendChild(body.firstChild);
  771. }
  772. } else {
  773. returnNode = body;
  774. }
  775. if (RETURN_DOM_IMPORT) {
  776. /* AdoptNode() is not used because internal state is not reset
  777. (e.g. the past names map of a HTMLFormElement), this is safe
  778. in theory but we would rather not risk another attack vector.
  779. The state that is cloned by importNode() is explicitly defined
  780. by the specs. */
  781. returnNode = importNode.call(originalDocument, returnNode, true);
  782. }
  783. return returnNode;
  784. }
  785. return WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
  786. };
  787. /**
  788. * Public method to set the configuration once
  789. * setConfig
  790. *
  791. * @param {Object} cfg configuration object
  792. */
  793. DOMPurify.setConfig = function (cfg) {
  794. _parseConfig(cfg);
  795. SET_CONFIG = true;
  796. };
  797. /**
  798. * Public method to remove the configuration
  799. * clearConfig
  800. *
  801. */
  802. DOMPurify.clearConfig = function () {
  803. CONFIG = null;
  804. SET_CONFIG = false;
  805. };
  806. /**
  807. * Public method to check if an attribute value is valid.
  808. * Uses last set config, if any. Otherwise, uses config defaults.
  809. * isValidAttribute
  810. *
  811. * @param {string} tag Tag name of containing element.
  812. * @param {string} attr Attribute name.
  813. * @param {string} value Attribute value.
  814. * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
  815. */
  816. DOMPurify.isValidAttribute = function (tag, attr, value) {
  817. /* Initialize shared config vars if necessary. */
  818. if (!CONFIG) {
  819. _parseConfig({});
  820. }
  821. var lcTag = tag.toLowerCase();
  822. var lcName = attr.toLowerCase();
  823. return _isValidAttribute(lcTag, lcName, value);
  824. };
  825. /**
  826. * AddHook
  827. * Public method to add DOMPurify hooks
  828. *
  829. * @param {String} entryPoint entry point for the hook to add
  830. * @param {Function} hookFunction function to execute
  831. */
  832. DOMPurify.addHook = function (entryPoint, hookFunction) {
  833. if (typeof hookFunction !== 'function') {
  834. return;
  835. }
  836. hooks[entryPoint] = hooks[entryPoint] || [];
  837. hooks[entryPoint].push(hookFunction);
  838. };
  839. /**
  840. * RemoveHook
  841. * Public method to remove a DOMPurify hook at a given entryPoint
  842. * (pops it from the stack of hooks if more are present)
  843. *
  844. * @param {String} entryPoint entry point for the hook to remove
  845. */
  846. DOMPurify.removeHook = function (entryPoint) {
  847. if (hooks[entryPoint]) {
  848. hooks[entryPoint].pop();
  849. }
  850. };
  851. /**
  852. * RemoveHooks
  853. * Public method to remove all DOMPurify hooks at a given entryPoint
  854. *
  855. * @param {String} entryPoint entry point for the hooks to remove
  856. */
  857. DOMPurify.removeHooks = function (entryPoint) {
  858. if (hooks[entryPoint]) {
  859. hooks[entryPoint] = [];
  860. }
  861. };
  862. /**
  863. * RemoveAllHooks
  864. * Public method to remove all DOMPurify hooks
  865. *
  866. */
  867. DOMPurify.removeAllHooks = function () {
  868. hooks = {};
  869. };
  870. return DOMPurify;
  871. }
  872. var purify = createDOMPurify();
  873. export default purify;