diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index bedae342e..8f7479879 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -7,7 +7,7 @@ name: Build and Test on: push: branches: - - main + - 2.x pull_request: jobs: @@ -16,13 +16,13 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x, 17.x, 18.x] + node-version: [16.x, 17.x, 18.x, 19.x, 20.x, 21.x] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'npm' @@ -37,7 +37,8 @@ jobs: with: run: npm run test:ci env: - TEST_BROWSERSTACK: ${{ startsWith(matrix.node-version, '18') }} - TEST_PROBE_ONLY: ${{ github.ref != 'refs/heads/main' }} + TEST_BROWSERSTACK: ${{ startsWith(matrix.node-version, '21') }} + TEST_PROBE_ONLY: ${{ github.ref != 'refs/heads/2.x' }} BS_USERNAME: ${{ secrets.BS_USERNAME }} BS_ACCESSKEY: ${{ secrets.BS_ACCESSKEY }} + diff --git a/LICENSE b/LICENSE index e099aad0f..aed61cbb2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ DOMPurify -Copyright 2015 Mario Heiderich +Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 DOMPurify is free software; you can redistribute it and/or modify it under the terms of either: @@ -9,17 +9,207 @@ b) the Mozilla Public License Version 2.0 ----------------------------------------------------------------------------- -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. ----------------------------------------------------------------------------- Mozilla Public License, version 2.0 diff --git a/README.md b/README.md index f96fa376c..8d26719a0 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. -It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version 2.4.3. +It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version 3.2.3. DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Internet Explorer (10+), Edge, Firefox and Chrome - as well as almost anything else using Blink or WebKit). It doesn't break on MSIE6 or other legacy browsers. It either uses [a fall-back](#what-about-older-browsers-like-msie8) or simply does nothing. -**Note that DOMPurify v2.4.3 is the final version supporting MSIE. For important security updates compatible with MSIE, please use the 2.x branch.** +**Note that DOMPurify v2.5.8 is the latest version supporting MSIE. For important security updates compatible with MSIE, please use the 2.x branch. Do not expect new features here, this branch only exists for compatibility with MSIE.** Our automated tests cover [19 different browsers](https://github.com/cure53/DOMPurify/blob/main/test/karma.custom-launchers.config.js#L5) right now, more to come. We also cover Node.js v14.x, v16.x, v17.x and v18.x, running DOMPurify on [jsdom](https://github.com/jsdom/jsdom). Older Node versions are known to work as well, but hey... no guarantees. diff --git a/bower.json b/bower.json index 726a3e607..c3bacdcaf 100644 --- a/bower.json +++ b/bower.json @@ -1,10 +1,10 @@ { "name": "DOMPurify", - "version": "2.4.3", + "version": "2.5.8", "homepage": "https://github.com/cure53/DOMPurify", "author": "Cure53 ", "description": "A DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG", - "main": "src/purify.js", + "main": "dist/purify.min.js", "keywords": [ "dom", "xss", diff --git a/dist/purify.cjs.js b/dist/purify.cjs.js index 75b865e17..ccdece8b1 100644 --- a/dist/purify.cjs.js +++ b/dist/purify.cjs.js @@ -1,4 +1,4 @@ -/*! @license DOMPurify 2.4.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.4.3/LICENSE */ +/*! @license DOMPurify 2.5.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.5.8/LICENSE */ 'use strict'; @@ -11,21 +11,17 @@ function _typeof(obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } - function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; - return _setPrototypeOf(o, p); } - function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; - try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; @@ -33,7 +29,6 @@ function _isNativeReflectConstruct() { return false; } } - function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; @@ -47,22 +42,17 @@ function _construct(Parent, args, Class) { return instance; }; } - return _construct.apply(null, arguments); } - function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } - function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } - function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); @@ -71,56 +61,46 @@ function _unsupportedIterableToArray(o, minLen) { if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; - for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; - return arr2; } - function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var hasOwnProperty = Object.hasOwnProperty, - setPrototypeOf = Object.setPrototypeOf, - isFrozen = Object.isFrozen, - getPrototypeOf = Object.getPrototypeOf, - getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; + setPrototypeOf = Object.setPrototypeOf, + isFrozen = Object.isFrozen, + getPrototypeOf = Object.getPrototypeOf, + getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var freeze = Object.freeze, - seal = Object.seal, - create = Object.create; // eslint-disable-line import/no-mutable-exports - + seal = Object.seal, + create = Object.create; // eslint-disable-line import/no-mutable-exports var _ref = typeof Reflect !== 'undefined' && Reflect, - apply = _ref.apply, - construct = _ref.construct; - + apply = _ref.apply, + construct = _ref.construct; if (!apply) { apply = function apply(fun, thisValue, args) { return fun.apply(thisValue, args); }; } - if (!freeze) { freeze = function freeze(x) { return x; }; } - if (!seal) { seal = function seal(x) { return x; }; } - if (!construct) { construct = function construct(Func, args) { return _construct(Func, _toConsumableArray(args)); }; } - var arrayForEach = unapply(Array.prototype.forEach); var arrayPop = unapply(Array.prototype.pop); var arrayPush = unapply(Array.prototype.push); @@ -137,7 +117,6 @@ function unapply(func) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } - return apply(func, thisArg, args); }; } @@ -146,101 +125,89 @@ function unconstruct(func) { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } - return construct(func, args); }; } -/* Add properties to a lookup table */ +/* Add properties to a lookup table */ function addToSet(set, array, transformCaseFunc) { - transformCaseFunc = transformCaseFunc ? transformCaseFunc : stringToLowerCase; - + var _transformCaseFunc; + transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase; if (setPrototypeOf) { // Make 'in' and truthy checks like Boolean(set.constructor) // independent of any properties defined on Object.prototype. // Prevent prototype setters from intercepting set as a this value. setPrototypeOf(set, null); } - var l = array.length; - while (l--) { var element = array[l]; - if (typeof element === 'string') { var lcElement = transformCaseFunc(element); - if (lcElement !== element) { // Config presets (e.g. tags.js, attrs.js) are immutable. if (!isFrozen(array)) { array[l] = lcElement; } - element = lcElement; } } - set[element] = true; } - return set; } -/* Shallow clone an object */ +/* Shallow clone an object */ function clone(object) { var newObject = create(null); var property; - for (property in object) { if (apply(hasOwnProperty, object, [property]) === true) { newObject[property] = object[property]; } } - return newObject; } + /* IE10 doesn't support __lookupGetter__ so lets' * simulate it. It also automatically checks * if the prop is function or getter and behaves * accordingly. */ - function lookupGetter(object, prop) { while (object !== null) { var desc = getOwnPropertyDescriptor(object, prop); - if (desc) { if (desc.get) { return unapply(desc.get); } - if (typeof desc.value === 'function') { return unapply(desc.value); } } - object = getPrototypeOf(object); } - function fallbackValue(element) { console.warn('fallback value for', element); return null; } - return fallbackValue; } -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 +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 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']); -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. +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. // We still need to know them so that we can do namespace // checks properly in case one wants to add them to // allow-list. - 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']); -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, -// even those that we disallow by default. +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, +// even those that we disallow by default. var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); var text = freeze(['#text']); @@ -249,24 +216,24 @@ var svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline 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']); var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); +// eslint-disable-next-line unicorn/better-regex var MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode - var ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm); var TMPLIT_EXPR = seal(/\${[\w\W]*}/gm); -var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape - +var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape - 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 ); var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex ); var DOCTYPE_NAME = seal(/^html$/i); +var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i); var getGlobal = function getGlobal() { return typeof window === 'undefined' ? null : window; }; + /** * Creates a no-op policy for internal use only. * Don't export this function outside this module! @@ -275,25 +242,20 @@ var getGlobal = function getGlobal() { * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types * are not supported). */ - - var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { if (_typeof(trustedTypes) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { return null; - } // Allow the callers to control the unique policy name + } + + // Allow the callers to control the unique policy name // by adding a data-tt-policy-suffix to the script element with the DOMPurify. // Policy creation with duplicate names throws in Trusted Types. - - var suffix = null; var ATTR_NAME = 'data-tt-policy-suffix'; - if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { suffix = document.currentScript.getAttribute(ATTR_NAME); } - var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); - try { return trustedTypes.createPolicy(policyName, { createHTML: function createHTML(html) { @@ -311,114 +273,106 @@ var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, return null; } }; - function createDOMPurify() { var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); - var DOMPurify = function DOMPurify(root) { return createDOMPurify(root); }; + /** * Version label, exposed for easier checks * if DOMPurify is up to date or not */ + DOMPurify.version = '2.5.8'; - - DOMPurify.version = '2.4.3'; /** * Array of elements that DOMPurify removed during sanitation. * Empty if nothing was removed. */ - DOMPurify.removed = []; - if (!window || !window.document || window.document.nodeType !== 9) { // Not running in a browser, provide a factory function // so that you can pass your own Window DOMPurify.isSupported = false; return DOMPurify; } - var originalDocument = window.document; var document = window.document; var DocumentFragment = window.DocumentFragment, - HTMLTemplateElement = window.HTMLTemplateElement, - Node = window.Node, - Element = window.Element, - NodeFilter = window.NodeFilter, - _window$NamedNodeMap = window.NamedNodeMap, - NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, - HTMLFormElement = window.HTMLFormElement, - DOMParser = window.DOMParser, - trustedTypes = window.trustedTypes; + HTMLTemplateElement = window.HTMLTemplateElement, + Node = window.Node, + Element = window.Element, + NodeFilter = window.NodeFilter, + _window$NamedNodeMap = window.NamedNodeMap, + NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, + HTMLFormElement = window.HTMLFormElement, + DOMParser = window.DOMParser, + trustedTypes = window.trustedTypes; var ElementPrototype = Element.prototype; var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); var getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); - var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a + var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); + + // As per issue #47, the web-components registry is inherited by a // new document created via createHTMLDocument. As per the spec // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) // a new empty registry is used when creating a template contents owner // document, so we use that as our parent document to ensure nothing // is inherited. - if (typeof HTMLTemplateElement === 'function') { var template = document.createElement('template'); - if (template.content && template.content.ownerDocument) { document = template.content.ownerDocument; } } - var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); - var emptyHTML = trustedTypesPolicy ? trustedTypesPolicy.createHTML('') : ''; var _document = document, - implementation = _document.implementation, - createNodeIterator = _document.createNodeIterator, - createDocumentFragment = _document.createDocumentFragment, - getElementsByTagName = _document.getElementsByTagName; + implementation = _document.implementation, + createNodeIterator = _document.createNodeIterator, + createDocumentFragment = _document.createDocumentFragment, + getElementsByTagName = _document.getElementsByTagName; var importNode = originalDocument.importNode; var documentMode = {}; - try { documentMode = clone(document).documentMode ? document.documentMode : {}; } catch (_) {} - var hooks = {}; + /** * Expose whether this browser supports running the full DOMPurify. */ - - DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; + DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined && documentMode !== 9; var MUSTACHE_EXPR$1 = MUSTACHE_EXPR, - ERB_EXPR$1 = ERB_EXPR, - TMPLIT_EXPR$1 = TMPLIT_EXPR, - DATA_ATTR$1 = DATA_ATTR, - ARIA_ATTR$1 = ARIA_ATTR, - IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, - ATTR_WHITESPACE$1 = ATTR_WHITESPACE; + ERB_EXPR$1 = ERB_EXPR, + TMPLIT_EXPR$1 = TMPLIT_EXPR, + DATA_ATTR$1 = DATA_ATTR, + ARIA_ATTR$1 = ARIA_ATTR, + IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA, + ATTR_WHITESPACE$1 = ATTR_WHITESPACE, + CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT; var IS_ALLOWED_URI$1 = IS_ALLOWED_URI; + /** * We consider the elements and attributes below to be safe. Ideally * don't add any new ones but feel free to remove unwanted ones. */ /* allowed element names */ - var ALLOWED_TAGS = null; var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray(html$1), _toConsumableArray(svg$1), _toConsumableArray(svgFilters), _toConsumableArray(mathMl$1), _toConsumableArray(text))); - /* Allowed attribute names */ + /* Allowed attribute names */ var ALLOWED_ATTR = null; var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray(html), _toConsumableArray(svg), _toConsumableArray(mathMl), _toConsumableArray(xml))); + /* * Configure how DOMPUrify should handle custom elements and their attributes as well as customized built-in elements. * @property {RegExp|Function|null} tagNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any custom elements) * @property {RegExp|Function|null} attributeNameCheck one of [null, regexPattern, predicate]. Default: `null` (disallow any attributes not on the allow list) * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`. */ - var CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, { tagNameCheck: { writable: true, @@ -439,55 +393,65 @@ function createDOMPurify() { value: false } })); - /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ + /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ var FORBID_TAGS = null; - /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ + /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ var FORBID_ATTR = null; - /* Decide if ARIA attributes are okay */ + /* Decide if ARIA attributes are okay */ var ALLOW_ARIA_ATTR = true; - /* Decide if custom data attributes are okay */ + /* Decide if custom data attributes are okay */ var ALLOW_DATA_ATTR = true; - /* Decide if unknown protocols are okay */ + /* Decide if unknown protocols are okay */ var ALLOW_UNKNOWN_PROTOCOLS = false; + + /* Decide if self-closing tags in attributes are allowed. + * Usually removed due to a mXSS issue in jQuery 3.0 */ + var ALLOW_SELF_CLOSE_IN_ATTR = true; + /* Output should be safe for common template engines. * This means, DOMPurify removes data attributes, mustaches and ERB */ - var SAFE_FOR_TEMPLATES = false; - /* Decide if document with ... should be returned */ + /* Output should be safe even for XML used within HTML and alike. + * This means, DOMPurify removes comments when containing risky content. + */ + var SAFE_FOR_XML = true; + + /* Decide if document with ... should be returned */ var WHOLE_DOCUMENT = false; - /* Track whether config is already set on this instance of DOMPurify. */ + /* Track whether config is already set on this instance of DOMPurify. */ var SET_CONFIG = false; + /* Decide if all elements (e.g. style, script) must be children of * document.body. By default, browsers might move them to document.head */ - var FORCE_BODY = false; + /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported). * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead */ - var RETURN_DOM = false; + /* Decide if a DOM `DocumentFragment` should be returned, instead of a html * string (or a TrustedHTML object if Trusted Types are supported) */ - var RETURN_DOM_FRAGMENT = false; + /* Try to return a Trusted Type object instead of a string, return a string in * case Trusted Types are not supported */ - var RETURN_TRUSTED_TYPE = false; + /* Output should be free from DOM clobbering attacks? * This sanitizes markups named with colliding, clobberable built-in DOM APIs. */ - var SANITIZE_DOM = true; + /* Achieve full DOM Clobbering protection by isolating the namespace of named * properties and JS variables, mitigating attacks that abuse the HTML/DOM spec rules. * @@ -501,98 +465,98 @@ function createDOMPurify() { * Namespace isolation is implemented by prefixing `id` and `name` attributes * with a constant string, i.e., `user-content-` */ - var SANITIZE_NAMED_PROPS = false; var SANITIZE_NAMED_PROPS_PREFIX = 'user-content-'; - /* Keep element content when removing element? */ + /* Keep element content when removing element? */ var KEEP_CONTENT = true; + /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead * of importing it into a new Document and returning a sanitized copy */ - var IN_PLACE = false; - /* Allow usage of profiles like html, svg and mathMl */ + /* Allow usage of profiles like html, svg and mathMl */ var USE_PROFILES = {}; - /* Tags to ignore content of when KEEP_CONTENT is true */ + /* Tags to ignore content of when KEEP_CONTENT is true */ var FORBID_CONTENTS = null; 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']); - /* Tags that are safe for data: URIs */ + /* Tags that are safe for data: URIs */ var DATA_URI_TAGS = null; var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); - /* Attributes safe for values like "javascript:" */ + /* Attributes safe for values like "javascript:" */ var URI_SAFE_ATTRIBUTES = null; var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']); var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; /* Document namespace */ - var NAMESPACE = HTML_NAMESPACE; var IS_EMPTY_INPUT = false; - /* Allowed XHTML+XML namespaces */ + /* Allowed XHTML+XML namespaces */ var ALLOWED_NAMESPACES = null; var DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString); - /* Parsing of strict XHTML documents */ + /* Parsing of strict XHTML documents */ var PARSER_MEDIA_TYPE; var SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html']; var DEFAULT_PARSER_MEDIA_TYPE = 'text/html'; var transformCaseFunc; - /* Keep a reference to config to pass to hooks */ + /* Keep a reference to config to pass to hooks */ var CONFIG = null; - /* Ideally, do not touch anything below this line */ + /* Ideally, do not touch anything below this line */ /* ______________________________________________ */ var formElement = document.createElement('form'); - var isRegexOrFunction = function isRegexOrFunction(testValue) { return testValue instanceof RegExp || testValue instanceof Function; }; + /** * _parseConfig * * @param {Object} cfg optional config literal */ // eslint-disable-next-line complexity - - var _parseConfig = function _parseConfig(cfg) { if (CONFIG && CONFIG === cfg) { return; } - /* Shield configuration object from tampering */ - + /* Shield configuration object from tampering */ if (!cfg || _typeof(cfg) !== 'object') { cfg = {}; } - /* Shield configuration object from prototype pollution */ - + /* Shield configuration object from prototype pollution */ cfg = clone(cfg); - PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes - 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. + PARSER_MEDIA_TYPE = + // eslint-disable-next-line unicorn/prefer-includes + 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. transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase; - /* Set configuration parameters */ + /* Set configuration parameters */ ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS; ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR; ALLOWED_NAMESPACES = 'ALLOWED_NAMESPACES' in cfg ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES; - URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), // eslint-disable-line indent - cfg.ADD_URI_SAFE_ATTR, // eslint-disable-line indent + URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), + // eslint-disable-line indent + cfg.ADD_URI_SAFE_ATTR, + // eslint-disable-line indent transformCaseFunc // eslint-disable-line indent ) // eslint-disable-line indent : DEFAULT_URI_SAFE_ATTRIBUTES; - DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), // eslint-disable-line indent - cfg.ADD_DATA_URI_TAGS, // eslint-disable-line indent + DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), + // eslint-disable-line indent + cfg.ADD_DATA_URI_TAGS, + // eslint-disable-line indent transformCaseFunc // eslint-disable-line indent ) // eslint-disable-line indent : DEFAULT_DATA_URI_TAGS; @@ -601,158 +565,128 @@ function createDOMPurify() { FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : {}; USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true - ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true - ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false - + ALLOW_SELF_CLOSE_IN_ATTR = cfg.ALLOW_SELF_CLOSE_IN_ATTR !== false; // Default true SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false - + SAFE_FOR_XML = cfg.SAFE_FOR_XML !== false; // Default true WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false - RETURN_DOM = cfg.RETURN_DOM || false; // Default false - RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false - RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false - FORCE_BODY = cfg.FORCE_BODY || false; // Default false - SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true - SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false - KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true - IN_PLACE = cfg.IN_PLACE || false; // Default false - IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$1; NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE; - + CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || {}; if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) { CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck; } - if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) { CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck; } - if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') { CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements; } - if (SAFE_FOR_TEMPLATES) { ALLOW_DATA_ATTR = false; } - if (RETURN_DOM_FRAGMENT) { RETURN_DOM = true; } - /* Parse profile info */ - + /* Parse profile info */ if (USE_PROFILES) { ALLOWED_TAGS = addToSet({}, _toConsumableArray(text)); ALLOWED_ATTR = []; - if (USE_PROFILES.html === true) { addToSet(ALLOWED_TAGS, html$1); addToSet(ALLOWED_ATTR, html); } - if (USE_PROFILES.svg === true) { addToSet(ALLOWED_TAGS, svg$1); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } - if (USE_PROFILES.svgFilters === true) { addToSet(ALLOWED_TAGS, svgFilters); addToSet(ALLOWED_ATTR, svg); addToSet(ALLOWED_ATTR, xml); } - if (USE_PROFILES.mathMl === true) { addToSet(ALLOWED_TAGS, mathMl$1); addToSet(ALLOWED_ATTR, mathMl); addToSet(ALLOWED_ATTR, xml); } } - /* Merge configuration parameters */ - + /* Merge configuration parameters */ if (cfg.ADD_TAGS) { if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { ALLOWED_TAGS = clone(ALLOWED_TAGS); } - addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc); } - if (cfg.ADD_ATTR) { if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { ALLOWED_ATTR = clone(ALLOWED_ATTR); } - addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc); } - if (cfg.ADD_URI_SAFE_ATTR) { addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc); } - if (cfg.FORBID_CONTENTS) { if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) { FORBID_CONTENTS = clone(FORBID_CONTENTS); } - addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc); } - /* Add #text in case KEEP_CONTENT is set to true */ - + /* Add #text in case KEEP_CONTENT is set to true */ if (KEEP_CONTENT) { ALLOWED_TAGS['#text'] = true; } - /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ - + /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ if (WHOLE_DOCUMENT) { addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); } - /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ - + /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ if (ALLOWED_TAGS.table) { addToSet(ALLOWED_TAGS, ['tbody']); delete FORBID_TAGS.tbody; - } // Prevent further manipulation of configuration. - // Not available in IE8, Safari 5, etc. - + } + // Prevent further manipulation of configuration. + // Not available in IE8, Safari 5, etc. if (freeze) { freeze(cfg); } - CONFIG = cfg; }; - var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); - var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); // Certain elements are allowed in both SVG and HTML + var HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']); + + // Certain elements are allowed in both SVG and HTML // namespace. We need to specify them explicitly // so that they don't get erroneously deleted from // HTML namespace. - var COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ['title', 'style', 'font', 'a', 'script']); + /* Keep track of all possible SVG and MathML tags * so that we can perform the namespace checks * correctly. */ - var ALL_SVG_TAGS = addToSet({}, svg$1); addToSet(ALL_SVG_TAGS, svgFilters); addToSet(ALL_SVG_TAGS, svgDisallowed); var ALL_MATHML_TAGS = addToSet({}, mathMl$1); addToSet(ALL_MATHML_TAGS, mathMlDisallowed); + /** * * @@ -761,64 +695,59 @@ function createDOMPurify() { * namespace that a spec-compliant parser would never * return. Return true otherwise. */ - var _checkValidNamespace = function _checkValidNamespace(element) { - var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode - // can be null. We just simulate parent in this case. + var parent = getParentNode(element); + // In JSDOM, if we're inside shadow DOM, then parentNode + // can be null. We just simulate parent in this case. if (!parent || !parent.tagName) { parent = { namespaceURI: NAMESPACE, tagName: 'template' }; } - var tagName = stringToLowerCase(element.tagName); var parentTagName = stringToLowerCase(parent.tagName); - if (!ALLOWED_NAMESPACES[element.namespaceURI]) { return false; } - if (element.namespaceURI === SVG_NAMESPACE) { // The only way to switch from HTML namespace to SVG // is via . If it happens via any other tag, then // it should be killed. if (parent.namespaceURI === HTML_NAMESPACE) { return tagName === 'svg'; - } // The only way to switch from MathML to SVG is via` + } + + // The only way to switch from MathML to SVG is via` // svg if parent is either or MathML // text integration points. - - if (parent.namespaceURI === MATHML_NAMESPACE) { return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); - } // We only allow elements that are defined in SVG - // spec. All others are disallowed in SVG namespace. - + } + // We only allow elements that are defined in SVG + // spec. All others are disallowed in SVG namespace. return Boolean(ALL_SVG_TAGS[tagName]); } - if (element.namespaceURI === MATHML_NAMESPACE) { // The only way to switch from HTML namespace to MathML // is via . If it happens via any other tag, then // it should be killed. if (parent.namespaceURI === HTML_NAMESPACE) { return tagName === 'math'; - } // The only way to switch from SVG to MathML is via - // and HTML integration points - + } + // The only way to switch from SVG to MathML is via + // and HTML integration points if (parent.namespaceURI === SVG_NAMESPACE) { return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; - } // We only allow elements that are defined in MathML - // spec. All others are disallowed in MathML namespace. - + } + // We only allow elements that are defined in MathML + // spec. All others are disallowed in MathML namespace. return Boolean(ALL_MATHML_TAGS[tagName]); } - if (element.namespaceURI === HTML_NAMESPACE) { // The only way to switch from SVG to HTML is via // HTML integration points, and from MathML to HTML @@ -826,39 +755,36 @@ function createDOMPurify() { if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { return false; } - if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { return false; - } // We disallow tags that are specific for MathML - // or SVG and should never appear in HTML namespace - + } + // We disallow tags that are specific for MathML + // or SVG and should never appear in HTML namespace return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]); - } // For XHTML and XML documents that support custom namespaces - + } + // For XHTML and XML documents that support custom namespaces if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) { return true; - } // The code should never reach this place (this means + } + + // The code should never reach this place (this means // that the element somehow got namespace that is not // HTML, SVG, MathML or allowed via ALLOWED_NAMESPACES). // Return false just in case. - - return false; }; + /** * _forceRemove * * @param {Node} node a DOM node */ - - var _forceRemove = function _forceRemove(node) { arrayPush(DOMPurify.removed, { element: node }); - try { // eslint-disable-next-line unicorn/prefer-dom-node-remove node.parentNode.removeChild(node); @@ -870,14 +796,13 @@ function createDOMPurify() { } } }; + /** * _removeAttribute * * @param {String} name an Attribute name * @param {Node} node a DOM node */ - - var _removeAttribute = function _removeAttribute(name, node) { try { arrayPush(DOMPurify.removed, { @@ -890,9 +815,9 @@ function createDOMPurify() { from: node }); } + node.removeAttribute(name); - node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes - + // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { @@ -905,19 +830,17 @@ function createDOMPurify() { } } }; + /** * _initDocument * * @param {String} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ - - var _initDocument = function _initDocument(dirty) { /* Create a HTML document */ var doc; var leadingWhitespace; - if (FORCE_BODY) { dirty = '' + dirty; } else { @@ -925,83 +848,74 @@ function createDOMPurify() { var matches = stringMatch(dirty, /^[\r\n\t ]+/); leadingWhitespace = matches && matches[0]; } - if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && NAMESPACE === HTML_NAMESPACE) { // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict) dirty = '' + dirty + ''; } - var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; /* * Use the DOMParser API by default, fallback later if needs be * DOMParser not work for svg when has multiple root element. */ - if (NAMESPACE === HTML_NAMESPACE) { try { doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE); } catch (_) {} } - /* Use createHTMLDocument in case DOMParser is not available */ - + /* Use createHTMLDocument in case DOMParser is not available */ if (!doc || !doc.documentElement) { doc = implementation.createDocument(NAMESPACE, 'template', null); - try { doc.documentElement.innerHTML = IS_EMPTY_INPUT ? emptyHTML : dirtyPayload; - } catch (_) {// Syntax error if dirtyPayload is invalid xml + } catch (_) { + // Syntax error if dirtyPayload is invalid xml } } - var body = doc.body || doc.documentElement; - if (dirty && leadingWhitespace) { body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); } - /* Work on whole document or just its body */ - + /* Work on whole document or just its body */ if (NAMESPACE === HTML_NAMESPACE) { return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; } - return WHOLE_DOCUMENT ? doc.documentElement : body; }; + /** * _createIterator * * @param {Document} root document/fragment to create iterator for * @return {Iterator} iterator instance */ - - var _createIterator = function _createIterator(root) { - return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise - NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); + return createNodeIterator.call(root.ownerDocument || root, root, + // eslint-disable-next-line no-bitwise + NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null, false); }; + /** * _isClobbered * * @param {Node} elm element to check for clobbering attacks * @return {Boolean} true if clobbered, false if safe */ - - var _isClobbered = function _isClobbered(elm) { return elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function' || typeof elm.hasChildNodes !== 'function'); }; + /** * _isNode * * @param {Node} obj object to check whether it's a DOM node * @return {Boolean} true is object is a DOM node */ - - var _isNode = function _isNode(object) { return _typeof(Node) === 'object' ? object instanceof Node : object && _typeof(object) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; }; + /** * _executeHook * Execute user configurable hooks @@ -1010,17 +924,15 @@ function createDOMPurify() { * @param {Node} currentNode node to work on with the hook * @param {Object} data additional hook parameters */ - - var _executeHook = function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { return; } - arrayForEach(hooks[entryPoint], function (hook) { hook.call(DOMPurify, currentNode, data, CONFIG); }); }; + /** * _sanitizeElements * @@ -1031,108 +943,101 @@ function createDOMPurify() { * @param {Node} currentNode to check for permission to exist * @return {Boolean} true if node was killed, false if left alive */ - - var _sanitizeElements = function _sanitizeElements(currentNode) { var content; - /* Execute a hook if present */ + /* Execute a hook if present */ _executeHook('beforeSanitizeElements', currentNode, null); - /* Check if element is clobbered or can clobber */ - + /* Check if element is clobbered or can clobber */ if (_isClobbered(currentNode)) { _forceRemove(currentNode); - return true; } - /* Check if tagname contains Unicode */ - + /* Check if tagname contains Unicode */ if (regExpTest(/[\u0080-\uFFFF]/, currentNode.nodeName)) { _forceRemove(currentNode); - return true; } - /* Now let's check the element's type and name */ - + /* Now let's check the element's type and name */ var tagName = transformCaseFunc(currentNode.nodeName); - /* Execute a hook if present */ + /* Execute a hook if present */ _executeHook('uponSanitizeElement', currentNode, { tagName: tagName, allowedTags: ALLOWED_TAGS }); - /* Detect mXSS attempts abusing namespace confusion */ - + /* Detect mXSS attempts abusing namespace confusion */ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { _forceRemove(currentNode); - return true; } - /* Mitigate a problem with templates inside select */ - + /* Mitigate a problem with templates inside select */ if (tagName === 'select' && regExpTest(/