Removing all script tags from html with JS Regular Expression
Solution 1:
jQuery uses a regex to remove script tags in some cases and I'm pretty sure its devs had a damn good reason to do so. Probably some browser does execute scripts when inserting them using innerHTML
.
Here's the regex:
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi
And before people start crying "but regexes for HTML are evil": Yes, they are - but for script tags they are safe because of the special behaviour - a <script>
section may not contain </script>
at all unless it should end at this position. So matching it with a regex is easily possible. However, from a quick look the regex above does not account for trailing whitespace inside the closing tag so you'd have to test if </script
etc. will still work.
Solution 2:
Attempting to remove HTML markup using a regular expression is problematic. You don't know what's in there as script or attribute values. One way is to insert it as the innerHTML of a div, remove any script elements and return the innerHTML, e.g.
function stripScripts(s) {
var div = document.createElement('div');
div.innerHTML = s;
var scripts = div.getElementsByTagName('script');
var i = scripts.length;
while (i--) {
scripts[i].parentNode.removeChild(scripts[i]);
}
return div.innerHTML;
}
alert(
stripScripts('<span><script type="text/javascript">alert(\'foo\');<\/script><\/span>')
);
Note that at present, browsers will not execute the script if inserted using the innerHTML property, and likely never will especially as the element is not added to the document.
Solution 3:
Regexes are beatable, but if you have a string version of HTML that you don't want to inject into a DOM, they may be the best approach. You may want to put it in a loop to handle something like:
<scr<script>Ha!</script>ipt> alert(document.cookie);</script>
Here's what I did, using the jquery regex from above:
var SCRIPT_REGEX = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
while (SCRIPT_REGEX.test(text)) {
text = text.replace(SCRIPT_REGEX, "");
}
Solution 4:
This Regex should work too:
<script(?:(?!\/\/)(?!\/\*)[^'"]|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\/\/.*(?:\n)|\/\*(?:(?:.|\s))*?\*\/)*?<\/script>
It even allows to have "problematic" variable strings like these inside:
<script type="text/javascript">
var test1 = "</script>";
var test2 = '\'</script>';
var test1 = "\"</script>";
var test1 = "<script>\"";
var test2 = '<scr\'ipt>';
/* </script> */
// </script>
/* ' */
// var foo=" '
</script>
It seams that jQuery and Prototype fail on these ones...
Edit July 31 '17: Added a) non-capturing groups for better performance (and no empty groups) and b) support for JavaScript comments.
Solution 5:
Whenever you have to resort to Regex based script tag cleanup. At least add a white-space to the closing tag in the form of
</script\s*>
Otherwise things like
<script>alert(666)</script >
would remain since trailing spaces after tagnames are valid.