Roll back to a complicated regex? To avoid this problem, most developers may use some libraries like js-xss or DOMPurify to handle complicated regular expressions for them. Nevertheless, these libraries are sometimes too strict to remove some safe tags, except if you can set up the right configurations.
Here, I just want to clarify another simple way where we can rely on the iframe sandbox technics, which can also forbid browsers from accessing resources from the content like loading images:
const unsafe = '<img src=x onerror=alert(1)>';
const $frame = $('<iframe>').appendTo('body'), $sandboxDoc = $frame.contents();
$frame.remove();
// scripts have been blocked
// the document won't also access the image which throws us a 404
$('<div>', $sandboxDoc).html(unsafe).find('img').remove();
// by pure javascript
const frame = document.createElement('iframe');
document.body.appendChild(frame);
const sandboxDoc = frame.contentDocument;
frame.parentNode.removeChild(frame);
const div = sandboxDoc.createElement('div');
div.innerHTML = unsafe;
const img = div.querySelector('img');
img && img.parentNode.removeChild(img);
Such a document still has a side effect in which DOM nodes won't compute out any styles:
$('<img style="cursor: zoom-in">', $sandboxDoc).css('cursor'); // => ''
// by pure javascript
const img = sandboxDoc.createElement('img');
img.setAttribute('style', 'cursor:zoom-in');
img.style.cursor; // => ''
It means that we will have some unexpected results in some situations like we cannot remove the cursor:
$('<img style="cursor: zoom-in">', $sandboxDoc)
.css({cursor : ''}).prop('outerHTML'); // => '<img style="cursor: zoom-in">'
// by pure javascript
img.style.cursor = '';
img.outerHTML; // => '<img style="cursor:zoom-in">'
To work around this, we should ensure to construct DOM nodes before removing the document:
function $sandbox(content) {
const $frame = $('<iframe>').appendTo('body');
const $elem = $(content, $frame.contents());
$frame.remove();
return $elem;
}
$sandbox('<img style="cursor: zoom-in">').css('cursor'); // => 'zoom-in'
// by pure javascript
function sandbox(fn) {
const frame = document.createElement('iframe');
document.body.appendChild(frame);
const elem = fn(frame.contentDocument);
frame.parentNode.removeChild(frame);
return elem;
}
sandbox(sandboxDoc => {
const img = sandboxDoc.createElement('img');
img.setAttribute('style', 'cursor:zoom-in');
return img;
}).style.cursor; // => 'zoom-in'