sanitize-html 2.5.2 → 2.7.0
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 +17 -0
- package/README.md +14 -4
- package/index.js +24 -17
- package/package.json +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.7.0 (2022-02-04)
|
|
4
|
+
|
|
5
|
+
- Allows a more sensible set of default attributes on `<img />` tags. Thanks to [Zade Viggers](https://github.com/zadeviggers).
|
|
6
|
+
|
|
7
|
+
## 2.6.1 (2021-12-08)
|
|
8
|
+
|
|
9
|
+
- Fixes style filtering to retain `!important` when used.
|
|
10
|
+
- 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).
|
|
11
|
+
|
|
12
|
+
## 2.6.0 (2021-11-23)
|
|
13
|
+
|
|
14
|
+
- Support for regular expressions in the `allowedClasses` option. Thanks to [Alex Rantos](https://github.com/alex-rantos).
|
|
15
|
+
|
|
16
|
+
## 2.5.3 (2021-11-02):
|
|
17
|
+
|
|
18
|
+
- Fixed bug introduced by klona 2.0.5, by removing klona entirely.
|
|
19
|
+
|
|
3
20
|
## 2.5.2 (2021-10-13):
|
|
4
21
|
|
|
5
22
|
- 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.
|
package/README.md
CHANGED
|
@@ -128,10 +128,9 @@ allowedTags: [
|
|
|
128
128
|
disallowedTagsMode: 'discard',
|
|
129
129
|
allowedAttributes: {
|
|
130
130
|
a: [ 'href', 'name', 'target' ],
|
|
131
|
-
// We don't currently allow img itself by default, but
|
|
132
|
-
// would make sense if we did.
|
|
133
|
-
|
|
134
|
-
img: [ 'src' ]
|
|
131
|
+
// We don't currently allow img itself by default, but
|
|
132
|
+
// these attributes would make sense if we did.
|
|
133
|
+
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
|
|
135
134
|
},
|
|
136
135
|
// Lots of these won't come up by default because we don't allow them
|
|
137
136
|
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
|
|
@@ -239,6 +238,7 @@ const clean = sanitizeHtml(dirty, {
|
|
|
239
238
|
```
|
|
240
239
|
|
|
241
240
|
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:
|
|
241
|
+
|
|
242
242
|
```js
|
|
243
243
|
allowedClasses: {
|
|
244
244
|
'code': [ 'language-*', 'lang-*' ],
|
|
@@ -246,6 +246,16 @@ allowedClasses: {
|
|
|
246
246
|
}
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
+
Furthermore, regular expressions are supported too:
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
allowedClasses: {
|
|
253
|
+
p: [ /^regex\d{2}$/ ]
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
> 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.
|
|
258
|
+
|
|
249
259
|
### Allowed CSS Styles
|
|
250
260
|
|
|
251
261
|
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');
|
|
@@ -157,6 +156,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
157
156
|
}
|
|
158
157
|
const allowedClassesMap = {};
|
|
159
158
|
const allowedClassesGlobMap = {};
|
|
159
|
+
const allowedClassesRegexMap = {};
|
|
160
160
|
each(options.allowedClasses, function(classes, tag) {
|
|
161
161
|
// Implicitly allows the class attribute
|
|
162
162
|
if (allowedAttributesMap) {
|
|
@@ -167,10 +167,13 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
allowedClassesMap[tag] = [];
|
|
170
|
+
allowedClassesRegexMap[tag] = [];
|
|
170
171
|
const globRegex = [];
|
|
171
172
|
classes.forEach(function(obj) {
|
|
172
173
|
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
|
|
173
174
|
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
|
|
175
|
+
} else if (obj instanceof RegExp) {
|
|
176
|
+
allowedClassesRegexMap[tag].push(obj);
|
|
174
177
|
} else {
|
|
175
178
|
allowedClassesMap[tag].push(obj);
|
|
176
179
|
}
|
|
@@ -432,12 +435,16 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
432
435
|
const allowedSpecificClasses = allowedClassesMap[name];
|
|
433
436
|
const allowedWildcardClasses = allowedClassesMap['*'];
|
|
434
437
|
const allowedSpecificClassesGlob = allowedClassesGlobMap[name];
|
|
438
|
+
const allowedSpecificClassesRegex = allowedClassesRegexMap[name];
|
|
435
439
|
const allowedWildcardClassesGlob = allowedClassesGlobMap['*'];
|
|
436
|
-
const allowedClassesGlobs = [
|
|
437
|
-
|
|
440
|
+
const allowedClassesGlobs = [
|
|
441
|
+
allowedSpecificClassesGlob,
|
|
442
|
+
allowedWildcardClassesGlob
|
|
443
|
+
]
|
|
444
|
+
.concat(allowedSpecificClassesRegex)
|
|
445
|
+
.filter(function (t) {
|
|
438
446
|
return t;
|
|
439
|
-
}
|
|
440
|
-
);
|
|
447
|
+
});
|
|
441
448
|
if (allowedSpecificClasses && allowedWildcardClasses) {
|
|
442
449
|
value = filterClasses(value, deepmerge(allowedSpecificClasses, allowedWildcardClasses), allowedClassesGlobs);
|
|
443
450
|
} else {
|
|
@@ -575,6 +582,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
575
582
|
result = tempResult + escapeHtml(result);
|
|
576
583
|
tempResult = '';
|
|
577
584
|
}
|
|
585
|
+
addedText = false;
|
|
578
586
|
}
|
|
579
587
|
}, options.parser);
|
|
580
588
|
parser.write(html);
|
|
@@ -650,18 +658,18 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
650
658
|
|
|
651
659
|
/**
|
|
652
660
|
* Filters user input css properties by allowlisted regex attributes.
|
|
661
|
+
* Modifies the abstractSyntaxTree object.
|
|
653
662
|
*
|
|
654
663
|
* @param {object} abstractSyntaxTree - Object representation of CSS attributes.
|
|
655
664
|
* @property {array[Declaration]} abstractSyntaxTree.nodes[0] - Each object cointains prop and value key, i.e { prop: 'color', value: 'red' }.
|
|
656
665
|
* @param {object} allowedStyles - Keys are properties (i.e color), value is list of permitted regex rules (i.e /green/i).
|
|
657
|
-
* @return {object} -
|
|
666
|
+
* @return {object} - The modified tree.
|
|
658
667
|
*/
|
|
659
668
|
function filterCss(abstractSyntaxTree, allowedStyles) {
|
|
660
669
|
if (!allowedStyles) {
|
|
661
670
|
return abstractSyntaxTree;
|
|
662
671
|
}
|
|
663
672
|
|
|
664
|
-
const filteredAST = klona(abstractSyntaxTree);
|
|
665
673
|
const astRules = abstractSyntaxTree.nodes[0];
|
|
666
674
|
let selectedRule;
|
|
667
675
|
|
|
@@ -676,24 +684,24 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
676
684
|
}
|
|
677
685
|
|
|
678
686
|
if (selectedRule) {
|
|
679
|
-
|
|
687
|
+
abstractSyntaxTree.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
|
|
680
688
|
}
|
|
681
689
|
|
|
682
|
-
return
|
|
690
|
+
return abstractSyntaxTree;
|
|
683
691
|
}
|
|
684
692
|
|
|
685
693
|
/**
|
|
686
|
-
* Extracts the style
|
|
694
|
+
* Extracts the style attributes from an AbstractSyntaxTree and formats those
|
|
687
695
|
* values in the inline style attribute format.
|
|
688
696
|
*
|
|
689
697
|
* @param {AbstractSyntaxTree} filteredAST
|
|
690
|
-
* @return {string} - Example: "color:yellow;text-align:center;font-family:helvetica;"
|
|
698
|
+
* @return {string} - Example: "color:yellow;text-align:center !important;font-family:helvetica;"
|
|
691
699
|
*/
|
|
692
700
|
function stringifyStyleAttributes(filteredAST) {
|
|
693
701
|
return filteredAST.nodes[0].nodes
|
|
694
|
-
.reduce(function(extractedAttributes,
|
|
702
|
+
.reduce(function(extractedAttributes, attrObject) {
|
|
695
703
|
extractedAttributes.push(
|
|
696
|
-
|
|
704
|
+
`${attrObject.prop}:${attrObject.value}${attrObject.important ? ' !important' : ''}`
|
|
697
705
|
);
|
|
698
706
|
return extractedAttributes;
|
|
699
707
|
}, [])
|
|
@@ -772,10 +780,9 @@ sanitizeHtml.defaults = {
|
|
|
772
780
|
disallowedTagsMode: 'discard',
|
|
773
781
|
allowedAttributes: {
|
|
774
782
|
a: [ 'href', 'name', 'target' ],
|
|
775
|
-
// We don't currently allow img itself by default, but
|
|
776
|
-
// would make sense if we did.
|
|
777
|
-
|
|
778
|
-
img: [ 'src' ]
|
|
783
|
+
// We don't currently allow img itself by default, but
|
|
784
|
+
// these attributes would make sense if we did.
|
|
785
|
+
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
|
|
779
786
|
},
|
|
780
787
|
// Lots of these won't come up by default because we don't allow them
|
|
781
788
|
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanitize-html",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
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",
|