dompurify 3.4.9 → 3.4.10

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.
@@ -1,4 +1,4 @@
1
- /*! @license DOMPurify 3.4.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.9/LICENSE */
1
+ /*! @license DOMPurify 3.4.10 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.10/LICENSE */
2
2
 
3
3
  function _arrayLikeToArray(r, a) {
4
4
  (null == a || a > r.length) && (a = r.length);
@@ -327,6 +327,13 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
327
327
  );
328
328
  const DOCTYPE_NAME = seal(/^html$/i);
329
329
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
330
+ // Markup-significant character probes used by _sanitizeElements.
331
+ // Shared module-level instances are safe despite the sticky /g flags:
332
+ // unapply() resets lastIndex for RegExp receivers before every call.
333
+ const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
334
+ const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
335
+ const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
336
+ const SELF_CLOSING_TAG = seal(/\/>/i);
330
337
 
331
338
  /* eslint-disable @typescript-eslint/indent */
332
339
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
@@ -339,7 +346,7 @@ const NODE_TYPE = {
339
346
  // Deprecated
340
347
  entityNode: 6,
341
348
  // Deprecated
342
- progressingInstruction: 7,
349
+ processingInstruction: 7,
343
350
  comment: 8,
344
351
  document: 9,
345
352
  documentType: 10,
@@ -400,10 +407,25 @@ const _createHooksMap = function _createHooksMap() {
400
407
  uponSanitizeShadowNode: []
401
408
  };
402
409
  };
410
+ /**
411
+ * Resolve a set-valued configuration option: a fresh set built from
412
+ * cfg[key] when it is an own array property (seeded with a clone of
413
+ * options.base when given, case-normalized via options.transform),
414
+ * the fallback set otherwise.
415
+ *
416
+ * @param cfg the cloned, prototype-free configuration object
417
+ * @param key the configuration property to read
418
+ * @param fallback the set to use when the option is absent or not an array
419
+ * @param options transform and optional base set to merge into
420
+ * @returns the resolved set
421
+ */
422
+ const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
423
+ return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
424
+ };
403
425
  function createDOMPurify() {
404
426
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
405
427
  const DOMPurify = root => createDOMPurify(root);
406
- DOMPurify.version = '3.4.9';
428
+ DOMPurify.version = '3.4.10';
407
429
  DOMPurify.removed = [];
408
430
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
409
431
  // Not running in a browser, provide a factory function
@@ -660,8 +682,10 @@ function createDOMPurify() {
660
682
  /* Allowed XHTML+XML namespaces */
661
683
  let ALLOWED_NAMESPACES = null;
662
684
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
663
- let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
664
- let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
685
+ const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
686
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
687
+ const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
688
+ let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
665
689
  // Certain elements are allowed in both SVG and HTML
666
690
  // namespace. We need to specify them explicitly
667
691
  // so that they don't get erroneously deleted from
@@ -703,14 +727,32 @@ function createDOMPurify() {
703
727
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
704
728
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
705
729
  /* Set configuration parameters */
706
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
707
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
708
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
709
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
710
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
711
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
712
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
713
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
730
+ ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
731
+ transform: transformCaseFunc
732
+ });
733
+ ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
734
+ transform: transformCaseFunc
735
+ });
736
+ ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
737
+ transform: stringToString
738
+ });
739
+ URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
740
+ transform: transformCaseFunc,
741
+ base: DEFAULT_URI_SAFE_ATTRIBUTES
742
+ });
743
+ DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
744
+ transform: transformCaseFunc,
745
+ base: DEFAULT_DATA_URI_TAGS
746
+ });
747
+ FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
748
+ transform: transformCaseFunc
749
+ });
750
+ FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
751
+ transform: transformCaseFunc
752
+ });
753
+ FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
754
+ transform: transformCaseFunc
755
+ });
714
756
  USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
715
757
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
716
758
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
@@ -729,8 +771,8 @@ function createDOMPurify() {
729
771
  IN_PLACE = cfg.IN_PLACE || false; // Default false
730
772
  IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
731
773
  NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
732
- MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
733
- HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
774
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); // Default built-in map
775
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); // Default built-in map
734
776
  const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
735
777
  CUSTOM_ELEMENT_HANDLING = create(null);
736
778
  if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
@@ -742,6 +784,7 @@ function createDOMPurify() {
742
784
  if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
743
785
  CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
744
786
  }
787
+ seal(CUSTOM_ELEMENT_HANDLING);
745
788
  if (SAFE_FOR_TEMPLATES) {
746
789
  ALLOW_DATA_ATTR = false;
747
790
  }
@@ -907,6 +950,77 @@ function createDOMPurify() {
907
950
  * correctly. */
908
951
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
909
952
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
953
+ /**
954
+ * Namespace rules for an element in the SVG namespace.
955
+ *
956
+ * @param tagName the element's lowercase tag name
957
+ * @param parent the (possibly simulated) parent node
958
+ * @param parentTagName the parent's lowercase tag name
959
+ * @returns true if a spec-compliant parser could produce this element
960
+ */
961
+ const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
962
+ // The only way to switch from HTML namespace to SVG
963
+ // is via <svg>. If it happens via any other tag, then
964
+ // it should be killed.
965
+ if (parent.namespaceURI === HTML_NAMESPACE) {
966
+ return tagName === 'svg';
967
+ }
968
+ // The only way to switch from MathML to SVG is via <svg>
969
+ // if the parent is either <annotation-xml> or a MathML
970
+ // text integration point.
971
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
972
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
973
+ }
974
+ // We only allow elements that are defined in SVG
975
+ // spec. All others are disallowed in SVG namespace.
976
+ return Boolean(ALL_SVG_TAGS[tagName]);
977
+ };
978
+ /**
979
+ * Namespace rules for an element in the MathML namespace.
980
+ *
981
+ * @param tagName the element's lowercase tag name
982
+ * @param parent the (possibly simulated) parent node
983
+ * @param parentTagName the parent's lowercase tag name
984
+ * @returns true if a spec-compliant parser could produce this element
985
+ */
986
+ const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
987
+ // The only way to switch from HTML namespace to MathML
988
+ // is via <math>. If it happens via any other tag, then
989
+ // it should be killed.
990
+ if (parent.namespaceURI === HTML_NAMESPACE) {
991
+ return tagName === 'math';
992
+ }
993
+ // The only way to switch from SVG to MathML is via
994
+ // <math> and HTML integration points
995
+ if (parent.namespaceURI === SVG_NAMESPACE) {
996
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
997
+ }
998
+ // We only allow elements that are defined in MathML
999
+ // spec. All others are disallowed in MathML namespace.
1000
+ return Boolean(ALL_MATHML_TAGS[tagName]);
1001
+ };
1002
+ /**
1003
+ * Namespace rules for an element in the HTML namespace.
1004
+ *
1005
+ * @param tagName the element's lowercase tag name
1006
+ * @param parent the (possibly simulated) parent node
1007
+ * @param parentTagName the parent's lowercase tag name
1008
+ * @returns true if a spec-compliant parser could produce this element
1009
+ */
1010
+ const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
1011
+ // The only way to switch from SVG to HTML is via
1012
+ // HTML integration points, and from MathML to HTML
1013
+ // is via MathML text integration points
1014
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
1015
+ return false;
1016
+ }
1017
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
1018
+ return false;
1019
+ }
1020
+ // We disallow tags that are specific for MathML
1021
+ // or SVG and should never appear in HTML namespace
1022
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1023
+ };
910
1024
  /**
911
1025
  * @param element a DOM element whose namespace is being checked
912
1026
  * @returns Return false if the element has a
@@ -929,51 +1043,13 @@ function createDOMPurify() {
929
1043
  return false;
930
1044
  }
931
1045
  if (element.namespaceURI === SVG_NAMESPACE) {
932
- // The only way to switch from HTML namespace to SVG
933
- // is via <svg>. If it happens via any other tag, then
934
- // it should be killed.
935
- if (parent.namespaceURI === HTML_NAMESPACE) {
936
- return tagName === 'svg';
937
- }
938
- // The only way to switch from MathML to SVG is via`
939
- // svg if parent is either <annotation-xml> or MathML
940
- // text integration points.
941
- if (parent.namespaceURI === MATHML_NAMESPACE) {
942
- return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
943
- }
944
- // We only allow elements that are defined in SVG
945
- // spec. All others are disallowed in SVG namespace.
946
- return Boolean(ALL_SVG_TAGS[tagName]);
1046
+ return _checkSvgNamespace(tagName, parent, parentTagName);
947
1047
  }
948
1048
  if (element.namespaceURI === MATHML_NAMESPACE) {
949
- // The only way to switch from HTML namespace to MathML
950
- // is via <math>. If it happens via any other tag, then
951
- // it should be killed.
952
- if (parent.namespaceURI === HTML_NAMESPACE) {
953
- return tagName === 'math';
954
- }
955
- // The only way to switch from SVG to MathML is via
956
- // <math> and HTML integration points
957
- if (parent.namespaceURI === SVG_NAMESPACE) {
958
- return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
959
- }
960
- // We only allow elements that are defined in MathML
961
- // spec. All others are disallowed in MathML namespace.
962
- return Boolean(ALL_MATHML_TAGS[tagName]);
1049
+ return _checkMathMlNamespace(tagName, parent, parentTagName);
963
1050
  }
964
1051
  if (element.namespaceURI === HTML_NAMESPACE) {
965
- // The only way to switch from SVG to HTML is via
966
- // HTML integration points, and from MathML to HTML
967
- // is via MathML text integration points
968
- if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
969
- return false;
970
- }
971
- if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
972
- return false;
973
- }
974
- // We disallow tags that are specific for MathML
975
- // or SVG and should never appear in HTML namespace
976
- return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
1052
+ return _checkHtmlNamespace(tagName, parent, parentTagName);
977
1053
  }
978
1054
  // For XHTML and XML documents that support custom namespaces
979
1055
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
@@ -1039,7 +1115,7 @@ function createDOMPurify() {
1039
1115
  * @param root the in-place root to empty
1040
1116
  */
1041
1117
  const _neutralizeRoot = function _neutralizeRoot(root) {
1042
- const childNodes = getChildNodes ? getChildNodes(root) : root.childNodes;
1118
+ const childNodes = getChildNodes(root);
1043
1119
  if (childNodes) {
1044
1120
  const snapshot = [];
1045
1121
  arrayForEach(childNodes, child => {
@@ -1053,7 +1129,7 @@ function createDOMPurify() {
1053
1129
  }
1054
1130
  });
1055
1131
  }
1056
- const attributes = getAttributes ? getAttributes(root) : null;
1132
+ const attributes = getAttributes(root);
1057
1133
  if (attributes) {
1058
1134
  for (let i = attributes.length - 1; i >= 0; --i) {
1059
1135
  const attribute = attributes[i];
@@ -1111,7 +1187,7 @@ function createDOMPurify() {
1111
1187
  * @param element the element to strip
1112
1188
  */
1113
1189
  const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
1114
- const attributes = getAttributes ? getAttributes(element) : element.attributes;
1190
+ const attributes = getAttributes(element);
1115
1191
  if (!attributes) {
1116
1192
  return;
1117
1193
  }
@@ -1158,7 +1234,7 @@ function createDOMPurify() {
1158
1234
  if (nodeType === NODE_TYPE.element) {
1159
1235
  _stripDisallowedAttributes(node);
1160
1236
  }
1161
- const childNodes = getChildNodes ? getChildNodes(node) : node.childNodes;
1237
+ const childNodes = getChildNodes(node);
1162
1238
  if (childNodes) {
1163
1239
  for (let i = childNodes.length - 1; i >= 0; --i) {
1164
1240
  stack.push(childNodes[i]);
@@ -1227,6 +1303,20 @@ function createDOMPurify() {
1227
1303
  // eslint-disable-next-line no-bitwise
1228
1304
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
1229
1305
  };
1306
+ /**
1307
+ * Replace template expression syntax (mustache, ERB, template
1308
+ * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
1309
+ * sites. Order matters: mustache, then ERB, then template literal.
1310
+ *
1311
+ * @param value the string to scrub
1312
+ * @returns the scrubbed string
1313
+ */
1314
+ const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
1315
+ value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
1316
+ value = stringReplace(value, ERB_EXPR$1, ' ');
1317
+ value = stringReplace(value, TMPLIT_EXPR$1, ' ');
1318
+ return value;
1319
+ };
1230
1320
  /**
1231
1321
  * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
1232
1322
  * character data of an element subtree. Used as the final safety net for
@@ -1247,29 +1337,27 @@ function createDOMPurify() {
1247
1337
  * @param node The root element whose character data should be scrubbed.
1248
1338
  */
1249
1339
  const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
1250
- var _node$querySelectorAl, _node$querySelectorAl2;
1340
+ var _node$querySelectorAl;
1251
1341
  node.normalize();
1252
1342
  const walker = createNodeIterator.call(node.ownerDocument || node, node,
1253
1343
  // eslint-disable-next-line no-bitwise
1254
1344
  NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
1255
1345
  let currentNode = walker.nextNode();
1256
1346
  while (currentNode) {
1257
- let data = currentNode.data;
1258
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1259
- data = stringReplace(data, expr, ' ');
1260
- });
1261
- currentNode.data = data;
1347
+ currentNode.data = _stripTemplateExpressions(currentNode.data);
1262
1348
  currentNode = walker.nextNode();
1263
1349
  }
1264
1350
  // NodeIterator does not descend into <template>.content per the DOM spec,
1265
1351
  // so we must explicitly recurse into each template's content fragment,
1266
1352
  // mirroring the approach used by _sanitizeShadowDOM.
1267
- const templates = (_node$querySelectorAl = (_node$querySelectorAl2 = node.querySelectorAll) === null || _node$querySelectorAl2 === void 0 ? void 0 : _node$querySelectorAl2.call(node, 'template')) !== null && _node$querySelectorAl !== void 0 ? _node$querySelectorAl : [];
1268
- arrayForEach(Array.from(templates), tmpl => {
1269
- if (_isDocumentFragment(tmpl.content)) {
1270
- _scrubTemplateExpressions2(tmpl.content);
1271
- }
1272
- });
1353
+ const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, 'template');
1354
+ if (templates) {
1355
+ arrayForEach(templates, tmpl => {
1356
+ if (_isDocumentFragment(tmpl.content)) {
1357
+ _scrubTemplateExpressions2(tmpl.content);
1358
+ }
1359
+ });
1360
+ }
1273
1361
  };
1274
1362
  /**
1275
1363
  * _isClobbered
@@ -1365,67 +1453,64 @@ function createDOMPurify() {
1365
1453
  }
1366
1454
  };
1367
1455
  function _executeHooks(hooks, currentNode, data) {
1456
+ if (hooks.length === 0) {
1457
+ return;
1458
+ }
1368
1459
  arrayForEach(hooks, hook => {
1369
1460
  hook.call(DOMPurify, currentNode, data, CONFIG);
1370
1461
  });
1371
1462
  }
1372
1463
  /**
1373
- * _sanitizeElements
1464
+ * Structural-threat checks that condemn a node regardless of the
1465
+ * allowlists: mXSS via namespace confusion, risky CSS construction,
1466
+ * processing instructions, markup-bearing comments. Pure predicate;
1467
+ * the caller removes. Check order is load-bearing.
1374
1468
  *
1375
- * @protect nodeName
1376
- * @protect textContent
1377
- * @protect removeChild
1378
- * @param currentNode to check for permission to exist
1379
- * @return true if node was killed, false if left alive
1469
+ * @param currentNode the node to inspect
1470
+ * @param tagName the node's transformCaseFunc'd tag name
1471
+ * @return true if the node must be removed
1380
1472
  */
1381
- const _sanitizeElements = function _sanitizeElements(currentNode) {
1382
- let content = null;
1383
- /* Execute a hook if present */
1384
- _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1385
- /* Check if element is clobbered or can clobber */
1386
- if (_isClobbered(currentNode)) {
1387
- _forceRemove(currentNode);
1388
- return true;
1389
- }
1390
- /* Now let's check the element's type and name */
1391
- const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
1392
- /* Execute a hook if present */
1393
- _executeHooks(hooks.uponSanitizeElement, currentNode, {
1394
- tagName,
1395
- allowedTags: ALLOWED_TAGS
1396
- });
1473
+ const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
1397
1474
  /* Detect mXSS attempts abusing namespace confusion */
1398
- if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
1399
- _forceRemove(currentNode);
1475
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
1400
1476
  return true;
1401
1477
  }
1402
1478
  /* Remove risky CSS construction leading to mXSS */
1403
1479
  if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
1404
- _forceRemove(currentNode);
1405
1480
  return true;
1406
1481
  }
1407
1482
  /* Remove any occurrence of processing instructions */
1408
- if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
1409
- _forceRemove(currentNode);
1483
+ if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
1410
1484
  return true;
1411
1485
  }
1412
1486
  /* Remove any kind of possibly harmful comments */
1413
- if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
1414
- _forceRemove(currentNode);
1487
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
1415
1488
  return true;
1416
1489
  }
1417
- /* Remove element if anything forbids its presence */
1418
- if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
1419
- /* Check if we have a custom element to handle */
1420
- if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1421
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
1422
- return false;
1423
- }
1424
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
1425
- return false;
1426
- }
1490
+ return false;
1491
+ };
1492
+ /**
1493
+ * Handle a node whose tag is forbidden or not allowlisted: keep
1494
+ * allowed custom elements (false return exits _sanitizeElements
1495
+ * early - namespace/fallback checks and the afterSanitizeElements
1496
+ * hook are intentionally skipped for kept custom elements), else
1497
+ * hoist content per KEEP_CONTENT and remove.
1498
+ *
1499
+ * @param currentNode the disallowed node
1500
+ * @param tagName the node's transformCaseFunc'd tag name
1501
+ * @return true if the node was removed, false if kept
1502
+ */
1503
+ const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
1504
+ /* Check if we have a custom element to handle */
1505
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
1506
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
1507
+ return false;
1508
+ }
1509
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
1510
+ return false;
1427
1511
  }
1428
- /* Keep content except for bad-listed elements.
1512
+ }
1513
+ /* Keep content except for bad-listed elements.
1429
1514
  Use the cached prototype getters exclusively — the previous code
1430
1515
  had `|| currentNode.parentNode` / `|| currentNode.childNodes`
1431
1516
  fallbacks, but the cached getters always return the canonical
@@ -1433,12 +1518,12 @@ function createDOMPurify() {
1433
1518
  path was dead in safe cases and a clobbering surface in unsafe
1434
1519
  ones. Falsy cached results stay falsy; the `if (childNodes &&
1435
1520
  parentNode)` check already gates correctly. */
1436
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1437
- const parentNode = getParentNode(currentNode);
1438
- const childNodes = getChildNodes(currentNode);
1439
- if (childNodes && parentNode) {
1440
- const childCount = childNodes.length;
1441
- /* In-place: hoist the *original* children so the iterator visits
1521
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
1522
+ const parentNode = getParentNode(currentNode);
1523
+ const childNodes = getChildNodes(currentNode);
1524
+ if (childNodes && parentNode) {
1525
+ const childCount = childNodes.length;
1526
+ /* In-place: hoist the *original* children so the iterator visits
1442
1527
  and sanitises them through the same allowlist pass as every other
1443
1528
  node. The caller built the tree in the live document, so the
1444
1529
  originals carry already-queued resource events (`<img onerror>`,
@@ -1448,24 +1533,57 @@ function createDOMPurify() {
1448
1533
  root is pre-validated as an allowed tag and so is never the node
1449
1534
  being removed, which keeps `parentNode` inside the iterator root
1450
1535
  and the relocated child inside the serialised tree.
1451
- Otherwise (string / DOM-copy paths): clone. The iterator is rooted
1536
+ Otherwise (string / DOM-copy paths): clone. The iterator is rooted
1452
1537
  at — and the result serialised from — `body`, so a restrictive
1453
1538
  ALLOWED_TAGS that removes `body` itself must leave its content in
1454
1539
  place, which only cloning does; and those paths parse into an
1455
1540
  inert document, so their discarded originals never had a queued
1456
1541
  event to neutralise.
1457
- `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
1542
+ `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
1458
1543
  valid whether we move (drops the trailing entry) or clone (leaves
1459
1544
  the list intact). */
1460
- for (let i = childCount - 1; i >= 0; --i) {
1461
- const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
1462
- parentNode.insertBefore(hoisted, getNextSibling(currentNode));
1463
- }
1545
+ for (let i = childCount - 1; i >= 0; --i) {
1546
+ const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
1547
+ parentNode.insertBefore(hoisted, getNextSibling(currentNode));
1464
1548
  }
1465
1549
  }
1550
+ }
1551
+ _forceRemove(currentNode);
1552
+ return true;
1553
+ };
1554
+ /**
1555
+ * _sanitizeElements
1556
+ *
1557
+ * @protect nodeName
1558
+ * @protect textContent
1559
+ * @protect removeChild
1560
+ * @param currentNode to check for permission to exist
1561
+ * @return true if node was killed, false if left alive
1562
+ */
1563
+ const _sanitizeElements = function _sanitizeElements(currentNode) {
1564
+ /* Execute a hook if present */
1565
+ _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
1566
+ /* Check if element is clobbered or can clobber */
1567
+ if (_isClobbered(currentNode)) {
1466
1568
  _forceRemove(currentNode);
1467
1569
  return true;
1468
1570
  }
1571
+ /* Now let's check the element's type and name */
1572
+ const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
1573
+ /* Execute a hook if present */
1574
+ _executeHooks(hooks.uponSanitizeElement, currentNode, {
1575
+ tagName,
1576
+ allowedTags: ALLOWED_TAGS
1577
+ });
1578
+ /* Remove mXSS vectors, processing instructions and risky comments */
1579
+ if (_isUnsafeNode(currentNode, tagName)) {
1580
+ _forceRemove(currentNode);
1581
+ return true;
1582
+ }
1583
+ /* Remove element if anything forbids its presence */
1584
+ if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
1585
+ return _sanitizeDisallowedNode(currentNode, tagName);
1586
+ }
1469
1587
  /* Check whether element has a valid namespace.
1470
1588
  Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
1471
1589
  nodeType getter rather than `instanceof Element`, which is realm-
@@ -1478,17 +1596,14 @@ function createDOMPurify() {
1478
1596
  return true;
1479
1597
  }
1480
1598
  /* Make sure that older browsers don't get fallback-tag mXSS */
1481
- if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
1599
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
1482
1600
  _forceRemove(currentNode);
1483
1601
  return true;
1484
1602
  }
1485
1603
  /* Sanitize element content to be template-safe */
1486
1604
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
1487
1605
  /* Get the element's text content */
1488
- content = currentNode.textContent;
1489
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1490
- content = stringReplace(content, expr, ' ');
1491
- });
1606
+ const content = _stripTemplateExpressions(currentNode.textContent);
1492
1607
  if (currentNode.textContent !== content) {
1493
1608
  arrayPush(DOMPurify.removed, {
1494
1609
  element: currentNode.cloneNode()
@@ -1523,7 +1638,7 @@ function createDOMPurify() {
1523
1638
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
1524
1639
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
1525
1640
  We don't need to check the value; it's always URI safe. */
1526
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted || FORBID_ATTR[lcName]) {
1641
+ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted) {
1527
1642
  if (
1528
1643
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
1529
1644
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -1555,6 +1670,63 @@ function createDOMPurify() {
1555
1670
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
1556
1671
  return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
1557
1672
  };
1673
+ /**
1674
+ * Wrap an attribute value in the matching Trusted Types object when
1675
+ * the active policy requires it. Namespaced attributes pass through
1676
+ * unchanged (no TT support yet, see
1677
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
1678
+ *
1679
+ * @param lcTag lowercase tag name of the containing element
1680
+ * @param lcName lowercase attribute name
1681
+ * @param namespaceURI the attribute's namespace, if any
1682
+ * @param value the attribute value to wrap
1683
+ * @return the value, wrapped when Trusted Types demand it
1684
+ */
1685
+ const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
1686
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function' && !namespaceURI) {
1687
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1688
+ case 'TrustedHTML':
1689
+ {
1690
+ return _createTrustedHTML(value);
1691
+ }
1692
+ case 'TrustedScriptURL':
1693
+ {
1694
+ return _createTrustedScriptURL(value);
1695
+ }
1696
+ }
1697
+ }
1698
+ return value;
1699
+ };
1700
+ /**
1701
+ * Write a modified attribute value back onto the element. On
1702
+ * success, re-probe for clobbering introduced by the new value and
1703
+ * remove the element when found; otherwise pop the removal entry
1704
+ * recorded by the earlier _removeAttribute (long-standing pairing
1705
+ * with the SANITIZE_NAMED_PROPS path - do not "fix" casually). On
1706
+ * failure, remove the attribute instead.
1707
+ *
1708
+ * @param currentNode the element carrying the attribute
1709
+ * @param name the attribute name as present on the element
1710
+ * @param namespaceURI the attribute's namespace, if any
1711
+ * @param value the new attribute value
1712
+ */
1713
+ const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
1714
+ try {
1715
+ if (namespaceURI) {
1716
+ currentNode.setAttributeNS(namespaceURI, name, value);
1717
+ } else {
1718
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1719
+ currentNode.setAttribute(name, value);
1720
+ }
1721
+ if (_isClobbered(currentNode)) {
1722
+ _forceRemove(currentNode);
1723
+ } else {
1724
+ arrayPop(DOMPurify.removed);
1725
+ }
1726
+ } catch (_) {
1727
+ _removeAttribute(name, currentNode);
1728
+ }
1729
+ };
1558
1730
  /**
1559
1731
  * _sanitizeAttributes
1560
1732
  *
@@ -1581,6 +1753,7 @@ function createDOMPurify() {
1581
1753
  forceKeepAttr: undefined
1582
1754
  };
1583
1755
  let l = attributes.length;
1756
+ const lcTag = transformCaseFunc(currentNode.nodeName);
1584
1757
  /* Go backwards over all attributes; safely remove bad ones */
1585
1758
  while (l--) {
1586
1759
  const attr = attributes[l];
@@ -1618,7 +1791,7 @@ function createDOMPurify() {
1618
1791
  _removeAttribute(name, currentNode);
1619
1792
  continue;
1620
1793
  }
1621
- /* Did the hooks approve of the attribute? */
1794
+ /* Did the hooks force-keep the attribute? */
1622
1795
  if (hookEvent.forceKeepAttr) {
1623
1796
  continue;
1624
1797
  }
@@ -1628,56 +1801,24 @@ function createDOMPurify() {
1628
1801
  continue;
1629
1802
  }
1630
1803
  /* Work around a security issue in jQuery 3.0 */
1631
- if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
1804
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
1632
1805
  _removeAttribute(name, currentNode);
1633
1806
  continue;
1634
1807
  }
1635
1808
  /* Sanitize attribute content to be template-safe */
1636
1809
  if (SAFE_FOR_TEMPLATES) {
1637
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
1638
- value = stringReplace(value, expr, ' ');
1639
- });
1810
+ value = _stripTemplateExpressions(value);
1640
1811
  }
1641
1812
  /* Is `value` valid for this attribute? */
1642
- const lcTag = transformCaseFunc(currentNode.nodeName);
1643
1813
  if (!_isValidAttribute(lcTag, lcName, value)) {
1644
1814
  _removeAttribute(name, currentNode);
1645
1815
  continue;
1646
1816
  }
1647
1817
  /* Handle attributes that require Trusted Types */
1648
- if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
1649
- if (namespaceURI) ; else {
1650
- switch (trustedTypes.getAttributeType(lcTag, lcName)) {
1651
- case 'TrustedHTML':
1652
- {
1653
- value = _createTrustedHTML(value);
1654
- break;
1655
- }
1656
- case 'TrustedScriptURL':
1657
- {
1658
- value = _createTrustedScriptURL(value);
1659
- break;
1660
- }
1661
- }
1662
- }
1663
- }
1818
+ value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
1664
1819
  /* Handle invalid data-* attribute set by try-catching it */
1665
1820
  if (value !== initValue) {
1666
- try {
1667
- if (namespaceURI) {
1668
- currentNode.setAttributeNS(namespaceURI, name, value);
1669
- } else {
1670
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
1671
- currentNode.setAttribute(name, value);
1672
- }
1673
- if (_isClobbered(currentNode)) {
1674
- _forceRemove(currentNode);
1675
- } else {
1676
- arrayPop(DOMPurify.removed);
1677
- }
1678
- } catch (_) {
1679
- _removeAttribute(name, currentNode);
1680
- }
1821
+ _setAttributeValue(currentNode, name, namespaceURI, value);
1681
1822
  }
1682
1823
  }
1683
1824
  /* Execute a hook if present */
@@ -1719,7 +1860,7 @@ function createDOMPurify() {
1719
1860
  iterator also surfaces. */
1720
1861
  const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
1721
1862
  if (shadowNodeType === NODE_TYPE.element) {
1722
- const innerSr = getShadowRoot ? getShadowRoot(shadowNode) : shadowNode.shadowRoot;
1863
+ const innerSr = getShadowRoot(shadowNode);
1723
1864
  if (_isDocumentFragment(innerSr)) {
1724
1865
  _sanitizeAttachedShadowRoots(innerSr);
1725
1866
  _sanitizeShadowDOM2(innerSr);
@@ -1781,7 +1922,7 @@ function createDOMPurify() {
1781
1922
  /* (pushed last → processed first) Children, snapshotted in reverse so
1782
1923
  the first child is processed first. Snapshotting matters because a
1783
1924
  hook may detach siblings mid-walk. */
1784
- const childNodes = getChildNodes ? getChildNodes(node) : node.childNodes;
1925
+ const childNodes = getChildNodes(node);
1785
1926
  if (childNodes) {
1786
1927
  for (let i = childNodes.length - 1; i >= 0; --i) {
1787
1928
  stack.push({
@@ -1811,7 +1952,7 @@ function createDOMPurify() {
1811
1952
  silently skipped foreign-realm shadow roots (e.g.
1812
1953
  iframe.contentDocument attachShadow). */
1813
1954
  if (isElement) {
1814
- const sr = getShadowRoot ? getShadowRoot(node) : node.shadowRoot;
1955
+ const sr = getShadowRoot(node);
1815
1956
  if (_isDocumentFragment(sr)) {
1816
1957
  /* Push the deferred sanitise first so it pops after the shadow
1817
1958
  walk we push next, i.e. nested shadow roots are discovered
@@ -2023,9 +2164,7 @@ function createDOMPurify() {
2023
2164
  }
2024
2165
  /* Sanitize final string template-safe */
2025
2166
  if (SAFE_FOR_TEMPLATES) {
2026
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], expr => {
2027
- serializedHTML = stringReplace(serializedHTML, expr, ' ');
2028
- });
2167
+ serializedHTML = _stripTemplateExpressions(serializedHTML);
2029
2168
  }
2030
2169
  return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
2031
2170
  };