sanitize-html 2.13.1 → 2.15.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/README.md +18 -3
- package/index.js +30 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -494,6 +494,21 @@ sanitizeHtml(
|
|
|
494
494
|
);
|
|
495
495
|
```
|
|
496
496
|
|
|
497
|
+
The filter function can also return the string `"excludeTag"` to only remove the tag, while keeping its content. For example, you can remove tags for anchors with invalid links:
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
sanitizeHtml(
|
|
501
|
+
'This is a <a href="javascript:alert(123)">bad link</a> and a <a href="https://www.linux.org">good link</a>',
|
|
502
|
+
{
|
|
503
|
+
exclusiveFilter: function(frame) {
|
|
504
|
+
// the href attribute is removed by the URL protocol check
|
|
505
|
+
return frame.tag === 'a' && !frame.attribs.href ? 'excludeTag' : false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
// Output: 'This is a bad link and a <a href="https://www.linux.org">good link</a>'
|
|
510
|
+
```
|
|
511
|
+
|
|
497
512
|
The `frame` object supplied to the callback provides the following attributes:
|
|
498
513
|
|
|
499
514
|
- `tag`: The tag name, i.e. `'img'`.
|
|
@@ -709,9 +724,9 @@ This will transform `<disallowed>content</disallowed>` to `<disallowed>con
|
|
|
709
724
|
|
|
710
725
|
Valid values are: `'discard'` (default), `'completelyDiscard'` (remove disallowed tag's content), `'escape'` (escape the tag) and `'recursiveEscape'` (to escape the tag and all its content).
|
|
711
726
|
|
|
712
|
-
#### Discard disallowed but
|
|
727
|
+
#### Discard disallowed but the inner content of disallowed tags is kept.
|
|
713
728
|
|
|
714
|
-
If you set `disallowedTagsMode` to `discard`, disallowed tags are discarded but
|
|
729
|
+
If you set `disallowedTagsMode` to `discard`, disallowed tags are discarded but the inner content of disallowed tags is kept.
|
|
715
730
|
|
|
716
731
|
```js
|
|
717
732
|
disallowedTagsMode: 'discard'
|
|
@@ -720,7 +735,7 @@ This will transform `<disallowed>content</disallowed>` to `content`
|
|
|
720
735
|
|
|
721
736
|
#### Discard entire content of a disallowed tag
|
|
722
737
|
|
|
723
|
-
If you set `disallowedTagsMode` to `completelyDiscard`, disallowed tags and any
|
|
738
|
+
If you set `disallowedTagsMode` to `completelyDiscard`, disallowed tags and any text they contain are discarded. This also discards top-level text. Any subtags are still included, as long as those individual subtags are allowed.
|
|
724
739
|
|
|
725
740
|
```js
|
|
726
741
|
disallowedTagsMode: 'completelyDiscard'
|
package/index.js
CHANGED
|
@@ -97,6 +97,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
97
97
|
this.attribs = attribs || {};
|
|
98
98
|
this.tagPosition = result.length;
|
|
99
99
|
this.text = ''; // Node inner text
|
|
100
|
+
this.openingTagLength = 0;
|
|
100
101
|
this.mediaChildren = [];
|
|
101
102
|
|
|
102
103
|
this.updateParentNodeText = function() {
|
|
@@ -268,12 +269,20 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
268
269
|
skipTextDepth = 1;
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
|
-
skipMap[depth] = true;
|
|
272
272
|
}
|
|
273
273
|
depth++;
|
|
274
274
|
if (skip) {
|
|
275
275
|
if (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') {
|
|
276
276
|
// We want the contents but not this tag
|
|
277
|
+
if (frame.innerText && !hasText) {
|
|
278
|
+
const escaped = escapeHtml(frame.innerText);
|
|
279
|
+
if (options.textFilter) {
|
|
280
|
+
result += options.textFilter(escaped, name);
|
|
281
|
+
} else {
|
|
282
|
+
result += escaped;
|
|
283
|
+
}
|
|
284
|
+
addedText = true;
|
|
285
|
+
}
|
|
277
286
|
return;
|
|
278
287
|
}
|
|
279
288
|
tempResult = result;
|
|
@@ -498,6 +507,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
498
507
|
result = tempResult + escapeHtml(result);
|
|
499
508
|
tempResult = '';
|
|
500
509
|
}
|
|
510
|
+
frame.openingTagLength = result.length - frame.tagPosition;
|
|
501
511
|
},
|
|
502
512
|
ontext: function(text) {
|
|
503
513
|
if (skipText) {
|
|
@@ -520,11 +530,11 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
520
530
|
// your concern, don't allow them. The same is essentially true for style tags
|
|
521
531
|
// which have their own collection of XSS vectors.
|
|
522
532
|
result += text;
|
|
523
|
-
} else {
|
|
533
|
+
} else if (!addedText) {
|
|
524
534
|
const escaped = escapeHtml(text, false);
|
|
525
|
-
if (options.textFilter
|
|
535
|
+
if (options.textFilter) {
|
|
526
536
|
result += options.textFilter(escaped, tag);
|
|
527
|
-
} else
|
|
537
|
+
} else {
|
|
528
538
|
result += escaped;
|
|
529
539
|
}
|
|
530
540
|
}
|
|
@@ -575,9 +585,21 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
575
585
|
delete transformMap[depth];
|
|
576
586
|
}
|
|
577
587
|
|
|
578
|
-
if (options.exclusiveFilter
|
|
579
|
-
|
|
580
|
-
|
|
588
|
+
if (options.exclusiveFilter) {
|
|
589
|
+
const filterResult = options.exclusiveFilter(frame);
|
|
590
|
+
if (filterResult === 'excludeTag') {
|
|
591
|
+
if (skip) {
|
|
592
|
+
// no longer escaping the tag since it's not added at all
|
|
593
|
+
result = tempResult;
|
|
594
|
+
tempResult = '';
|
|
595
|
+
}
|
|
596
|
+
// remove the opening tag from the result
|
|
597
|
+
result = result.substring(0, frame.tagPosition) + result.substring(frame.tagPosition + frame.openingTagLength);
|
|
598
|
+
return;
|
|
599
|
+
} else if (filterResult) {
|
|
600
|
+
result = result.substring(0, frame.tagPosition);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
581
603
|
}
|
|
582
604
|
|
|
583
605
|
frame.updateParentNodeMediaChildren();
|
|
@@ -823,7 +845,7 @@ sanitizeHtml.defaults = {
|
|
|
823
845
|
'main', 'nav', 'section',
|
|
824
846
|
// Text content
|
|
825
847
|
'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure',
|
|
826
|
-
'hr', 'li', '
|
|
848
|
+
'hr', 'li', 'menu', 'ol', 'p', 'pre', 'ul',
|
|
827
849
|
// Inline text semantics
|
|
828
850
|
'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn',
|
|
829
851
|
'em', 'i', 'kbd', 'mark', 'q',
|
package/package.json
CHANGED