sanitize-html 2.17.3 → 2.17.5-alpha.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/README.md +2 -2
- package/index.js +18 -42
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -695,13 +695,13 @@ Normally, with a few exceptions, if a tag is not allowed, all of the text within
|
|
|
695
695
|
|
|
696
696
|
The exceptions are:
|
|
697
697
|
|
|
698
|
-
`style`, `script`, `textarea`, `option`
|
|
698
|
+
`style`, `script`, `textarea`, `option`, `xmp`
|
|
699
699
|
|
|
700
700
|
If you wish to replace this list, for instance to discard whatever is found
|
|
701
701
|
inside a `noscript` tag, use the `nonTextTags` option:
|
|
702
702
|
|
|
703
703
|
```js
|
|
704
|
-
nonTextTags: [ 'style', 'script', 'textarea', 'option', 'noscript' ]
|
|
704
|
+
nonTextTags: [ 'style', 'script', 'textarea', 'option', 'xmp', 'noscript' ]
|
|
705
705
|
```
|
|
706
706
|
|
|
707
707
|
Note that if you use this option you are responsible for stating the entire list. This gives you the power to retain the content of `textarea`, if you want to.
|
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const { isPlainObject } = require('is-plain-object');
|
|
|
4
4
|
const deepmerge = require('deepmerge');
|
|
5
5
|
const parseSrcset = require('parse-srcset');
|
|
6
6
|
const { parse: postcssParse } = require('postcss');
|
|
7
|
+
const { naughtyHref: launderNaughtyHref } = require('launder');
|
|
7
8
|
// Tags that can conceivably represent stand-alone media.
|
|
8
9
|
const mediaTags = [
|
|
9
10
|
'img', 'audio', 'video', 'picture', 'svg',
|
|
@@ -135,11 +136,16 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
135
136
|
// the text when the tag is disallowed makes sense for other reasons.
|
|
136
137
|
// If we are not allowing these tags, we should drop their content too.
|
|
137
138
|
// For other tags you would drop the tag but keep its content.
|
|
139
|
+
// `xmp` is included because htmlparser2 treats it as a raw-text element,
|
|
140
|
+
// so markup inside is parsed as text on input but would otherwise be
|
|
141
|
+
// re-emitted unescaped via the `ontext` branch below, allowing XSS bypass
|
|
142
|
+
// when `xmp` is disallowed (which is the default).
|
|
138
143
|
const nonTextTagsArray = options.nonTextTags || [
|
|
139
144
|
'script',
|
|
140
145
|
'style',
|
|
141
146
|
'textarea',
|
|
142
|
-
'option'
|
|
147
|
+
'option',
|
|
148
|
+
'xmp'
|
|
143
149
|
];
|
|
144
150
|
let allowedAttributesMap;
|
|
145
151
|
let allowedAttributesGlobMap;
|
|
@@ -560,13 +566,13 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
560
566
|
|
|
561
567
|
if (options.disallowedTagsMode === 'completelyDiscard' && !tagAllowed(tag)) {
|
|
562
568
|
text = '';
|
|
563
|
-
} else if ((options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && ((tag === 'script') || (tag === 'style'))) {
|
|
569
|
+
} else if (tag && tagAllowed(tag) && (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && ((tag === 'script') || (tag === 'style'))) {
|
|
564
570
|
// htmlparser2 gives us these as-is. Escaping them ruins the content. Allowing
|
|
565
571
|
// script tags is, by definition, game over for XSS protection, so if that's
|
|
566
572
|
// your concern, don't allow them. The same is essentially true for style tags
|
|
567
573
|
// which have their own collection of XSS vectors.
|
|
568
574
|
result += text;
|
|
569
|
-
} else if ((options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && (tag === 'textarea' || tag === 'xmp')) {
|
|
575
|
+
} else if (tag && tagAllowed(tag) && (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && (tag === 'textarea' || tag === 'xmp')) {
|
|
570
576
|
// htmlparser2 treats <textarea> and <xmp> as raw text elements and
|
|
571
577
|
// does NOT decode entities inside them. The text is already properly
|
|
572
578
|
// encoded, so pass it through without additional escaping to avoid
|
|
@@ -726,45 +732,15 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
726
732
|
}
|
|
727
733
|
|
|
728
734
|
function naughtyHref(name, href) {
|
|
729
|
-
//
|
|
730
|
-
//
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const firstIndex = href.indexOf('<!--');
|
|
739
|
-
if (firstIndex === -1) {
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
742
|
-
const lastIndex = href.indexOf('-->', firstIndex + 4);
|
|
743
|
-
if (lastIndex === -1) {
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
href = href.substring(0, firstIndex) + href.substring(lastIndex + 3);
|
|
747
|
-
}
|
|
748
|
-
// Case insensitive so we don't get faked out by JAVASCRIPT #1
|
|
749
|
-
// Allow more characters after the first so we don't get faked
|
|
750
|
-
// out by certain schemes browsers accept
|
|
751
|
-
const matches = href.match(/^([a-zA-Z][a-zA-Z0-9.\-+]*):/);
|
|
752
|
-
if (!matches) {
|
|
753
|
-
// Protocol-relative URL starting with any combination of '/' and '\'
|
|
754
|
-
if (href.match(/^[/\\]{2}/)) {
|
|
755
|
-
return !options.allowProtocolRelative;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// No scheme
|
|
759
|
-
return false;
|
|
760
|
-
}
|
|
761
|
-
const scheme = matches[1].toLowerCase();
|
|
762
|
-
|
|
763
|
-
if (has(options.allowedSchemesByTag, name)) {
|
|
764
|
-
return options.allowedSchemesByTag[name].indexOf(scheme) === -1;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
return !options.allowedSchemes || options.allowedSchemes.indexOf(scheme) === -1;
|
|
735
|
+
// Resolve the per-tag scheme allowlist if one is configured for
|
|
736
|
+
// this tag, otherwise fall back to the global allowedSchemes.
|
|
737
|
+
const allowedSchemes = has(options.allowedSchemesByTag, name)
|
|
738
|
+
? options.allowedSchemesByTag[name]
|
|
739
|
+
: (options.allowedSchemes || []);
|
|
740
|
+
return launderNaughtyHref(href, {
|
|
741
|
+
allowedSchemes,
|
|
742
|
+
allowProtocolRelative: options.allowProtocolRelative
|
|
743
|
+
});
|
|
768
744
|
}
|
|
769
745
|
|
|
770
746
|
function parseUrl(value) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanitize-html",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.5-alpha.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,7 +27,8 @@
|
|
|
27
27
|
"htmlparser2": "^10.1.0",
|
|
28
28
|
"is-plain-object": "^5.0.0",
|
|
29
29
|
"parse-srcset": "^1.0.2",
|
|
30
|
-
"postcss": "^8.3.11"
|
|
30
|
+
"postcss": "^8.3.11",
|
|
31
|
+
"launder": "^1.7.2-alpha.1"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"eslint": "^9.39.1",
|