dompurify 3.0.5 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/purify.es.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.0.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.5/LICENSE */
1
+ /*! @license DOMPurify 3.0.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.6/LICENSE */
2
2
 
3
3
  const {
4
4
  entries,
@@ -18,12 +18,6 @@ let {
18
18
  construct
19
19
  } = typeof Reflect !== 'undefined' && Reflect;
20
20
 
21
- if (!apply) {
22
- apply = function apply(fun, thisValue, args) {
23
- return fun.apply(thisValue, args);
24
- };
25
- }
26
-
27
21
  if (!freeze) {
28
22
  freeze = function freeze(x) {
29
23
  return x;
@@ -36,6 +30,12 @@ if (!seal) {
36
30
  };
37
31
  }
38
32
 
33
+ if (!apply) {
34
+ apply = function apply(fun, thisValue, args) {
35
+ return fun.apply(thisValue, args);
36
+ };
37
+ }
38
+
39
39
  if (!construct) {
40
40
  construct = function construct(Func, args) {
41
41
  return new Func(...args);
@@ -53,6 +53,13 @@ const stringIndexOf = unapply(String.prototype.indexOf);
53
53
  const stringTrim = unapply(String.prototype.trim);
54
54
  const regExpTest = unapply(RegExp.prototype.test);
55
55
  const typeErrorCreate = unconstruct(TypeError);
56
+ /**
57
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
58
+ *
59
+ * @param {Function} func - The function to be wrapped and called.
60
+ * @returns {Function} A new function that calls the given function with a specified thisArg and arguments.
61
+ */
62
+
56
63
  function unapply(func) {
57
64
  return function (thisArg) {
58
65
  for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
@@ -62,6 +69,14 @@ function unapply(func) {
62
69
  return apply(func, thisArg, args);
63
70
  };
64
71
  }
72
+ /**
73
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
74
+ *
75
+ * @param {Function} func - The constructor function to be wrapped and called.
76
+ * @returns {Function} A new function that constructs an instance of the given constructor function with the provided arguments.
77
+ */
78
+
79
+
65
80
  function unconstruct(func) {
66
81
  return function () {
67
82
  for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
@@ -71,12 +86,18 @@ function unconstruct(func) {
71
86
  return construct(func, args);
72
87
  };
73
88
  }
74
- /* Add properties to a lookup table */
89
+ /**
90
+ * Add properties to a lookup table
91
+ *
92
+ * @param {Object} set - The set to which elements will be added.
93
+ * @param {Array} array - The array containing elements to be added to the set.
94
+ * @param {Function} transformCaseFunc - An optional function to transform the case of each element before adding to the set.
95
+ * @returns {Object} The modified set with added elements.
96
+ */
75
97
 
76
- function addToSet(set, array, transformCaseFunc) {
77
- var _transformCaseFunc;
78
98
 
79
- transformCaseFunc = (_transformCaseFunc = transformCaseFunc) !== null && _transformCaseFunc !== void 0 ? _transformCaseFunc : stringToLowerCase;
99
+ function addToSet(set, array) {
100
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : stringToLowerCase;
80
101
 
81
102
  if (setPrototypeOf) {
82
103
  // Make 'in' and truthy checks like Boolean(set.constructor)
@@ -108,19 +129,32 @@ function addToSet(set, array, transformCaseFunc) {
108
129
 
109
130
  return set;
110
131
  }
111
- /* Shallow clone an object */
132
+ /**
133
+ * Shallow clone an object
134
+ *
135
+ * @param {Object} object - The object to be cloned.
136
+ * @returns {Object} A new object that copies the original.
137
+ */
138
+
112
139
 
113
140
  function clone(object) {
114
141
  const newObject = create(null);
115
142
 
116
143
  for (const [property, value] of entries(object)) {
117
- newObject[property] = value;
144
+ if (getOwnPropertyDescriptor(object, property) !== undefined) {
145
+ newObject[property] = value;
146
+ }
118
147
  }
119
148
 
120
149
  return newObject;
121
150
  }
122
- /* This method automatically checks if the prop is function
123
- * or getter and behaves accordingly. */
151
+ /**
152
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
153
+ *
154
+ * @param {Object} object - The object to look up the getter function in its prototype chain.
155
+ * @param {String} prop - The property name for which to find the getter function.
156
+ * @returns {Function} The getter function found in the prototype chain or a fallback function.
157
+ */
124
158
 
125
159
  function lookupGetter(object, prop) {
126
160
  while (object !== null) {
@@ -195,7 +229,9 @@ var EXPRESSIONS = /*#__PURE__*/Object.freeze({
195
229
  DOCTYPE_NAME: DOCTYPE_NAME
196
230
  });
197
231
 
198
- const getGlobal = () => typeof window === 'undefined' ? null : window;
232
+ const getGlobal = function getGlobal() {
233
+ return typeof window === 'undefined' ? null : window;
234
+ };
199
235
  /**
200
236
  * Creates a no-op policy for internal use only.
201
237
  * Don't export this function outside this module!
@@ -253,7 +289,7 @@ function createDOMPurify() {
253
289
  */
254
290
 
255
291
 
256
- DOMPurify.version = '3.0.5';
292
+ DOMPurify.version = '3.0.6';
257
293
  /**
258
294
  * Array of elements that DOMPurify removed during sanitation.
259
295
  * Empty if nothing was removed.
@@ -268,11 +304,11 @@ function createDOMPurify() {
268
304
  return DOMPurify;
269
305
  }
270
306
 
271
- const originalDocument = window.document;
272
- const currentScript = originalDocument.currentScript;
273
307
  let {
274
308
  document
275
309
  } = window;
310
+ const originalDocument = document;
311
+ const currentScript = originalDocument.currentScript;
276
312
  const {
277
313
  DocumentFragment,
278
314
  HTMLTemplateElement,
@@ -352,7 +388,7 @@ function createDOMPurify() {
352
388
  * @property {boolean} allowCustomizedBuiltInElements allow custom elements derived from built-ins if they pass CUSTOM_ELEMENT_HANDLING.tagNameCheck. Default: `false`.
353
389
  */
354
390
 
355
- let CUSTOM_ELEMENT_HANDLING = Object.seal(Object.create(null, {
391
+ let CUSTOM_ELEMENT_HANDLING = Object.seal(create(null, {
356
392
  tagNameCheck: {
357
393
  writable: true,
358
394
  configurable: false,
@@ -476,10 +512,10 @@ function createDOMPurify() {
476
512
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
477
513
  /* Parsing of strict XHTML documents */
478
514
 
479
- let PARSER_MEDIA_TYPE;
515
+ let PARSER_MEDIA_TYPE = null;
480
516
  const SUPPORTED_PARSER_MEDIA_TYPES = ['application/xhtml+xml', 'text/html'];
481
517
  const DEFAULT_PARSER_MEDIA_TYPE = 'text/html';
482
- let transformCaseFunc;
518
+ let transformCaseFunc = null;
483
519
  /* Keep a reference to config to pass to hooks */
484
520
 
485
521
  let CONFIG = null;
@@ -500,7 +536,9 @@ function createDOMPurify() {
500
536
  // eslint-disable-next-line complexity
501
537
 
502
538
 
503
- const _parseConfig = function _parseConfig(cfg) {
539
+ const _parseConfig = function _parseConfig() {
540
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
541
+
504
542
  if (CONFIG && CONFIG === cfg) {
505
543
  return;
506
544
  }
@@ -719,8 +757,6 @@ function createDOMPurify() {
719
757
  const ALL_MATHML_TAGS = addToSet({}, mathMl$1);
720
758
  addToSet(ALL_MATHML_TAGS, mathMlDisallowed);
721
759
  /**
722
- *
723
- *
724
760
  * @param {Element} element a DOM element whose namespace is being checked
725
761
  * @returns {boolean} Return false if the element has a
726
762
  * namespace that a spec-compliant parser would never
@@ -876,8 +912,8 @@ function createDOMPurify() {
876
912
 
877
913
  const _initDocument = function _initDocument(dirty) {
878
914
  /* Create a HTML document */
879
- let doc;
880
- let leadingWhitespace;
915
+ let doc = null;
916
+ let leadingWhitespace = null;
881
917
 
882
918
  if (FORCE_BODY) {
883
919
  dirty = '<remove></remove>' + dirty;
@@ -930,16 +966,16 @@ function createDOMPurify() {
930
966
  return WHOLE_DOCUMENT ? doc.documentElement : body;
931
967
  };
932
968
  /**
933
- * _createIterator
969
+ * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document.
934
970
  *
935
- * @param {Document} root document/fragment to create iterator for
936
- * @return {Iterator} iterator instance
971
+ * @param {Node} root The root element or node to start traversing on.
972
+ * @return {NodeIterator} The created NodeIterator
937
973
  */
938
974
 
939
975
 
940
- const _createIterator = function _createIterator(root) {
976
+ const _createNodeIterator = function _createNodeIterator(root) {
941
977
  return createNodeIterator.call(root.ownerDocument || root, root, // eslint-disable-next-line no-bitwise
942
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false);
978
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null);
943
979
  };
944
980
  /**
945
981
  * _isClobbered
@@ -953,15 +989,15 @@ function createDOMPurify() {
953
989
  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');
954
990
  };
955
991
  /**
956
- * _isNode
992
+ * Checks whether the given object is a DOM node.
957
993
  *
958
- * @param {Node} obj object to check whether it's a DOM node
994
+ * @param {Node} object object to check whether it's a DOM node
959
995
  * @return {Boolean} true is object is a DOM node
960
996
  */
961
997
 
962
998
 
963
999
  const _isNode = function _isNode(object) {
964
- return typeof Node === 'object' ? object instanceof Node : object && typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
1000
+ return typeof Node === 'function' && object instanceof Node;
965
1001
  };
966
1002
  /**
967
1003
  * _executeHook
@@ -995,7 +1031,7 @@ function createDOMPurify() {
995
1031
 
996
1032
 
997
1033
  const _sanitizeElements = function _sanitizeElements(currentNode) {
998
- let content;
1034
+ let content = null;
999
1035
  /* Execute a hook if present */
1000
1036
 
1001
1037
  _executeHook('beforeSanitizeElements', currentNode, null);
@@ -1020,7 +1056,7 @@ function createDOMPurify() {
1020
1056
  /* Detect mXSS attempts abusing namespace confusion */
1021
1057
 
1022
1058
 
1023
- if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1059
+ if (currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
1024
1060
  _forceRemove(currentNode);
1025
1061
 
1026
1062
  return true;
@@ -1030,9 +1066,14 @@ function createDOMPurify() {
1030
1066
 
1031
1067
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
1032
1068
  /* Check if we have a custom element to handle */
1033
- if (!FORBID_TAGS[tagName] && _basicCustomElementTest(tagName)) {
1034
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
1035
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) return false;
1069
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1070
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
1071
+ return false;
1072
+ }
1073
+
1074
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
1075
+ return false;
1076
+ }
1036
1077
  }
1037
1078
  /* Keep content except for bad-listed elements */
1038
1079
 
@@ -1076,9 +1117,9 @@ function createDOMPurify() {
1076
1117
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
1077
1118
  /* Get the element's text content */
1078
1119
  content = currentNode.textContent;
1079
- content = stringReplace(content, MUSTACHE_EXPR, ' ');
1080
- content = stringReplace(content, ERB_EXPR, ' ');
1081
- content = stringReplace(content, TMPLIT_EXPR, ' ');
1120
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1121
+ content = stringReplace(content, expr, ' ');
1122
+ });
1082
1123
 
1083
1124
  if (currentNode.textContent !== content) {
1084
1125
  arrayPush(DOMPurify.removed, {
@@ -1120,7 +1161,7 @@ function createDOMPurify() {
1120
1161
  if ( // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1121
1162
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1122
1163
  // and c) if the attribute name passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.attributeNameCheck
1123
- _basicCustomElementTest(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1164
+ _isBasicCustomElement(lcTag) && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, lcTag) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(lcTag)) && (CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.attributeNameCheck, lcName) || CUSTOM_ELEMENT_HANDLING.attributeNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.attributeNameCheck(lcName)) || // Alternative, second condition checks if it's an `is`-attribute, AND
1124
1165
  // the value passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
1125
1166
  lcName === 'is' && CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements && (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, value) || CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(value))) ; else {
1126
1167
  return false;
@@ -1134,14 +1175,16 @@ function createDOMPurify() {
1134
1175
  return true;
1135
1176
  };
1136
1177
  /**
1137
- * _basicCustomElementCheck
1178
+ * _isBasicCustomElement
1138
1179
  * checks if at least one dash is included in tagName, and it's not the first char
1139
1180
  * for more sophisticated checking see https://github.com/sindresorhus/validate-element-name
1181
+ *
1140
1182
  * @param {string} tagName name of the tag of the node to sanitize
1183
+ * @returns {boolean} Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
1141
1184
  */
1142
1185
 
1143
1186
 
1144
- const _basicCustomElementTest = function _basicCustomElementTest(tagName) {
1187
+ const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1145
1188
  return tagName.indexOf('-') > 0;
1146
1189
  };
1147
1190
  /**
@@ -1157,12 +1200,7 @@ function createDOMPurify() {
1157
1200
 
1158
1201
 
1159
1202
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
1160
- let attr;
1161
- let value;
1162
- let lcName;
1163
- let l;
1164
1203
  /* Execute a hook if present */
1165
-
1166
1204
  _executeHook('beforeSanitizeAttributes', currentNode, null);
1167
1205
 
1168
1206
  const {
@@ -1180,17 +1218,18 @@ function createDOMPurify() {
1180
1218
  keepAttr: true,
1181
1219
  allowedAttributes: ALLOWED_ATTR
1182
1220
  };
1183
- l = attributes.length;
1221
+ let l = attributes.length;
1184
1222
  /* Go backwards over all attributes; safely remove bad ones */
1185
1223
 
1186
1224
  while (l--) {
1187
- attr = attributes[l];
1225
+ const attr = attributes[l];
1188
1226
  const {
1189
1227
  name,
1190
- namespaceURI
1228
+ namespaceURI,
1229
+ value: attrValue
1191
1230
  } = attr;
1192
- value = name === 'value' ? attr.value : stringTrim(attr.value);
1193
- lcName = transformCaseFunc(name);
1231
+ const lcName = transformCaseFunc(name);
1232
+ let value = name === 'value' ? attrValue : stringTrim(attrValue);
1194
1233
  /* Execute a hook if present */
1195
1234
 
1196
1235
  hookEvent.attrName = lcName;
@@ -1228,9 +1267,9 @@ function createDOMPurify() {
1228
1267
 
1229
1268
 
1230
1269
  if (SAFE_FOR_TEMPLATES) {
1231
- value = stringReplace(value, MUSTACHE_EXPR, ' ');
1232
- value = stringReplace(value, ERB_EXPR, ' ');
1233
- value = stringReplace(value, TMPLIT_EXPR, ' ');
1270
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1271
+ value = stringReplace(value, expr, ' ');
1272
+ });
1234
1273
  }
1235
1274
  /* Is `value` valid for this attribute? */
1236
1275
 
@@ -1299,9 +1338,9 @@ function createDOMPurify() {
1299
1338
 
1300
1339
 
1301
1340
  const _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
1302
- let shadowNode;
1341
+ let shadowNode = null;
1303
1342
 
1304
- const shadowIterator = _createIterator(fragment);
1343
+ const shadowIterator = _createNodeIterator(fragment);
1305
1344
  /* Execute a hook if present */
1306
1345
 
1307
1346
 
@@ -1337,17 +1376,17 @@ function createDOMPurify() {
1337
1376
  * Public method providing core sanitation functionality
1338
1377
  *
1339
1378
  * @param {String|Node} dirty string or DOM node
1340
- * @param {Object} configuration object
1379
+ * @param {Object} cfg object
1341
1380
  */
1342
1381
  // eslint-disable-next-line complexity
1343
1382
 
1344
1383
 
1345
1384
  DOMPurify.sanitize = function (dirty) {
1346
1385
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1347
- let body;
1348
- let importedNode;
1349
- let currentNode;
1350
- let returnNode;
1386
+ let body = null;
1387
+ let importedNode = null;
1388
+ let currentNode = null;
1389
+ let returnNode = null;
1351
1390
  /* Make sure we have a string to sanitize.
1352
1391
  DO NOT return early, as this will return the wrong type if
1353
1392
  the user has requested a DOM object rather than a string */
@@ -1442,7 +1481,7 @@ function createDOMPurify() {
1442
1481
  /* Get node iterator */
1443
1482
 
1444
1483
 
1445
- const nodeIterator = _createIterator(IN_PLACE ? dirty : body);
1484
+ const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
1446
1485
  /* Now start iterating over the created document */
1447
1486
 
1448
1487
 
@@ -1507,9 +1546,9 @@ function createDOMPurify() {
1507
1546
 
1508
1547
 
1509
1548
  if (SAFE_FOR_TEMPLATES) {
1510
- serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR, ' ');
1511
- serializedHTML = stringReplace(serializedHTML, ERB_EXPR, ' ');
1512
- serializedHTML = stringReplace(serializedHTML, TMPLIT_EXPR, ' ');
1549
+ arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
1550
+ serializedHTML = stringReplace(serializedHTML, expr, ' ');
1551
+ });
1513
1552
  }
1514
1553
 
1515
1554
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
@@ -1522,7 +1561,9 @@ function createDOMPurify() {
1522
1561
  */
1523
1562
 
1524
1563
 
1525
- DOMPurify.setConfig = function (cfg) {
1564
+ DOMPurify.setConfig = function () {
1565
+ let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1566
+
1526
1567
  _parseConfig(cfg);
1527
1568
 
1528
1569
  SET_CONFIG = true;
@@ -1543,9 +1584,9 @@ function createDOMPurify() {
1543
1584
  * Uses last set config, if any. Otherwise, uses config defaults.
1544
1585
  * isValidAttribute
1545
1586
  *
1546
- * @param {string} tag Tag name of containing element.
1547
- * @param {string} attr Attribute name.
1548
- * @param {string} value Attribute value.
1587
+ * @param {String} tag Tag name of containing element.
1588
+ * @param {String} attr Attribute name.
1589
+ * @param {String} value Attribute value.
1549
1590
  * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
1550
1591
  */
1551
1592
 
@@ -1608,7 +1649,6 @@ function createDOMPurify() {
1608
1649
  /**
1609
1650
  * RemoveAllHooks
1610
1651
  * Public method to remove all DOMPurify hooks
1611
- *
1612
1652
  */
1613
1653
 
1614
1654