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.