Search code examples
javascriptreactjshtml-sanitizingdompurify

Cannot replace values inside dangerouslySetInnerHTML


I have the following piece of ReactJS code that displays the value of the variable bodyHtml that contains html as a string.

I would like all links, inside the html string contained in the variable, to open in a new tab.

I am using the DOMPurify library and the following code works only if I remove purify.sanitize.

If I use purify.sanitize instead the replaceAll has no effect.

How can I get around this problem?

This doesn't work:

dangerouslySetInnerHTML={{
    __html: purify.sanitize(
        bodyHtml.replaceAll('href', 'target="_blank" href')
    )
}}

While this works:

dangerouslySetInnerHTML={{
    __html: bodyHtml.replaceAll('href', 'target="_blank" href')
}}

Solution

  • This is an expected result. Replace all has an effect, it's just that it gets removed afterwards anyway. Library by default sanitizes the content of your html. If you want to keep the target blank you have to add exceptions to the sanitization.

    const dirty = "<a href='' target='_blank'/>";
    
    console.log("W/O:", DOMPurify.sanitize(dirty));
    console.log("With:", DOMPurify.sanitize(dirty, {ADD_ATTR: ['target']}));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.3/purify.min.js"></script>

    However the way you add target to the tag is questionable as well. Instead of doing it manually, maybe you should try to use features of the library you use for sanitization in the first place.

    DOMPurify.addHook('afterSanitizeAttributes', (node) => {
      // you could probably check via node.tagName but to replicate your logic exactly I check for href attribute
      if ('href' in node) node.setAttribute('target', '_blank');
    });