1

I have read in several places that scripts are not executed when we use React dangerouslySetInnerHTML to insert a html fragment.

However I just tried to insert this:

<img src= "img.png" onload="alert('picture loaded')" alt="script test">  

And the alert was triggered.

That doesn't really surprise me (that's why I did the test in the first place), but I'd like to better understand what we mean by "scripts don't execute".

My questions:

  • apart from my example above, are there other types of scripts that will execute?
  • is there a way to fully prevent script execution, including those embedded in html event handlers like in my example?
  • if a script tag defines a function, will that function still be loaded and be callable later?
  • any other behavior I should be aware of?

[Edit] I am using dangerouslySetInnerHTMLin a function component:

const htmlString = '<img src="img.png" onload="alert('picture loaded')" alt="script test">'

In the return statement (JSX):

return <div dangerouslySetInnerHTML={{__html: htmlString}} />
2
  • 1
    How are you inserting this? Commented Oct 5, 2020 at 0:49
  • Ihave added some more info to the post. Hopefully the answer doesn't depend on how dangerouslySetInnerHTML isused. Commented Oct 5, 2020 at 0:55

1 Answer 1

6

The scripts that "will not execute" are script tags, like <script>. See here for an example:

const html = `
<script>console.log('this does NOT run');<\/script>
<img src onerror="console.log('but this will')">
`;
const App = () => {
  return <div dangerouslySetInnerHTML={{__html: html}} />;
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>

Inline handlers, not being <script> tags, can run, if the event they're attached to fires. (Above, an <img> tag with a src property but no valid path throws an error, so its onerror inline handler runs)

There are no other categories of scripts that will run in combination with dangerouslySetInnerHTML (unless an inline handler itself injects a <script> by other means, like with document.createElement('script')).

is there a way to fully prevent script execution, including those embedded in html event handlers like in my example?

You'll need to remove the on- attributes. If all on- attributes are removed, no events which may be fired will ever result in unexpected scripts running. You could sanitize the input by sending it through DOMParser first:

const sanitize = (input) => {
  const doc = new DOMParser().parseFromString(input, 'text/html');
  for (const elm of doc.querySelectorAll('*')) {
    for (const attrib of elm.attributes) {
      if (attrib.name.startsWith('on')) {
        elm.removeAttribute(attrib.name);
      }
    }
  }
  return doc.body.innerHTML;
};

const html = `
<div>
  <script>console.log('this does NOT run');<\/script>
  <img src onerror="console.log('but this will')">
  more content
  <style type="text/css"> img {float: left; margin: 5px}</style> 
</div>
`;
const App = () => {
  return <div dangerouslySetInnerHTML={{__html: sanitize(html)}} />;
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
<img src="https://www.gravatar.com/avatar/f117a950c689d3d6ec459885a908166e?s=32&d=identicon&r=PG">

if a script tag defines a function, will that function still be loaded and be callable later?

No, because the <script> tag will not have run at all, so nothing that goes on inside of it will do anything; functions defined inside will not become visible. For such a thing to occur, you'd have to deliberately reload the newly injected <script> tag somehow, in a manner that gets it to run.

Sign up to request clarification or add additional context in comments.

10 Comments

Quick question, out of curiosity: could the sanitize method also remove the scripts?
You can use if (elm.tagName === 'SCRIPT') elm.remove();
fyi my IDE was not happy with the for loops (says not arrays) and I had to write them differently.
For some reason, the sanitize function is also wiping out an inline style element in my test.
It looks to be applied properly to me, see edit to snippet in answer
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.