|
13 | 13 | /* global DOMPurify */
|
14 | 14 | 'use strict';
|
15 | 15 | window.onload = function(){
|
16 |
| - |
| 16 | + |
17 | 17 | // Specify dirty HTML
|
18 |
| - var dirty = document.getElementById('payload').value; |
| 18 | + let dirty = document.getElementById('payload').value; |
19 | 19 |
|
20 | 20 | // We can allow all (default elements) but SVG
|
21 |
| - var config = { |
| 21 | + const config = { |
22 | 22 | FORBID_TAGS: ['svg'] // SVG is not yet supported. Too messy.
|
23 | 23 | };
|
24 | 24 |
|
25 |
| - // Specify CSS property allow-list |
26 |
| - var allowed_properties = [ |
27 |
| - 'color', |
| 25 | + // Specify CSS property whitelist |
| 26 | + const allowed_properties = [ |
| 27 | + 'color', |
28 | 28 | 'background',
|
29 |
| - 'border', |
30 |
| - 'padding', |
| 29 | + 'border', |
| 30 | + 'padding', |
31 | 31 | 'margin',
|
32 | 32 | 'font-family',
|
33 | 33 | 'content',
|
34 | 34 | 'padding'
|
35 | 35 | ];
|
36 | 36 |
|
37 | 37 | // Specify if CSS functions are permitted
|
38 |
| - var allow_css_functions = true; |
| 38 | + const allow_css_functions = true; |
39 | 39 |
|
40 | 40 | /**
|
41 |
| - * Take CSS property-value pairs and validate against allow-list, |
| 41 | + * Take CSS property-value pairs and validate against white-list, |
42 | 42 | * then add the styles to an array of property-value pairs
|
43 | 43 | */
|
44 | 44 | function validateStyles(output, styles) {
|
45 |
| - // Validate regular CSS properties |
46 |
| - for (var prop in styles) { |
47 |
| - if (typeof styles[prop] === 'string') { |
48 |
| - if (styles[prop] && allowed_properties.indexOf(prop) > -1) { |
49 |
| - if (allow_css_functions || !/\w+\(/.test(styles[prop])) { |
50 |
| - output.push(prop + ':' + styles[prop] +';'); |
51 |
| - } |
| 45 | + Object.keys(styles).forEach(prop => { |
| 46 | + const value = styles[prop]; |
| 47 | + if (value && typeof value === 'string') { |
| 48 | + const normalizedProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase(); |
| 49 | + if (allowed_properties.includes(normalizedProp) && |
| 50 | + (allow_css_functions || !/\w+\(/.test(value))) { |
| 51 | + output.push(`${normalizedProp}:${value};`); |
52 | 52 | }
|
53 | 53 | }
|
54 |
| - } |
| 54 | + }); |
55 | 55 | }
|
56 | 56 |
|
57 | 57 | /**
|
58 | 58 | * Take CSS rules and analyze them, create string wrapper to
|
59 |
| - * apply them to the DOM later on. Note that only selector rules |
| 59 | + * apply them to the DOM later on. Note that only selector rules |
60 | 60 | * are supported right now
|
61 | 61 | */
|
62 | 62 | function addCSSRules(output, cssRules) {
|
63 |
| - for (var index = cssRules.length-1; index >= 0; index--) { |
64 |
| - var rule = cssRules[index]; |
| 63 | + Array.from(cssRules).reverse().forEach(rule => { |
65 | 64 | // check for rules with selector
|
66 |
| - if (rule.type == 1 && rule.selectorText) { |
67 |
| - output.push(rule.selectorText + '{') |
| 65 | + if (rule.type === 1 && rule.selectorText) { |
| 66 | + output.push(`${rule.selectorText}{`); |
68 | 67 | if (rule.style) {
|
69 |
| - validateStyles(output, rule.style) |
| 68 | + validateStyles(output, rule.style); |
70 | 69 | }
|
71 | 70 | output.push('}');
|
72 | 71 | }
|
73 |
| - } |
| 72 | + }); |
74 | 73 | }
|
75 | 74 |
|
76 | 75 | // Add a hook to enforce CSS element sanitization
|
77 | 76 | DOMPurify.addHook('uponSanitizeElement', function(node, data) {
|
78 | 77 | if (data.tagName === 'style') {
|
79 |
| - var output = []; |
| 78 | + let output = []; |
80 | 79 | addCSSRules(output, node.sheet.cssRules);
|
81 | 80 | node.textContent = output.join("\n");
|
82 | 81 | }
|
|
86 | 85 | DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
87 | 86 | // Nasty hack to fix baseURI + CSS problems in Chrome
|
88 | 87 | if (!node.ownerDocument.baseURI) {
|
89 |
| - var base = document.createElement('base'); |
| 88 | + let base = document.createElement('base'); |
90 | 89 | base.href = document.baseURI;
|
91 | 90 | node.ownerDocument.head.appendChild(base);
|
92 | 91 | }
|
93 | 92 | // Check all style attribute values and validate them
|
94 | 93 | if (node.hasAttribute('style')) {
|
95 |
| - var output = []; |
| 94 | + let output = []; |
96 | 95 | validateStyles(output, node.style);
|
97 | 96 | // re-add styles in case any are left
|
98 | 97 | if (output.length) {
|
|
104 | 103 | });
|
105 | 104 |
|
106 | 105 | // Clean HTML string and write into our DIV
|
107 |
| - var clean = DOMPurify.sanitize(dirty, config); |
| 106 | + let clean = DOMPurify.sanitize(dirty, config); |
108 | 107 | document.getElementById('sanitized').innerHTML = clean;
|
109 | 108 | }
|
110 | 109 | </script>
|
|
0 commit comments