sanitize-html 2.5.1 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +12 -0
- package/index.js +25 -13
- package/package.json +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.6.1 (2021-12-08)
|
|
4
|
+
|
|
5
|
+
- Fixes style filtering to retain `!important` when used.
|
|
6
|
+
- Fixed trailing text bug on `transformTags` options that was reported on [issue #506](https://github.com/apostrophecms/sanitize-html/issues/506). Thanks to [Alex Rantos](https://github.com/alex-rantos).
|
|
7
|
+
|
|
8
|
+
## 2.6.0 (2021-11-23)
|
|
9
|
+
|
|
10
|
+
- Support for regular expressions in the `allowedClasses` option. Thanks to [Alex Rantos](https://github.com/alex-rantos).
|
|
11
|
+
|
|
12
|
+
## 2.5.3 (2021-11-02):
|
|
13
|
+
|
|
14
|
+
- Fixed bug introduced by klona 2.0.5, by removing klona entirely.
|
|
15
|
+
|
|
16
|
+
## 2.5.2 (2021-10-13):
|
|
17
|
+
|
|
18
|
+
- Nullish HTML input now returns an empty string. Nullish value may be explicit `null`, `undefined` or implicit `undefined` when value is not provided. Thanks to Artem Kostiuk for the contribution.
|
|
19
|
+
- Documented that all text content is escaped. Thanks to Siddharth Singh.
|
|
20
|
+
|
|
3
21
|
## 2.5.1 (2021-09-14):
|
|
4
22
|
- The `allowedScriptHostnames` and `allowedScriptDomains` options now implicitly purge the inline content of all script tags, not just those with `src` attributes. This behavior was already strongly implied by the fact that they purged it in the case where a `src` attribute was actually present, and is necessary for the feature to provide any real security. Thanks to Grigorii Duca for pointing out the issue.
|
|
5
23
|
|
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ The syntax of poorly closed `p` and `img` elements is cleaned up.
|
|
|
21
21
|
Allowing particular urls as a `src` to an iframe tag by filtering hostnames is also supported.
|
|
22
22
|
|
|
23
23
|
HTML comments are not preserved.
|
|
24
|
+
Additionally, `sanitize-html` escapes _ALL_ text content - this means that ampersands, greater-than, and less-than signs are converted to their equivalent HTML character references (`&` --> `&`, `<` --> `<`, and so on). Additionally, in attribute values, quotation marks are escaped as well (`"` --> `"`).
|
|
24
25
|
|
|
25
26
|
## Requirements
|
|
26
27
|
|
|
@@ -238,6 +239,7 @@ const clean = sanitizeHtml(dirty, {
|
|
|
238
239
|
```
|
|
239
240
|
|
|
240
241
|
Similar to `allowedAttributes`, you can use `*` to allow classes with a certain prefix, or use `*` as a tag name to allow listed classes to be valid for any tag:
|
|
242
|
+
|
|
241
243
|
```js
|
|
242
244
|
allowedClasses: {
|
|
243
245
|
'code': [ 'language-*', 'lang-*' ],
|
|
@@ -245,6 +247,16 @@ allowedClasses: {
|
|
|
245
247
|
}
|
|
246
248
|
```
|
|
247
249
|
|
|
250
|
+
Furthermore, regular expressions are supported too:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
allowedClasses: {
|
|
254
|
+
p: [ /^regex\d{2}$/ ]
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> Note: It is advised that your regular expressions always begin with `^` so that you are requiring a known prefix. A regular expression with neither `^` nor `$` just requires that something appear in the middle.
|
|
259
|
+
|
|
248
260
|
### Allowed CSS Styles
|
|
249
261
|
|
|
250
262
|
If you wish to allow specific CSS _styles_ on a particular element, you can do that with the `allowedStyles` option. Simply declare your desired attributes as regular expression options within an array for the given attribute. Specific elements will inherit allowlisted attributes from the global (`*`) attribute. Any other CSS classes are discarded.
|
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const htmlparser = require('htmlparser2');
|
|
2
2
|
const escapeStringRegexp = require('escape-string-regexp');
|
|
3
|
-
const { klona } = require('klona');
|
|
4
3
|
const { isPlainObject } = require('is-plain-object');
|
|
5
4
|
const deepmerge = require('deepmerge');
|
|
6
5
|
const parseSrcset = require('parse-srcset');
|
|
@@ -81,6 +80,10 @@ const VALID_HTML_ATTRIBUTE_NAME = /^[^\0\t\n\f\r /<=>]+$/;
|
|
|
81
80
|
// https://github.com/fb55/htmlparser2/issues/105
|
|
82
81
|
|
|
83
82
|
function sanitizeHtml(html, options, _recursing) {
|
|
83
|
+
if (html == null) {
|
|
84
|
+
return '';
|
|
85
|
+
}
|
|
86
|
+
|
|
84
87
|
let result = '';
|
|
85
88
|
// Used for hot swapping the result variable with an empty string in order to "capture" the text written to it.
|
|
86
89
|
let tempResult = '';
|
|
@@ -153,6 +156,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
153
156
|
}
|
|
154
157
|
const allowedClassesMap = {};
|
|
155
158
|
const allowedClassesGlobMap = {};
|
|
159
|
+
const allowedClassesRegexMap = {};
|
|
156
160
|
each(options.allowedClasses, function(classes, tag) {
|
|
157
161
|
// Implicitly allows the class attribute
|
|
158
162
|
if (allowedAttributesMap) {
|
|
@@ -163,10 +167,13 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
163
167
|
}
|
|
164
168
|
|
|
165
169
|
allowedClassesMap[tag] = [];
|
|
170
|
+
allowedClassesRegexMap[tag] = [];
|
|
166
171
|
const globRegex = [];
|
|
167
172
|
classes.forEach(function(obj) {
|
|
168
173
|
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
|
|
169
174
|
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
|
|
175
|
+
} else if (obj instanceof RegExp) {
|
|
176
|
+
allowedClassesRegexMap[tag].push(obj);
|
|
170
177
|
} else {
|
|
171
178
|
allowedClassesMap[tag].push(obj);
|
|
172
179
|
}
|
|
@@ -428,12 +435,16 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
428
435
|
const allowedSpecificClasses = allowedClassesMap[name];
|
|
429
436
|
const allowedWildcardClasses = allowedClassesMap['*'];
|
|
430
437
|
const allowedSpecificClassesGlob = allowedClassesGlobMap[name];
|
|
438
|
+
const allowedSpecificClassesRegex = allowedClassesRegexMap[name];
|
|
431
439
|
const allowedWildcardClassesGlob = allowedClassesGlobMap['*'];
|
|
432
|
-
const allowedClassesGlobs = [
|
|
433
|
-
|
|
440
|
+
const allowedClassesGlobs = [
|
|
441
|
+
allowedSpecificClassesGlob,
|
|
442
|
+
allowedWildcardClassesGlob
|
|
443
|
+
]
|
|
444
|
+
.concat(allowedSpecificClassesRegex)
|
|
445
|
+
.filter(function (t) {
|
|
434
446
|
return t;
|
|
435
|
-
}
|
|
436
|
-
);
|
|
447
|
+
});
|
|
437
448
|
if (allowedSpecificClasses && allowedWildcardClasses) {
|
|
438
449
|
value = filterClasses(value, deepmerge(allowedSpecificClasses, allowedWildcardClasses), allowedClassesGlobs);
|
|
439
450
|
} else {
|
|
@@ -571,6 +582,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
571
582
|
result = tempResult + escapeHtml(result);
|
|
572
583
|
tempResult = '';
|
|
573
584
|
}
|
|
585
|
+
addedText = false;
|
|
574
586
|
}
|
|
575
587
|
}, options.parser);
|
|
576
588
|
parser.write(html);
|
|
@@ -646,18 +658,18 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
646
658
|
|
|
647
659
|
/**
|
|
648
660
|
* Filters user input css properties by allowlisted regex attributes.
|
|
661
|
+
* Modifies the abstractSyntaxTree object.
|
|
649
662
|
*
|
|
650
663
|
* @param {object} abstractSyntaxTree - Object representation of CSS attributes.
|
|
651
664
|
* @property {array[Declaration]} abstractSyntaxTree.nodes[0] - Each object cointains prop and value key, i.e { prop: 'color', value: 'red' }.
|
|
652
665
|
* @param {object} allowedStyles - Keys are properties (i.e color), value is list of permitted regex rules (i.e /green/i).
|
|
653
|
-
* @return {object} -
|
|
666
|
+
* @return {object} - The modified tree.
|
|
654
667
|
*/
|
|
655
668
|
function filterCss(abstractSyntaxTree, allowedStyles) {
|
|
656
669
|
if (!allowedStyles) {
|
|
657
670
|
return abstractSyntaxTree;
|
|
658
671
|
}
|
|
659
672
|
|
|
660
|
-
const filteredAST = klona(abstractSyntaxTree);
|
|
661
673
|
const astRules = abstractSyntaxTree.nodes[0];
|
|
662
674
|
let selectedRule;
|
|
663
675
|
|
|
@@ -672,24 +684,24 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
672
684
|
}
|
|
673
685
|
|
|
674
686
|
if (selectedRule) {
|
|
675
|
-
|
|
687
|
+
abstractSyntaxTree.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
|
|
676
688
|
}
|
|
677
689
|
|
|
678
|
-
return
|
|
690
|
+
return abstractSyntaxTree;
|
|
679
691
|
}
|
|
680
692
|
|
|
681
693
|
/**
|
|
682
|
-
* Extracts the style
|
|
694
|
+
* Extracts the style attributes from an AbstractSyntaxTree and formats those
|
|
683
695
|
* values in the inline style attribute format.
|
|
684
696
|
*
|
|
685
697
|
* @param {AbstractSyntaxTree} filteredAST
|
|
686
|
-
* @return {string} - Example: "color:yellow;text-align:center;font-family:helvetica;"
|
|
698
|
+
* @return {string} - Example: "color:yellow;text-align:center !important;font-family:helvetica;"
|
|
687
699
|
*/
|
|
688
700
|
function stringifyStyleAttributes(filteredAST) {
|
|
689
701
|
return filteredAST.nodes[0].nodes
|
|
690
|
-
.reduce(function(extractedAttributes,
|
|
702
|
+
.reduce(function(extractedAttributes, attrObject) {
|
|
691
703
|
extractedAttributes.push(
|
|
692
|
-
|
|
704
|
+
`${attrObject.prop}:${attrObject.value}${attrObject.important ? ' !important' : ''}`
|
|
693
705
|
);
|
|
694
706
|
return extractedAttributes;
|
|
695
707
|
}, [])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanitize-html",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Clean up user-submitted HTML, preserving allowlisted elements and allowlisted attributes on a per-element basis",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "index.js",
|
|
@@ -27,15 +27,14 @@
|
|
|
27
27
|
"escape-string-regexp": "^4.0.0",
|
|
28
28
|
"htmlparser2": "^6.0.0",
|
|
29
29
|
"is-plain-object": "^5.0.0",
|
|
30
|
-
"klona": "^2.0.3",
|
|
31
30
|
"parse-srcset": "^1.0.2",
|
|
32
|
-
"postcss": "^8.
|
|
31
|
+
"postcss": "^8.3.11"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"eslint": "^7.3.1",
|
|
36
35
|
"eslint-config-apostrophe": "^3.4.0",
|
|
37
36
|
"eslint-config-standard": "^14.1.1",
|
|
38
|
-
"eslint-plugin-import": "^2.
|
|
37
|
+
"eslint-plugin-import": "^2.25.2",
|
|
39
38
|
"eslint-plugin-node": "^11.1.0",
|
|
40
39
|
"eslint-plugin-promise": "^4.2.1",
|
|
41
40
|
"eslint-plugin-standard": "^4.0.1",
|