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 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 this
132
- // would make sense if we did. You could add srcset here,
133
- // and if you do the URL is checked for safety
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 = [ allowedSpecificClassesGlob, allowedWildcardClassesGlob ].filter(
437
- function(t) {
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} - Abstract Syntax Tree with filtered style attributes.
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
- filteredAST.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
687
+ abstractSyntaxTree.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
680
688
  }
681
689
 
682
- return filteredAST;
690
+ return abstractSyntaxTree;
683
691
  }
684
692
 
685
693
  /**
686
- * Extracts the style attribues from an AbstractSyntaxTree and formats those
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, attributeObject) {
702
+ .reduce(function(extractedAttributes, attrObject) {
695
703
  extractedAttributes.push(
696
- attributeObject.prop + ':' + attributeObject.value
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 this
776
- // would make sense if we did. You could add srcset here,
777
- // and if you do the URL is checked for safety
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.5.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.0.2"
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.21.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",