From fbd2ce82860b30f23fd0192e582dd9451356b695 Mon Sep 17 00:00:00 2001 From: ssi02014 Date: Thu, 7 Nov 2024 17:40:38 +0900 Subject: [PATCH] imp: improved types and utils --- dist/purify.cjs.js | 45 ++++++++++++++------------ dist/purify.cjs.js.map | 2 +- dist/purify.es.mjs | 45 ++++++++++++++------------ dist/purify.es.mjs.map | 2 +- dist/purify.js | 45 ++++++++++++++------------ dist/purify.js.map | 2 +- dist/purify.min.js | 45 ++++++++++++++------------ dist/purify.min.js.map | 2 +- src/attrs.ts | 6 ++-- src/purify.ts | 73 ++++++++++++++++++++++++------------------ src/tags.ts | 14 ++++---- src/utils.ts | 25 ++++++++------- 12 files changed, 170 insertions(+), 136 deletions(-) diff --git a/dist/purify.cjs.js b/dist/purify.cjs.js index 28931c2b7..45f554d7d 100644 --- a/dist/purify.cjs.js +++ b/dist/purify.cjs.js @@ -114,8 +114,8 @@ function addToSet(set, array) { /** * Clean up an array to harden against CSPP * - * @param array - The array to be cleaned. - * @returns The cleaned version of the array + * @param {T[]} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array */ function cleanArray(array) { for (let index = 0; index < array.length; index++) { @@ -498,6 +498,7 @@ function createDOMPurify() { * _parseConfig * * @param {Object} cfg optional config literal + * @returns {void} */ // eslint-disable-next-line complexity const _parseConfig = function _parseConfig() { @@ -750,31 +751,32 @@ function createDOMPurify() { /** * _removeAttribute * - * @param {String} name an Attribute name - * @param {Node} node a DOM node + * @param {string} name an Attribute name + * @param {Element} element a DOM node + * @returns {void} */ - const _removeAttribute = function _removeAttribute(name, node) { + const _removeAttribute = function _removeAttribute(name, element) { try { arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node + attribute: element.getAttributeNode(name), + from: element }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, - from: node + from: element }); } - node.removeAttribute(name); + element.removeAttribute(name); // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { - _forceRemove(node); + _forceRemove(element); } catch (_) {} } else { try { - node.setAttribute(name, ''); + element.setAttribute(name, ''); } catch (_) {} } } @@ -782,7 +784,7 @@ function createDOMPurify() { /** * _initDocument * - * @param {String} dirty a string of dirty markup + * @param {string} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ const _initDocument = function _initDocument(dirty) { @@ -844,7 +846,7 @@ function createDOMPurify() { * _isClobbered * * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe + * @return {boolean} true if clobbered, false if safe */ const _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'); @@ -852,11 +854,11 @@ function createDOMPurify() { /** * Checks whether the given object is a DOM node. * - * @param {Node} object object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node + * @param {unknown} value object to check whether it's a DOM node + * @return {value is Node} true is object is a DOM node */ - const _isNode = function _isNode(object) { - return typeof Node === 'function' && object instanceof Node; + const _isNode = function _isNode(value) { + return typeof Node === 'function' && value instanceof Node; }; /** * _executeHook @@ -865,6 +867,7 @@ function createDOMPurify() { * @param entryPoint Name of the hook's entry point * @param currentNode node to work on with the hook * @param {Object} data additional hook parameters + * @returns {void} */ function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { @@ -976,7 +979,7 @@ function createDOMPurify() { * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. - * @return {Boolean} Returns true if `value` is valid, otherwise false. + * @return {boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { @@ -1011,7 +1014,7 @@ function createDOMPurify() { * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name * * @param {string} tagName name of the tag of the node to sanitize - * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. + * @returns {RegExpMatchArray} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. */ const _isBasicCustomElement = function _isBasicCustomElement(tagName) { return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT); @@ -1024,7 +1027,8 @@ function createDOMPurify() { * @protect removeAttribute * @protect setAttribute * - * @param {Node} currentNode to sanitize + * @param {Element} currentNode to sanitize + * @returns {void} */ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) { /* Execute a hook if present */ @@ -1140,6 +1144,7 @@ function createDOMPurify() { * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively + * @returns {void} */ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { let shadowNode = null; diff --git a/dist/purify.cjs.js.map b/dist/purify.cjs.js.map index cd325e0c2..37c4f5238 100644 --- a/dist/purify.cjs.js.map +++ b/dist/purify.cjs.js.map @@ -1 +1 @@ -{"version":3,"file":"purify.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"purify.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/dist/purify.es.mjs b/dist/purify.es.mjs index e18d606f9..34751f52a 100644 --- a/dist/purify.es.mjs +++ b/dist/purify.es.mjs @@ -112,8 +112,8 @@ function addToSet(set, array) { /** * Clean up an array to harden against CSPP * - * @param array - The array to be cleaned. - * @returns The cleaned version of the array + * @param {T[]} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array */ function cleanArray(array) { for (let index = 0; index < array.length; index++) { @@ -496,6 +496,7 @@ function createDOMPurify() { * _parseConfig * * @param {Object} cfg optional config literal + * @returns {void} */ // eslint-disable-next-line complexity const _parseConfig = function _parseConfig() { @@ -748,31 +749,32 @@ function createDOMPurify() { /** * _removeAttribute * - * @param {String} name an Attribute name - * @param {Node} node a DOM node + * @param {string} name an Attribute name + * @param {Element} element a DOM node + * @returns {void} */ - const _removeAttribute = function _removeAttribute(name, node) { + const _removeAttribute = function _removeAttribute(name, element) { try { arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node + attribute: element.getAttributeNode(name), + from: element }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, - from: node + from: element }); } - node.removeAttribute(name); + element.removeAttribute(name); // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { - _forceRemove(node); + _forceRemove(element); } catch (_) {} } else { try { - node.setAttribute(name, ''); + element.setAttribute(name, ''); } catch (_) {} } } @@ -780,7 +782,7 @@ function createDOMPurify() { /** * _initDocument * - * @param {String} dirty a string of dirty markup + * @param {string} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ const _initDocument = function _initDocument(dirty) { @@ -842,7 +844,7 @@ function createDOMPurify() { * _isClobbered * * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe + * @return {boolean} true if clobbered, false if safe */ const _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'); @@ -850,11 +852,11 @@ function createDOMPurify() { /** * Checks whether the given object is a DOM node. * - * @param {Node} object object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node + * @param {unknown} value object to check whether it's a DOM node + * @return {value is Node} true is object is a DOM node */ - const _isNode = function _isNode(object) { - return typeof Node === 'function' && object instanceof Node; + const _isNode = function _isNode(value) { + return typeof Node === 'function' && value instanceof Node; }; /** * _executeHook @@ -863,6 +865,7 @@ function createDOMPurify() { * @param entryPoint Name of the hook's entry point * @param currentNode node to work on with the hook * @param {Object} data additional hook parameters + * @returns {void} */ function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { @@ -974,7 +977,7 @@ function createDOMPurify() { * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. - * @return {Boolean} Returns true if `value` is valid, otherwise false. + * @return {boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { @@ -1009,7 +1012,7 @@ function createDOMPurify() { * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name * * @param {string} tagName name of the tag of the node to sanitize - * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. + * @returns {RegExpMatchArray} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. */ const _isBasicCustomElement = function _isBasicCustomElement(tagName) { return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT); @@ -1022,7 +1025,8 @@ function createDOMPurify() { * @protect removeAttribute * @protect setAttribute * - * @param {Node} currentNode to sanitize + * @param {Element} currentNode to sanitize + * @returns {void} */ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) { /* Execute a hook if present */ @@ -1138,6 +1142,7 @@ function createDOMPurify() { * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively + * @returns {void} */ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { let shadowNode = null; diff --git a/dist/purify.es.mjs.map b/dist/purify.es.mjs.map index 475a9f4b6..464e4d71d 100644 --- a/dist/purify.es.mjs.map +++ b/dist/purify.es.mjs.map @@ -1 +1 @@ -{"version":3,"file":"purify.es.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"purify.es.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/dist/purify.js b/dist/purify.js index 3c008643a..254b5b4e8 100644 --- a/dist/purify.js +++ b/dist/purify.js @@ -118,8 +118,8 @@ /** * Clean up an array to harden against CSPP * - * @param array - The array to be cleaned. - * @returns The cleaned version of the array + * @param {T[]} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array */ function cleanArray(array) { for (let index = 0; index < array.length; index++) { @@ -502,6 +502,7 @@ * _parseConfig * * @param {Object} cfg optional config literal + * @returns {void} */ // eslint-disable-next-line complexity const _parseConfig = function _parseConfig() { @@ -754,31 +755,32 @@ /** * _removeAttribute * - * @param {String} name an Attribute name - * @param {Node} node a DOM node + * @param {string} name an Attribute name + * @param {Element} element a DOM node + * @returns {void} */ - const _removeAttribute = function _removeAttribute(name, node) { + const _removeAttribute = function _removeAttribute(name, element) { try { arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node + attribute: element.getAttributeNode(name), + from: element }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, - from: node + from: element }); } - node.removeAttribute(name); + element.removeAttribute(name); // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { - _forceRemove(node); + _forceRemove(element); } catch (_) {} } else { try { - node.setAttribute(name, ''); + element.setAttribute(name, ''); } catch (_) {} } } @@ -786,7 +788,7 @@ /** * _initDocument * - * @param {String} dirty a string of dirty markup + * @param {string} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ const _initDocument = function _initDocument(dirty) { @@ -848,7 +850,7 @@ * _isClobbered * * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe + * @return {boolean} true if clobbered, false if safe */ const _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'); @@ -856,11 +858,11 @@ /** * Checks whether the given object is a DOM node. * - * @param {Node} object object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node + * @param {unknown} value object to check whether it's a DOM node + * @return {value is Node} true is object is a DOM node */ - const _isNode = function _isNode(object) { - return typeof Node === 'function' && object instanceof Node; + const _isNode = function _isNode(value) { + return typeof Node === 'function' && value instanceof Node; }; /** * _executeHook @@ -869,6 +871,7 @@ * @param entryPoint Name of the hook's entry point * @param currentNode node to work on with the hook * @param {Object} data additional hook parameters + * @returns {void} */ function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { @@ -980,7 +983,7 @@ * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. - * @return {Boolean} Returns true if `value` is valid, otherwise false. + * @return {boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { @@ -1015,7 +1018,7 @@ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name * * @param {string} tagName name of the tag of the node to sanitize - * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. + * @returns {RegExpMatchArray} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. */ const _isBasicCustomElement = function _isBasicCustomElement(tagName) { return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT); @@ -1028,7 +1031,8 @@ * @protect removeAttribute * @protect setAttribute * - * @param {Node} currentNode to sanitize + * @param {Element} currentNode to sanitize + * @returns {void} */ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) { /* Execute a hook if present */ @@ -1144,6 +1148,7 @@ * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively + * @returns {void} */ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { let shadowNode = null; diff --git a/dist/purify.js.map b/dist/purify.js.map index 6f7a27015..8802a8f23 100644 --- a/dist/purify.js.map +++ b/dist/purify.js.map @@ -1 +1 @@ -{"version":3,"file":"purify.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"purify.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/dist/purify.min.js b/dist/purify.min.js index 7cee4760a..92597dbca 100644 --- a/dist/purify.min.js +++ b/dist/purify.min.js @@ -118,8 +118,8 @@ /** * Clean up an array to harden against CSPP * - * @param array - The array to be cleaned. - * @returns The cleaned version of the array + * @param {T[]} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array */ function cleanArray(array) { for (let index = 0; index < array.length; index++) { @@ -502,6 +502,7 @@ * _parseConfig * * @param {Object} cfg optional config literal + * @returns {void} */ // eslint-disable-next-line complexity const _parseConfig = function _parseConfig() { @@ -754,31 +755,32 @@ /** * _removeAttribute * - * @param {String} name an Attribute name - * @param {Node} node a DOM node + * @param {string} name an Attribute name + * @param {Element} element a DOM node + * @returns {void} */ - const _removeAttribute = function _removeAttribute(name, node) { + const _removeAttribute = function _removeAttribute(name, element) { try { arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node + attribute: element.getAttributeNode(name), + from: element }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, - from: node + from: element }); } - node.removeAttribute(name); + element.removeAttribute(name); // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { - _forceRemove(node); + _forceRemove(element); } catch (_) {} } else { try { - node.setAttribute(name, ''); + element.setAttribute(name, ''); } catch (_) {} } } @@ -786,7 +788,7 @@ /** * _initDocument * - * @param {String} dirty a string of dirty markup + * @param {string} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ const _initDocument = function _initDocument(dirty) { @@ -848,7 +850,7 @@ * _isClobbered * * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe + * @return {boolean} true if clobbered, false if safe */ const _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'); @@ -856,11 +858,11 @@ /** * Checks whether the given object is a DOM node. * - * @param {Node} object object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node + * @param {unknown} value object to check whether it's a DOM node + * @return {value is Node} true is object is a DOM node */ - const _isNode = function _isNode(object) { - return typeof Node === 'function' && object instanceof Node; + const _isNode = function _isNode(value) { + return typeof Node === 'function' && value instanceof Node; }; /** * _executeHook @@ -869,6 +871,7 @@ * @param entryPoint Name of the hook's entry point * @param currentNode node to work on with the hook * @param {Object} data additional hook parameters + * @returns {void} */ function _executeHook(entryPoint, currentNode, data) { if (!hooks[entryPoint]) { @@ -980,7 +983,7 @@ * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. - * @return {Boolean} Returns true if `value` is valid, otherwise false. + * @return {boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity const _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { @@ -1015,7 +1018,7 @@ * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name * * @param {string} tagName name of the tag of the node to sanitize - * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. + * @returns {RegExpMatchArray} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. */ const _isBasicCustomElement = function _isBasicCustomElement(tagName) { return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT); @@ -1028,7 +1031,8 @@ * @protect removeAttribute * @protect setAttribute * - * @param {Node} currentNode to sanitize + * @param {Element} currentNode to sanitize + * @returns {void} */ const _sanitizeAttributes = function _sanitizeAttributes(currentNode) { /* Execute a hook if present */ @@ -1144,6 +1148,7 @@ * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively + * @returns {void} */ const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { let shadowNode = null; diff --git a/dist/purify.min.js.map b/dist/purify.min.js.map index 7b10cb3c2..8b65751c9 100644 --- a/dist/purify.min.js.map +++ b/dist/purify.min.js.map @@ -1 +1 @@ -{"version":3,"file":"purify.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file +{"version":3,"file":"purify.min.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/src/attrs.ts b/src/attrs.ts index b9c2aed1e..709d52dee 100644 --- a/src/attrs.ts +++ b/src/attrs.ts @@ -114,7 +114,7 @@ export const html = freeze([ 'wrap', 'xmlns', 'slot', -]); +] as const); export const svg = freeze([ 'accent-height', @@ -304,7 +304,7 @@ export const svg = freeze([ 'y2', 'z', 'zoomandpan', -]); +] as const); export const mathMl = freeze([ 'accent', @@ -368,4 +368,4 @@ export const xml = freeze([ 'xlink:title', 'xml:space', 'xmlns:xlink', -]); +] as const); diff --git a/src/purify.ts b/src/purify.ts index 631af788b..555c4a0eb 100644 --- a/src/purify.ts +++ b/src/purify.ts @@ -423,10 +423,10 @@ function createDOMPurify(window: WindowLike = getGlobal()) { ]); /* Parsing of strict XHTML documents */ - let PARSER_MEDIA_TYPE = null; + let PARSER_MEDIA_TYPE: null | DOMParserSupportedType = null; const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html']; const DEFAULT_PARSER_MEDIA_TYPE = 'text/html'; - let transformCaseFunc = null; + let transformCaseFunc: null | Parameters[2] = null; /* Keep a reference to config to pass to hooks */ let CONFIG: Config | null = null; @@ -436,7 +436,9 @@ function createDOMPurify(window: WindowLike = getGlobal()) { const formElement = document.createElement('form'); - const isRegexOrFunction = function (testValue) { + const isRegexOrFunction = function ( + testValue: unknown + ): testValue is Function | RegExp { return testValue instanceof RegExp || testValue instanceof Function; }; @@ -444,9 +446,10 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * _parseConfig * * @param {Object} cfg optional config literal + * @returns {void} */ // eslint-disable-next-line complexity - const _parseConfig = function (cfg: Config = {}) { + const _parseConfig = function (cfg: Config = {}): void { if (CONFIG && CONFIG === cfg) { return; } @@ -697,7 +700,7 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * namespace that a spec-compliant parser would never * return. Return true otherwise. */ - const _checkValidNamespace = function (element) { + const _checkValidNamespace = function (element: Element): boolean { let parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode @@ -819,33 +822,34 @@ function createDOMPurify(window: WindowLike = getGlobal()) { /** * _removeAttribute * - * @param {String} name an Attribute name - * @param {Node} node a DOM node + * @param {string} name an Attribute name + * @param {Element} element a DOM node + * @returns {void} */ - const _removeAttribute = function (name, node) { + const _removeAttribute = function (name: string, element: Element): void { try { arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node, + attribute: element.getAttributeNode(name), + from: element, }); } catch (_) { arrayPush(DOMPurify.removed, { attribute: null, - from: node, + from: element, }); } - node.removeAttribute(name); + element.removeAttribute(name); // We void attribute values for unremovable "is"" attributes if (name === 'is' && !ALLOWED_ATTR[name]) { if (RETURN_DOM || RETURN_DOM_FRAGMENT) { try { - _forceRemove(node); + _forceRemove(element); } catch (_) {} } else { try { - node.setAttribute(name, ''); + element.setAttribute(name, ''); } catch (_) {} } } @@ -854,10 +858,10 @@ function createDOMPurify(window: WindowLike = getGlobal()) { /** * _initDocument * - * @param {String} dirty a string of dirty markup + * @param {string} dirty a string of dirty markup * @return {Document} a DOM, filled with the dirty markup */ - const _initDocument = function (dirty) { + const _initDocument = function (dirty: string): Document { /* Create a HTML document */ let doc = null; let leadingWhitespace = null; @@ -932,7 +936,7 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * @param {Node} root The root element or node to start traversing on. * @return {NodeIterator} The created NodeIterator */ - const _createNodeIterator = function (root) { + const _createNodeIterator = function (root: Node): NodeIterator { return createNodeIterator.call( root.ownerDocument || root, root, @@ -950,9 +954,9 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * _isClobbered * * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe + * @return {boolean} true if clobbered, false if safe */ - const _isClobbered = function (elm) { + const _isClobbered = function (elm: Node): boolean { return ( elm instanceof HTMLFormElement && (typeof elm.nodeName !== 'string' || @@ -970,11 +974,11 @@ function createDOMPurify(window: WindowLike = getGlobal()) { /** * Checks whether the given object is a DOM node. * - * @param {Node} object object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node + * @param {unknown} value object to check whether it's a DOM node + * @return {value is Node} true is object is a DOM node */ - const _isNode = function (object) { - return typeof Node === 'function' && object instanceof Node; + const _isNode = function (value: unknown): value is Node { + return typeof Node === 'function' && value instanceof Node; }; // The following overloads of `_executeHook` add type-safety to the callers, @@ -1005,12 +1009,13 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * @param entryPoint Name of the hook's entry point * @param currentNode node to work on with the hook * @param {Object} data additional hook parameters + * @returns {void} */ function _executeHook( entryPoint: HookName, currentNode: Node, data: UponSanitizeAttributeHookEvent | UponSanitizeElementHookEvent | null - ) { + ): void { if (!hooks[entryPoint]) { return; } @@ -1161,10 +1166,14 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * @param {string} lcTag Lowercase tag name of containing element. * @param {string} lcName Lowercase attribute name. * @param {string} value Attribute value. - * @return {Boolean} Returns true if `value` is valid, otherwise false. + * @return {boolean} Returns true if `value` is valid, otherwise false. */ // eslint-disable-next-line complexity - const _isValidAttribute = function (lcTag, lcName, value) { + const _isValidAttribute = function ( + lcTag: string, + lcName: string, + value: string + ): boolean { /* Make sure attribute cannot clobber */ if ( SANITIZE_DOM && @@ -1258,9 +1267,9 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name * * @param {string} tagName name of the tag of the node to sanitize - * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. + * @returns {RegExpMatchArray} Returns true if the tag name meets the basic criteria for a custom element, otherwise false. */ - const _isBasicCustomElement = function (tagName) { + const _isBasicCustomElement = function (tagName: string): RegExpMatchArray { return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT); }; @@ -1272,9 +1281,10 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * @protect removeAttribute * @protect setAttribute * - * @param {Node} currentNode to sanitize + * @param {Element} currentNode to sanitize + * @returns {void} */ - const _sanitizeAttributes = function (currentNode) { + const _sanitizeAttributes = function (currentNode: Element): void { /* Execute a hook if present */ _executeHook('beforeSanitizeAttributes', currentNode, null); @@ -1411,8 +1421,9 @@ function createDOMPurify(window: WindowLike = getGlobal()) { * _sanitizeShadowDOM * * @param {DocumentFragment} fragment to iterate over recursively + * @returns {void} */ - const _sanitizeShadowDOM = function (fragment) { + const _sanitizeShadowDOM = function (fragment: DocumentFragment): void { let shadowNode = null; const shadowIterator = _createNodeIterator(fragment); diff --git a/src/tags.ts b/src/tags.ts index 853c7308a..d6ad4643e 100644 --- a/src/tags.ts +++ b/src/tags.ts @@ -118,7 +118,7 @@ export const html = freeze([ 'var', 'video', 'wbr', -]); +] as const); // SVG export const svg = freeze([ @@ -165,7 +165,7 @@ export const svg = freeze([ 'tspan', 'view', 'vkern', -]); +] as const); export const svgFilters = freeze([ 'feBlend', @@ -193,7 +193,7 @@ export const svgFilters = freeze([ 'feSpotLight', 'feTile', 'feTurbulence', -]); +] as const); // List of SVG elements that are disallowed by default. // We still need to know them so that we can do namespace @@ -222,7 +222,7 @@ export const svgDisallowed = freeze([ 'solidcolor', 'unknown', 'use', -]); +] as const); export const mathMl = freeze([ 'math', @@ -255,7 +255,7 @@ export const mathMl = freeze([ 'munder', 'munderover', 'mprescripts', -]); +] as const); // Similarly to SVG, we want to know all MathML elements, // even those that we disallow by default. @@ -275,6 +275,6 @@ export const mathMlDisallowed = freeze([ 'annotation-xml', 'mprescripts', 'none', -]); +] as const); -export const text = freeze(['#text']); +export const text = freeze(['#text'] as const); diff --git a/src/utils.ts b/src/utils.ts index 6fe60579f..d7068a3b6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -58,8 +58,8 @@ const typeErrorCreate = unconstruct(TypeError); * @param func - The function to be wrapped and called. * @returns A new function that calls the given function with a specified thisArg and arguments. */ -function unapply(func: Function): Function { - return (thisArg, ...args) => apply(func, thisArg, args); +function unapply(func: (thisArg: any, ...args: any[]) => T) { + return (thisArg: any, ...args: any[]): T => apply(func, thisArg, args); } /** @@ -68,8 +68,8 @@ function unapply(func: Function): Function { * @param func - The constructor function to be wrapped and called. * @returns A new function that constructs an instance of the given constructor function with the provided arguments. */ -function unconstruct(func: Function): Function { - return (...args) => construct(func, args); +function unconstruct(func: (...args: any[]) => T) { + return (...args: any[]): T => construct(func, args); } /** @@ -83,7 +83,7 @@ function unconstruct(func: Function): Function { function addToSet( set: Record, array: readonly any[], - transformCaseFunc: Function = stringToLowerCase + transformCaseFunc: ReturnType> = stringToLowerCase ): Record { if (setPrototypeOf) { // Make 'in' and truthy checks like Boolean(set.constructor) @@ -116,10 +116,10 @@ function addToSet( /** * Clean up an array to harden against CSPP * - * @param array - The array to be cleaned. - * @returns The cleaned version of the array + * @param {T[]} array - The array to be cleaned. + * @returns {Array} The cleaned version of the array */ -function cleanArray(array: any[]): any[] { +function cleanArray(array: T[]): Array { for (let index = 0; index < array.length; index++) { const isPropertyExist = objectHasOwnProperty(array, index); @@ -137,7 +137,7 @@ function cleanArray(array: any[]): any[] { * @param object - The object to be cloned. * @returns A new object that copies the original. */ -function clone(object: T): T { +function clone>(object: T): T { const newObject = create(null); for (const [property, value] of entries(object)) { @@ -168,7 +168,10 @@ function clone(object: T): T { * @param prop - The property name for which to find the getter function. * @returns The getter function found in the prototype chain or a fallback function. */ -function lookupGetter(object: object, prop: string): Function { +function lookupGetter>( + object: T, + prop: string +): ReturnType> | (() => null) { while (object !== null) { const desc = getOwnPropertyDescriptor(object, prop); @@ -185,7 +188,7 @@ function lookupGetter(object: object, prop: string): Function { object = getPrototypeOf(object); } - function fallbackValue() { + function fallbackValue(): null { return null; }