-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e052a83
commit 8e10675
Showing
2 changed files
with
76 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,29 +8,37 @@ import Singleton from '../abstracts/Singleton'; | |
* as attributes, i.e. `onload` or `onerror`) or contain the `javascript:` pseudo protocol in their | ||
* values. | ||
* | ||
* This is only a simple sanitizer and does not attempt to be exhaustive. If you're truly paranoid | ||
* about the input, you might want to consider a more robust sanitizer like | ||
* [DOMPurify](https://github.com/cure53/DOMPurify). | ||
* | ||
* @author Ben Thomson <[email protected]> | ||
*/ | ||
export default class Sanitizer extends Singleton { | ||
construct() { | ||
// Add to global function for backwards compatibility | ||
window.wnSanitize = (html) => this.sanitize(html); | ||
window.ocSanitize = window.wnSanitize; | ||
} | ||
|
||
sanitize(html, bodyOnly) { | ||
/** | ||
* Sanitizes a HTML string. | ||
* | ||
* @param {string} html | ||
* @param {boolean} bodyOnly | ||
* @returns {string} | ||
*/ | ||
sanitize(html, bodyOnly = true) { | ||
const parser = new DOMParser(); | ||
const dom = parser.parseFromString(html, 'text/html'); | ||
const returnBodyOnly = (bodyOnly !== undefined && typeof bodyOnly === 'boolean') | ||
? bodyOnly | ||
: true; | ||
|
||
this.sanitizeNode(dom.getRootNode()); | ||
|
||
return (returnBodyOnly) ? dom.body.innerHTML : dom.innerHTML; | ||
return (bodyOnly) ? dom.body.innerHTML : dom.documentElement.outerHTML; | ||
} | ||
|
||
/** | ||
* Sanitizes an individual node. | ||
* | ||
* @param {Node} node | ||
* @returns {void} | ||
*/ | ||
sanitizeNode(node) { | ||
if (node.tagName === 'SCRIPT') { | ||
if (['SCRIPT', 'IFRAME', 'OBJECT'].includes(node.tagName)) { | ||
node.remove(); | ||
return; | ||
} | ||
|
@@ -44,6 +52,12 @@ export default class Sanitizer extends Singleton { | |
}); | ||
} | ||
|
||
/** | ||
* Sanitizes the attributes of a node. | ||
* | ||
* @param {Node} node | ||
* @returns {void} | ||
*/ | ||
trimAttributes(node) { | ||
if (!node.attributes) { | ||
return; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import TestInstance from '../../src/main/Snowboard'; | ||
|
||
describe('Sanitizer utility', () => { | ||
beforeEach(() => { | ||
document.currentScript.dataset.baseUrl = 'https://example.com'; | ||
document.currentScript.dataset.assetUrl = 'https://example.com/fixtures/assets/'; | ||
|
||
window.Snowboard = new TestInstance(); | ||
}); | ||
|
||
it('sanitizes some common XSS vectors', () => { | ||
const html = `<div onclick="javascript:alert(1)"> | ||
<script src="/myscript.js"></script> | ||
<script> | ||
document.body.innerHTML = 'Hacked!'; | ||
</script> | ||
<p>We're cool. <iframe src="blah.html">But we're not.</iframe></p> | ||
<a href="javascript:alert(2)"></a> | ||
<img src="myimage.png" onload="javascript:alert(3)"> | ||
</div>`; | ||
|
||
const output = Snowboard.sanitizer().sanitize(html).replace(/\s+/g, ' '); | ||
|
||
expect(output).toEqual('<div> <p>We\'re cool. </p> <a></a> <img src="myimage.png"> </div>'); | ||
}); | ||
|
||
it('can return the full html', () => { | ||
const html = `<html> | ||
<head> | ||
<title>Hi</title> | ||
<script src="/myscript.js"></script> | ||
</head> | ||
<body> | ||
<div onclick="javascript:alert(1)"> | ||
<script src="/myscript.js"></script> | ||
<script> | ||
document.body.innerHTML = 'Hacked!'; | ||
</script> | ||
<p>We're cool. <iframe src="blah.html">But we're not.</iframe></p> | ||
<a href="javascript:alert(2)"></a> | ||
<img src="myimage.png" onload="javascript:alert(3)"> | ||
</div> | ||
</body> | ||
</html>`; | ||
|
||
const output = Snowboard.sanitizer().sanitize(html, false).replace(/\s+/g, ' '); | ||
|
||
expect(output).toEqual('<html><head> <title>Hi</title> </head> <body> <div> <p>We\'re cool. </p> <a></a> <img src=\"myimage.png\"> </div> </body></html>'); | ||
}); | ||
}); |