diff --git a/README.md b/README.md
index e64d86d4d..627e55b75 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG.
-It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.0.8**.
+It's also very simple to use and get started with. DOMPurify was [started in February 2014](https://github.com/cure53/DOMPurify/commit/a630922616927373485e0e787ab19e73e3691b2b) and, meanwhile, has reached version **v3.0.9**.
DOMPurify is written in JavaScript and works in all modern browsers (Safari (10+), Opera (15+), Edge, Firefox and Chrome - as well as almost anything else using Blink, Gecko or WebKit). It doesn't break on MSIE or other legacy browsers. It simply does nothing.
@@ -413,6 +413,6 @@ Many people helped and help DOMPurify become what it is and need to be acknowled
## Testing powered by
- elements allowed
// Note: We want to also keep 's text content, so we add #text too
-var config = { ALLOWED_TAGS: ['p', '#text'], KEEP_CONTENT: false };
+const config = { ALLOWED_TAGS: ['p', '#text'], KEEP_CONTENT: false };
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Advanced Config Demo [Link](advanced-config-demo.html)
@@ -38,7 +38,7 @@ This is the relevant code:
```javascript
// Specify a configuration directive
-var config = {
+const config = {
ALLOWED_TAGS: ['p', '#text'], // only and text nodes
KEEP_CONTENT: false, // remove content from non-allow-listed nodes too
ADD_ATTR: ['kitty-litter'], // permit kitty-litter attributes
@@ -47,7 +47,7 @@ var config = {
};
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hooks Demo [Link](hooks-demo.html)
@@ -66,7 +66,7 @@ DOMPurify.addHook('beforeSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Add hooks and remove hooks [Link](hooks-removal-demo.html)
@@ -85,13 +85,13 @@ DOMPurify.addHook('beforeSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+let clean = DOMPurify.sanitize(dirty);
// now let's remove the hook again
console.log(DOMPurify.removeHook('beforeSanitizeAttributes'));
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+let clean = DOMPurify.sanitize(dirty);
```
### Hook to open all links in a new window [Link](hooks-target-blank-demo.html)
@@ -117,7 +117,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to white-list safe URI Schemes [Link](hooks-scheme-allowlist.html)
@@ -130,15 +130,15 @@ This is the relevant code:
```javascript
// allowed URI schemes
-var allowlist = ['http', 'https', 'ftp'];
+const allowlist = ['http', 'https', 'ftp'];
// build fitting regex
-var regex = RegExp('^(' + allowlist.join('|') + '):', 'gim');
+const regex = RegExp('^(' + allowlist.join('|') + '):', 'gim');
// Add a hook to enforce URI scheme allow-list
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// build an anchor to map URLs to
- var anchor = document.createElement('a');
+ const anchor = document.createElement('a');
// check all href attributes for validity
if (node.hasAttribute('href')) {
@@ -164,7 +164,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to allow and sand-box all JavaScript [Link](hooks-mentaljs-demo.html)
@@ -177,7 +177,7 @@ This is the relevant code:
```javascript
// allow script elements
-var config = {
+const config = {
ADD_TAGS: ['script'],
ADD_ATTR: ['onclick', 'onmouseover', 'onload', 'onunload'],
};
@@ -185,7 +185,7 @@ var config = {
// Add a hook to sanitize all script content with MentalJS
DOMPurify.addHook('uponSanitizeElement', function (node, data) {
if (data.tagName === 'script') {
- var script = node.textContent;
+ let script = node.textContent;
if (
!script ||
'src' in node.attributes ||
@@ -195,7 +195,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
return node.parentNode.removeChild(node);
}
try {
- var mental = MentalJS().parse({
+ let mental = MentalJS().parse({
options: {
eval: false,
dom: true,
@@ -212,7 +212,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
// Add a hook to sanitize all white-listed events with MentalJS
DOMPurify.addHook('uponSanitizeAttribute', function (node, data) {
if (data.attrName.match(/^on\w+/)) {
- var script = data.attrValue;
+ let script = data.attrValue;
try {
return (data.attrValue = MentalJS().parse({
options: {
@@ -228,7 +228,7 @@ DOMPurify.addHook('uponSanitizeAttribute', function (node, data) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hook to proxy all links [Link](hooks-link-proxy-demo.html)
@@ -264,7 +264,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty);
+const clean = DOMPurify.sanitize(dirty);
```
### Hook to proxy all HTTP leaks including CSS [Link](hooks-proxy-demo.html)
@@ -277,28 +277,28 @@ This is the relevant code:
```javascript
// Specify proxy URL
-var proxy = 'https://my.proxy/?url=';
+const proxy = 'https://my.proxy/?url=';
// What do we allow? Not much for now. But it's tight.
-var config = {
+const config = {
FORBID_TAGS: ['svg'],
WHOLE_DOCUMENT: true,
};
// Specify attributes to proxy
-var attributes = ['action', 'background', 'href', 'poster', 'src', 'srcset']
+const attributes = ['action', 'background', 'href', 'poster', 'src', 'srcset']
// specify the regex to detect external content
-var regex = /(url\("?)(?!data:)/gim;
+const regex = /(url\("?)(?!data:)/gim;
/**
* Take CSS property-value pairs and proxy URLs in values,
* then add the styles to an array of property-value pairs
*/
function addStyles(output, styles) {
- for (var prop = styles.length - 1; prop >= 0; prop--) {
+ for (let prop = styles.length - 1; prop >= 0; prop--) {
if (styles[styles[prop]]) {
- var url = styles[styles[prop]].replace(regex, '$1' + proxy);
+ let url = styles[styles[prop]].replace(regex, '$1' + proxy);
styles[styles[prop]] = url;
}
if (styles[styles[prop]]) {
@@ -312,8 +312,8 @@ function addStyles(output, styles) {
* then create matching CSS text for later application to the DOM
*/
function addCSSRules(output, cssRules) {
- for (var index = cssRules.length - 1; index >= 0; index--) {
- var rule = cssRules[index];
+ for (let index = cssRules.length - 1; index >= 0; index--) {
+ let rule = cssRules[index];
// check for rules with selector
if (rule.type == 1 && rule.selectorText) {
output.push(rule.selectorText + '{');
@@ -336,8 +336,8 @@ function addCSSRules(output, cssRules) {
// check for @keyframes rules
} else if (rule.type === rule.KEYFRAMES_RULE) {
output.push('@keyframes ' + rule.name + '{');
- for (var i = rule.cssRules.length - 1; i >= 0; i--) {
- var frame = rule.cssRules[i];
+ for (let i = rule.cssRules.length - 1; i >= 0; i--) {
+ let frame = rule.cssRules[i];
if (frame.type === 8 && frame.keyText) {
output.push(frame.keyText + '{');
if (frame.style) {
@@ -365,7 +365,7 @@ function proxyAttribute(url) {
// Add a hook to enforce proxy for leaky CSS rules
DOMPurify.addHook('uponSanitizeElement', function (node, data) {
if (data.tagName === 'style') {
- var output = [];
+ let output = [];
addCSSRules(output, node.sheet.cssRules);
node.textContent = output.join('\n');
}
@@ -374,7 +374,7 @@ DOMPurify.addHook('uponSanitizeElement', function (node, data) {
// Add a hook to enforce proxy for all HTTP leaks incl. inline CSS
DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// Check all src attributes and proxy them
- for (var i = 0; i <= attributes.length - 1; i++) {
+ for (let i = 0; i <= attributes.length - 1; i++) {
if (node.hasAttribute(attributes[i])) {
node.setAttribute(
attributes[i],
@@ -385,12 +385,12 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
// Check all style attribute values and proxy them
if (node.hasAttribute('style')) {
- var styles = node.style;
- var output = [];
- for (var prop = styles.length - 1; prop >= 0; prop--) {
+ let styles = node.style;
+ let output = [];
+ for (let prop = styles.length - 1; prop >= 0; prop--) {
// we re-write each property-value pair to remove invalid CSS
if (node.style[styles[prop]] && regex.test(node.style[styles[prop]])) {
- var url = node.style[styles[prop]].replace(regex, '$1' + proxy);
+ let url = node.style[styles[prop]].replace(regex, '$1' + proxy);
node.style[styles[prop]] = url;
}
output.push(styles[prop] + ':' + node.style[styles[prop]] + ';');
@@ -405,7 +405,7 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean HTML string and write into our DIV
-var clean = DOMPurify.sanitize(dirty, config);
+const clean = DOMPurify.sanitize(dirty, config);
```
### Hook to sanitize SVGs shown via an `
+
And last but not least, thanks to [BrowserStack Open-Source Program](https://www.browserstack.com/open-source) for supporting this project with their services for free and delivering excellent, dedicated and very professional support on top of that.
diff --git a/bower.json b/bower.json
index 8468dcadc..1a50444f9 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "DOMPurify",
- "version": "3.0.8",
+ "version": "3.0.9",
"homepage": "https://github.com/cure53/DOMPurify",
"author": "Cure53 ` tag. [Link](hooks-svg-demo.html)
@@ -423,14 +423,14 @@ DOMPurify.addHook('afterSanitizeAttributes', function (node) {
});
// Clean SVG string and allow the "filter" tag
-var clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['filter'] });
+const clean = DOMPurify.sanitize(dirty, { ADD_TAGS: ['filter'] });
// Remove partial XML comment left in the HTML
-var badTag = clean.indexOf(']>');
-var pureSvg = clean.substring(badTag < 0 ? 0 : 5, clean.length);
+let badTag = clean.indexOf(']>');
+let pureSvg = clean.substring(badTag < 0 ? 0 : 5, clean.length);
// Show sanitized content in
element
-var img = new Image();
+let img = new Image();
img.src = 'data:image/svg+xml;base64,' + window.btoa(pureSvg);
document.getElementById('sanitized').appendChild(img);
```
diff --git a/demos/advanced-config-demo.html b/demos/advanced-config-demo.html
index 92b29d55b..9009f244e 100644
--- a/demos/advanced-config-demo.html
+++ b/demos/advanced-config-demo.html
@@ -9,28 +9,32 @@
diff --git a/demos/basic-demo.html b/demos/basic-demo.html
index b7577b14f..e5b02cadf 100644
--- a/demos/basic-demo.html
+++ b/demos/basic-demo.html
@@ -9,15 +9,16 @@
diff --git a/demos/config-demo.html b/demos/config-demo.html index f84f901d8..c56e0caee 100644 --- a/demos/config-demo.html +++ b/demos/config-demo.html @@ -9,20 +9,23 @@ diff --git a/demos/hooks-demo.html b/demos/hooks-demo.html index bd6c099c3..15ab3cc20 100644 --- a/demos/hooks-demo.html +++ b/demos/hooks-demo.html @@ -9,23 +9,24 @@ diff --git a/demos/hooks-link-proxy-demo.html b/demos/hooks-link-proxy-demo.html index 02d0ed188..7a7019e5b 100644 --- a/demos/hooks-link-proxy-demo.html +++ b/demos/hooks-link-proxy-demo.html @@ -9,44 +9,42 @@ diff --git a/demos/hooks-mentaljs-demo.html b/demos/hooks-mentaljs-demo.html index e951316f4..5f4ddd730 100644 --- a/demos/hooks-mentaljs-demo.html +++ b/demos/hooks-mentaljs-demo.html @@ -11,84 +11,73 @@
diff --git a/demos/hooks-node-removal-demo.html b/demos/hooks-node-removal-demo.html index 012312b90..c3cef45b9 100644 --- a/demos/hooks-node-removal-demo.html +++ b/demos/hooks-node-removal-demo.html @@ -9,23 +9,21 @@ diff --git a/demos/hooks-node-removal2-demo.html b/demos/hooks-node-removal2-demo.html index f222c6ce7..1350f910e 100644 --- a/demos/hooks-node-removal2-demo.html +++ b/demos/hooks-node-removal2-demo.html @@ -9,53 +9,49 @@ diff --git a/demos/hooks-proxy-demo.html b/demos/hooks-proxy-demo.html index 17076ce1b..d6f976863 100644 --- a/demos/hooks-proxy-demo.html +++ b/demos/hooks-proxy-demo.html @@ -9,147 +9,100 @@ diff --git a/demos/hooks-removal-demo.html b/demos/hooks-removal-demo.html index 1134c7303..da9675a2b 100644 --- a/demos/hooks-removal-demo.html +++ b/demos/hooks-removal-demo.html @@ -9,90 +9,53 @@ diff --git a/demos/hooks-sanitize-css-demo.html b/demos/hooks-sanitize-css-demo.html index 5ebd354c5..f01cb6486 100644 --- a/demos/hooks-sanitize-css-demo.html +++ b/demos/hooks-sanitize-css-demo.html @@ -13,21 +13,21 @@ /* global DOMPurify */ 'use strict'; window.onload = function(){ - + // Specify dirty HTML - var dirty = document.getElementById('payload').value; + let dirty = document.getElementById('payload').value; // We can allow all (default elements) but SVG - var config = { + const config = { FORBID_TAGS: ['svg'] // SVG is not yet supported. Too messy. }; - // Specify CSS property allow-list - var allowed_properties = [ - 'color', + // Specify CSS property whitelist + const allowed_properties = [ + 'color', 'background', - 'border', - 'padding', + 'border', + 'padding', 'margin', 'font-family', 'content', @@ -35,48 +35,47 @@ ]; // Specify if CSS functions are permitted - var allow_css_functions = true; + const allow_css_functions = true; /** - * Take CSS property-value pairs and validate against allow-list, + * Take CSS property-value pairs and validate against white-list, * then add the styles to an array of property-value pairs */ function validateStyles(output, styles) { - // Validate regular CSS properties - for (var prop in styles) { - if (typeof styles[prop] === 'string') { - if (styles[prop] && allowed_properties.indexOf(prop) > -1) { - if (allow_css_functions || !/\w+\(/.test(styles[prop])) { - output.push(prop + ':' + styles[prop] +';'); - } + Object.keys(styles).forEach(prop => { + const value = styles[prop]; + if (value && typeof value === 'string') { + const normalizedProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase(); + if (allowed_properties.includes(normalizedProp) && + (allow_css_functions || !/\w+\(/.test(value))) { + output.push(`${normalizedProp}:${value};`); } } - } + }); } /** * Take CSS rules and analyze them, create string wrapper to - * apply them to the DOM later on. Note that only selector rules + * apply them to the DOM later on. Note that only selector rules * are supported right now */ function addCSSRules(output, cssRules) { - for (var index = cssRules.length-1; index >= 0; index--) { - var rule = cssRules[index]; + Array.from(cssRules).reverse().forEach(rule => { // check for rules with selector - if (rule.type == 1 && rule.selectorText) { - output.push(rule.selectorText + '{') + if (rule.type === 1 && rule.selectorText) { + output.push(`${rule.selectorText}{`); if (rule.style) { - validateStyles(output, rule.style) + validateStyles(output, rule.style); } output.push('}'); } - } + }); } // Add a hook to enforce CSS element sanitization DOMPurify.addHook('uponSanitizeElement', function(node, data) { if (data.tagName === 'style') { - var output = []; + let output = []; addCSSRules(output, node.sheet.cssRules); node.textContent = output.join("\n"); } @@ -86,13 +85,13 @@ DOMPurify.addHook('afterSanitizeAttributes', function(node) { // Nasty hack to fix baseURI + CSS problems in Chrome if (!node.ownerDocument.baseURI) { - var base = document.createElement('base'); + let base = document.createElement('base'); base.href = document.baseURI; node.ownerDocument.head.appendChild(base); } // Check all style attribute values and validate them if (node.hasAttribute('style')) { - var output = []; + let output = []; validateStyles(output, node.style); // re-add styles in case any are left if (output.length) { @@ -104,7 +103,7 @@ }); // Clean HTML string and write into our DIV - var clean = DOMPurify.sanitize(dirty, config); + let clean = DOMPurify.sanitize(dirty, config); document.getElementById('sanitized').innerHTML = clean; } diff --git a/demos/hooks-scheme-allowlist.html b/demos/hooks-scheme-allowlist.html index 9f78fa1f3..8eb7411fc 100644 --- a/demos/hooks-scheme-allowlist.html +++ b/demos/hooks-scheme-allowlist.html @@ -9,68 +9,69 @@ diff --git a/demos/hooks-svg-demo.html b/demos/hooks-svg-demo.html deleted file mode 100644 index efb7dfd13..000000000 --- a/demos/hooks-svg-demo.html +++ /dev/null @@ -1,120 +0,0 @@ - - -
- - -
-