title: Security concern over method of removing tags from html
body: <p>I am using <a href="https://github.com/padolsey/findAndReplaceDOMText" rel="nofollow noreferrer">findAndReplaceDOMText</a>, a library that lets you wrap text that spans multiple tags.</p>
  
  <p>Consider wrapping <code>o b</code> in <code>&lt;em&gt;</code> tags in the following html:</p>
  
  <pre><code>&lt;p&gt;foo &lt;span&gt;bar&lt;/span&gt;&lt;/p&gt;
  </code></pre>
  
  <p>It generates the following:</p>
  
  <pre><code>&lt;p&gt;fo&lt;em&gt;o &lt;/em&gt;&lt;span&gt;&lt;em&gt;b&lt;/em&gt;ar&lt;/span&gt;&lt;/p&gt;
  </code></pre>
  
  <p>This works great. My concern is that my strategy for <em>removing</em> these tags may open up the potential for code injection. The below code <em>does</em> work, I am just worried of potential code injection opportunities, particularly because I'm working on a chrome extension, so the target page's HTML may be malformed.</p>
  
  <pre><code>import $ from 'jquery'
  
  export default function clearMarks() {
    $(".deepSearch-highlight").parent().each(function() {
      const contents = []
      const $parent = $(this)
      $parent.contents().each(function() {
        const $node = $(this)
        let html
  
        if ($node.hasClass("deepSearch-highlight")) {
          html = $node.html()
        }
        else if (this.nodeName === "#text") {
          html = this.data
        }
        else {
          html = this.outerHTML
        }
        contents.push(html)
      })
      $parent.html(contents.join(""))
    })
  }
  </code></pre>
  
  <p>My goal is to restore the html to <em>exactly</em> what it was before it was transformed with findAndReplaceDOMText. In the "additional information" section you can see how a simpler <code>clearMarks</code> function will result in a change to the number of text nodes.</p>
  
  <p>Does my strategy have any security vulnerabilities I'm missing? Is there a more secure/more elegant/generally better way of achieving my goal?</p>
  
  <hr>
  
  <h2>Additional Information:</h2>
  
  <ul>
  <li><p>I am using the <a href="https://github.com/padolsey/findAndReplaceDOMText#presetprose" rel="nofollow noreferrer">findAndReplaceDOMText</a> option <code>preset: "prose"</code> which:</p>
  
  <blockquote>
    <p>Ignore non-textual elements (E.g. <code>&lt;script&gt;</code>, <code>&lt;svg&gt;</code>, <code>&lt;optgroup&gt;,</code>`, etc.)</p>
  </blockquote></li>
  <li><p>As an aside, the much simpler <code>$(this).replaceWith($(this).html())</code> results in an explosion in the number of text nodes. With the above example we would result in: <code>&lt;p&gt;"fo""o "&lt;span&gt;"b""ar"&lt;/span&gt;&lt;/p&gt;</code> (where text nodes are denoted with <code>"</code>). That's causes problems if you try reapplying <code>findAndReplaceDOMText</code> in addition to being generally smelly.</p></li>
  <li><p>The inserted <code>span</code> elements have a class of <code>.deepSearch-highlight</code> (in contrast to the above example which wraps text in <code>em</code>'s. See the full code below.</p></li>
  </ul>
  
  <p>.</p>
  
  <pre><code>import $ from "jquery"
  import findAndReplaceDomText from "findandreplacedomtext"
  
  import buildRegex from "../../shared/buildRegex"
  import scrollToElement from "./scrollToElement"
  
  
  export default function search(queryParams) {
    const regex = buildRegex(queryParams)
    findAndReplaceDomText($('body')[0], {
      find: regex,
      replace: createHighlight,
      preset: "prose",
      filterElements,
    })
    scrollToElement($(".deepSearch-current-highlight"))
  }
  
  function createHighlight(portion, match) {
    var wrapped = document.createElement("span")
    var wrappedClasses = "deepSearch-highlight"
    if (match.index === 0) {
      wrappedClasses += " deepSearch-current-highlight"
    }
    wrapped.setAttribute("class", wrappedClasses)
    wrapped.setAttribute("data-highlight-index", match.index)
    wrapped.appendChild(document.createTextNode(portion.text))
    return wrapped
  }
  
  function filterElements(elem) {
    const $elem = $(elem)
    return $elem.is(":visible") &amp;&amp; !$elem.attr("aria-hidden")
  }
  </code></pre>