sanitize-html 2.2.0 → 2.3.3

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.3 (2021-03-19):
4
+ - Security fix: `allowedSchemes` and related options did not properly block schemes containing a hyphen, plus sign, period or digit, such as `ms-calculator:`. Thanks to Lukas Euler for pointing out the issue.
5
+ - Added a security note about the known risks associated with using the `parser` option, especially `decodeEntities: false`. See the documentation.
6
+
7
+ ## 2.3.2 (2021-01-26):
8
+
9
+ - Additional fixes for iframe validation exploits. Prevent exploits based on browsers' tolerance of the use of "\" rather than "/" and the presence of whitespace at this point in the URL. Thanks to Ron Masas of [Checkmarx](https://www.checkmarx.com/) for pointing out the issue and writing unit tests.
10
+ - Updates README `yarn add` syntax. Thanks to [Tagir Khadshiev](https://github.com/Aspedm) for the contribution.
11
+
12
+ ## 2.3.1 (2021-01-22):
13
+ - Uses the standard WHATWG URL parser to stop IDNA (Internationalized Domain Name) attacks on the iframe hostname validator. Thanks to Ron Masas of [Checkmarx](https://www.checkmarx.com/) for pointing out the issue and suggesting the use of the WHATWG parser.
14
+
15
+ ## 2.3.0 (2020-12-16):
16
+ - Upgrades `htmlparser2` to new major version `^6.0.0`. Thanks to [Bogdan Chadkin](https://github.com/TrySound) for the contribution.
17
+
3
18
  ## 2.2.0 (2020-12-02):
4
19
  - Adds a note to the README about Typescript support (or the lack-thereof).
5
20
  - Adds `tel` to the default `allowedSchemes`. Thanks to [Arne Herbots](https://github.com/aHerbots) for this contribution.
package/README.md CHANGED
@@ -24,7 +24,7 @@ HTML comments are not preserved.
24
24
 
25
25
  ## Requirements
26
26
 
27
- sanitize-html is intended for use with Node. That's pretty much it. All of its npm dependencies are pure JavaScript. sanitize-html is built on the excellent `htmlparser2` module.
27
+ sanitize-html is intended for use with Node.js and supports Node 10+. All of its npm dependencies are pure JavaScript. sanitize-html is built on the excellent `htmlparser2` module.
28
28
 
29
29
  ### Regarding Typescript
30
30
 
@@ -42,7 +42,7 @@ But, perhaps you'd like to display sanitized HTML immediately in the browser for
42
42
  * Run npm install and:
43
43
 
44
44
  ```bash
45
- npm install sanitize-html # yarn install sanitize-html
45
+ npm install sanitize-html # yarn add sanitize-html
46
46
  ```
47
47
 
48
48
  The primary change in the 2.x version of sanitize-html is that it no longer includes a build that is ready for browser use. Developers are expected to include sanitize-html in their project builds (e.g., webpack) as they would any other dependency. So while sanitize-html is no longer ready to link to directly in HTML, developers can now more easily process it according to their needs.
@@ -274,7 +274,9 @@ enforceHtmlBoundary: true
274
274
 
275
275
  ### htmlparser2 Options
276
276
 
277
- sanitize-html is built on `htmlparser2`. By default the only option passed down is `decodeEntities: true` You can set the options to pass by using the parser option.
277
+ sanitize-html is built on `htmlparser2`. By default the only option passed down is `decodeEntities: true`. You can set the options to pass by using the parser option.
278
+
279
+ **Security note: changing the `parser` settings can be risky.** In particular, `decodeEntities: false` has known security concerns and a complete test suite does not exist for every possible combination of settings when used with `sanitize-html`. If security is your goal we recommend you use the defaults rather than changing `parser`, except for the `lowerCaseTags` option.
278
280
 
279
281
  ```javascript
280
282
  const clean = sanitizeHtml(dirty, {
package/index.js CHANGED
@@ -5,7 +5,6 @@ const { isPlainObject } = require('is-plain-object');
5
5
  const deepmerge = require('deepmerge');
6
6
  const parseSrcset = require('parse-srcset');
7
7
  const { parse: postcssParse } = require('postcss');
8
- const url = require('url');
9
8
  // Tags that can conceivably represent stand-alone media.
10
9
  const mediaTags = [
11
10
  'img', 'audio', 'video', 'picture', 'svg',
@@ -109,17 +108,9 @@ function sanitizeHtml(html, options, _recursing) {
109
108
  };
110
109
  }
111
110
 
112
- if (!options) {
113
- options = sanitizeHtml.defaults;
114
- options.parser = htmlParserDefaults;
115
- } else {
116
- options = Object.assign({}, sanitizeHtml.defaults, options);
117
- if (options.parser) {
118
- options.parser = Object.assign({}, htmlParserDefaults, options.parser);
119
- } else {
120
- options.parser = htmlParserDefaults;
121
- }
122
- }
111
+ options = Object.assign({}, sanitizeHtml.defaults, options);
112
+ options.parser = Object.assign({}, htmlParserDefaults, options.parser);
113
+
123
114
  // vulnerableTags
124
115
  vulnerableTags.forEach(function (tag) {
125
116
  if (
@@ -313,12 +304,28 @@ function sanitizeHtml(html, options, _recursing) {
313
304
  if (name === 'iframe' && a === 'src') {
314
305
  let allowed = true;
315
306
  try {
307
+ // Chrome accepts \ as a substitute for / in the // at the
308
+ // start of a URL, so rewrite accordingly to prevent exploit.
309
+ // Also drop any whitespace at that point in the URL
310
+ value = value.replace(/^(\w+:)?\s*[\\/]\s*[\\/]/, '$1//');
311
+ if (value.startsWith('relative:')) {
312
+ // An attempt to exploit our workaround for base URLs being
313
+ // mandatory for relative URL validation in the WHATWG
314
+ // URL parser, reject it
315
+ throw new Error('relative: exploit attempt');
316
+ }
316
317
  // naughtyHref is in charge of whether protocol relative URLs
317
- // are cool. We should just accept them
318
- // TODO: Replace deprecated `url.parse`
319
- // eslint-disable-next-line node/no-deprecated-api
320
- parsed = url.parse(value, false, true);
321
- const isRelativeUrl = parsed && parsed.host === null && parsed.protocol === null;
318
+ // are cool. Here we are concerned just with allowed hostnames and
319
+ // whether to allow relative URLs.
320
+ //
321
+ // Build a placeholder "base URL" against which any reasonable
322
+ // relative URL may be parsed successfully
323
+ let base = 'relative://relative-site';
324
+ for (let i = 0; (i < 100); i++) {
325
+ base += `/${i}`;
326
+ }
327
+ const parsed = new URL(value, base);
328
+ const isRelativeUrl = parsed && parsed.hostname === 'relative-site' && parsed.protocol === 'relative:';
322
329
  if (isRelativeUrl) {
323
330
  // default value of allowIframeRelativeUrls is true
324
331
  // unless allowedIframeHostnames or allowedIframeDomains specified
@@ -561,7 +568,9 @@ function sanitizeHtml(html, options, _recursing) {
561
568
  // a javascript: URL to be snuck through
562
569
  href = href.replace(/<!--.*?-->/g, '');
563
570
  // Case insensitive so we don't get faked out by JAVASCRIPT #1
564
- const matches = href.match(/^([a-zA-Z]+):/);
571
+ // Allow more characters after the first so we don't get faked
572
+ // out by certain schemes browsers accept
573
+ const matches = href.match(/^([a-zA-Z][a-zA-Z0-9.\-+]*):/);
565
574
  if (!matches) {
566
575
  // Protocol-relative URL starting with any combination of '/' and '\'
567
576
  if (href.match(/^[/\\]{2}/)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanitize-html",
3
- "version": "2.2.0",
3
+ "version": "2.3.3",
4
4
  "description": "Clean up user-submitted HTML, preserving whitelisted elements and whitelisted attributes on a per-element basis",
5
5
  "sideEffects": false,
6
6
  "main": "index.js",
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "deepmerge": "^4.2.2",
27
27
  "escape-string-regexp": "^4.0.0",
28
- "htmlparser2": "^4.1.0",
28
+ "htmlparser2": "^6.0.0",
29
29
  "is-plain-object": "^5.0.0",
30
30
  "klona": "^2.0.3",
31
31
  "parse-srcset": "^1.0.2",