sanitize-html
Advanced tools
Comparing version 2.0.0-rc.2 to 2.0.0
# Changelog | ||
## 2.0.0 (2020-09-23): | ||
- `nestingLimit` option added. | ||
- Updates ESLint config package and fixes warnings. | ||
- Upgrade `is-plain-object` package with named export. Thanks to [Bogdan Chadkin](https://github.com/TrySound) for the contribution. | ||
- Upgrade `postcss` package and drop Node 11 and Node 13 support (enforced by postcss). | ||
### Backwards compatibility breaks: | ||
- There is no build. You should no longer directly link to a sanitize-html file directly in the browser as it is using modern Javascript that is not fully supported by all major browsers (depending on your definition). You should now include sanitize-html in your project build for this purpose if you have one. | ||
- On the server side, Node.js 10 or higher is required. | ||
- The default `allowedTags` array was updated significantly. This mostly added HTML tags to be more comprehensive by default. You should review your projects and consider the `allowedTags` defaults if you are not already overriding them. | ||
## 2.0.0-rc.2 (2020-09-09): | ||
@@ -9,7 +20,2 @@ - Always use existing `has` function rather than duplicating it. | ||
### Backwards compatibility breaks: | ||
- There is no build. You should no longer directly link to a sanitize-html file directly in the browser as it is using modern Javascript that is not fully supported by all major browsers (depending on your definition). You should now include sanitize-html in your project build for this purpose if you have one. | ||
- On the server side, Node.js 10 or higher is required. | ||
- The default `allowedTags` array was updated significantly. This mostly added HTML tags to be more comprehensive by default. You should review your projects and consider the `allowedTags` defaults if you are not already overriding them. | ||
## 2.0.0-beta.2: | ||
@@ -32,2 +38,15 @@ - Add `files` to `package.json` to prevent publishing unnecessary files to npm #392. Thanks to [styfle](https://github.com/styfle) for the contribution. | ||
## 1.27.5 (2020-09-23): | ||
- Updates README to include ES modules syntax. | ||
## 1.27.4 (2020-08-26): | ||
- Fixes an IE11 regression from using `Array.prototype.includes`, replacing it with `Array.prototype.indexOf`. | ||
## 1.27.3 (2020-08-12): | ||
- Fixes a bug when using `transformTags` with out `textFilter`. Thanks to [Andrzej Porebski](https://github.com/andpor) for the help with a failing test. | ||
## 1.27.2 (2020-07-29): | ||
- Fixes CHANGELOG links. Thanks to [Alex Mayer](https://github.com/amayer5125) for the contribution. | ||
- Replaces `srcset` with `parse-srcset`. Thanks to [Massimiliano Mirra](https://github.com/bard) for the contribution. | ||
## 1.27.1 (2020-07-15): | ||
@@ -128,5 +147,3 @@ - Removes the unused chalk dependency. | ||
* The new `allowedSchemesAppliedToAttributes` option. This determines which attributes are validated as URLs, replacing the old hardcoded list of `src` and `href` only. The default list now includes `cite`. Thanks to ml-dublin for this contribution. | ||
* It is now easy to configure a specific list of allowed values for an attribute. When configuring `allowedAttributes`, rather than listing an attribute name, simply list an object with an attribute `name` property and an allowed `values` array property. You can also add `multiple: true` to allow multiple space-separated allowed values in the attribute, otherwise the attribute must match one and only one of the allowed values. Thanks again to ml-dublin for this contribution. | ||
* Fixed a bug in the npm test procedure. | ||
@@ -133,0 +150,0 @@ |
43
index.js
const htmlparser = require('htmlparser2'); | ||
const escapeStringRegexp = require('escape-string-regexp'); | ||
const { klona } = require('klona'); | ||
const { isPlainObject } = require('is-plain-object'); | ||
const deepmerge = require('deepmerge'); | ||
const isPlainObject = require('is-plain-object'); | ||
const srcset = require('srcset'); | ||
const postcss = require('postcss'); | ||
const parseSrcset = require('parse-srcset'); | ||
const { parse: postcssParse } = require('postcss'); | ||
const url = require('url'); | ||
@@ -15,3 +15,3 @@ // Tags that can conceivably represent stand-alone media. | ||
// Tags that are inherently vulnerable to being used in XSS attacks. | ||
const vulnerableTags = ['script', 'style']; | ||
const vulnerableTags = [ 'script', 'style' ]; | ||
@@ -51,2 +51,17 @@ function each(obj, cb) { | ||
function stringifySrcset(parsedSrcset) { | ||
return parsedSrcset.map(function(part) { | ||
if (!part.url) { | ||
throw new Error('URL missing'); | ||
} | ||
return ( | ||
part.url + | ||
(part.w ? ` ${part.w}w` : '') + | ||
(part.h ? ` ${part.h}h` : '') + | ||
(part.d ? ` ${part.d}x` : '') | ||
); | ||
}).join(', '); | ||
} | ||
module.exports = sanitizeHtml; | ||
@@ -113,3 +128,3 @@ | ||
if ( | ||
options.allowedTags && options.allowedTags.includes(tag) && | ||
options.allowedTags && options.allowedTags.indexOf(tag) > -1 && | ||
!options.allowVulnerableTags | ||
@@ -230,3 +245,3 @@ ) { | ||
if ((options.allowedTags && options.allowedTags.indexOf(name) === -1) || (options.disallowedTagsMode === 'recursiveEscape' && !isEmptyObject(skipMap))) { | ||
if ((options.allowedTags && options.allowedTags.indexOf(name) === -1) || (options.disallowedTagsMode === 'recursiveEscape' && !isEmptyObject(skipMap)) || (options.nestingLimit != null && depth >= options.nestingLimit)) { | ||
skip = true; | ||
@@ -337,3 +352,3 @@ skipMap[depth] = true; | ||
try { | ||
parsed = srcset.parse(value); | ||
parsed = parseSrcset(value); | ||
parsed.forEach(function(value) { | ||
@@ -351,3 +366,3 @@ if (naughtyHref('srcset', value.url)) { | ||
} else { | ||
value = srcset.stringify(filter(parsed, function(v) { | ||
value = stringifySrcset(filter(parsed, function(v) { | ||
return !v.evil; | ||
@@ -372,3 +387,3 @@ })); | ||
try { | ||
const abstractSyntaxTree = postcss.parse(name + ' {' + value + '}'); | ||
const abstractSyntaxTree = postcssParse(name + ' {' + value + '}'); | ||
const filteredAST = filterCss(abstractSyntaxTree, options.allowedStyles); | ||
@@ -690,14 +705,14 @@ | ||
allowedAttributes: { | ||
a: ['href', 'name', 'target'], | ||
a: [ 'href', 'name', 'target' ], | ||
// We don't currently allow img itself by default, but this | ||
// would make sense if we did. You could add srcset here, | ||
// and if you do the URL is checked for safety | ||
img: ['src'] | ||
img: [ 'src' ] | ||
}, | ||
// Lots of these won't come up by default because we don't allow them | ||
selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'], | ||
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ], | ||
// URL schemes we permit | ||
allowedSchemes: ['http', 'https', 'ftp', 'mailto'], | ||
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ], | ||
allowedSchemesByTag: {}, | ||
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'], | ||
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ], | ||
allowProtocolRelative: true, | ||
@@ -704,0 +719,0 @@ enforceHtmlBoundary: false |
{ | ||
"name": "sanitize-html", | ||
"version": "2.0.0-rc.2", | ||
"version": "2.0.0", | ||
"description": "Clean up user-submitted HTML, preserving whitelisted elements and whitelisted attributes on a per-element basis", | ||
@@ -28,10 +28,10 @@ "sideEffects": false, | ||
"htmlparser2": "^4.1.0", | ||
"is-plain-object": "^4.1.1", | ||
"is-plain-object": "^5.0.0", | ||
"klona": "^2.0.3", | ||
"postcss": "^7.0.27", | ||
"srcset": "^3.0.0" | ||
"parse-srcset": "^1.0.2", | ||
"postcss": "^8.0.2" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^7.3.1", | ||
"eslint-config-apostrophe": "^3.2.0", | ||
"eslint-config-apostrophe": "^3.4.0", | ||
"eslint-config-standard": "^14.1.1", | ||
@@ -38,0 +38,0 @@ "eslint-plugin-import": "^2.21.2", |
@@ -5,3 +5,3 @@ # sanitize-html | ||
<a href="https://apostrophecms.com/"><img src="https://raw.github.com/apostrophecms/sanitize-html/master/logos/logo-box-madefor.png" align="right" /></a> | ||
<a href="https://apostrophecms.com/"><img src="https://raw.githubusercontent.com/apostrophecms/sanitize-html/main/logos/logo-box-madefor.png" align="right" /></a> | ||
@@ -67,7 +67,15 @@ `sanitize-html` provides a simple HTML sanitizer with a clear API. | ||
Use it in your node app: | ||
Import the module: | ||
```js | ||
```bash | ||
// In ES modules | ||
import sanitizeHtml from 'sanitize-html'; | ||
// Or in CommonJS | ||
const sanitizeHtml = require('sanitize-html'); | ||
``` | ||
Use it in your JavaScript app: | ||
```js | ||
const dirty = 'some really tacky HTML'; | ||
@@ -168,2 +176,3 @@ const clean = sanitizeHtml(dirty); | ||
] | ||
} | ||
``` | ||
@@ -177,3 +186,3 @@ | ||
```javascript | ||
```js | ||
allowedAttributes: { | ||
@@ -186,3 +195,3 @@ a: [ 'href', 'data-*' ] | ||
```javascript | ||
```js | ||
allowedAttributes: { | ||
@@ -248,3 +257,3 @@ '*': [ 'href', 'align', 'alt', 'center', 'bgcolor' ] | ||
```javascript | ||
```js | ||
enforceHtmlBoundary: true | ||
@@ -287,9 +296,9 @@ ``` | ||
'ol': function(tagName, attribs) { | ||
// My own custom magic goes here | ||
return { | ||
tagName: 'ul', | ||
attribs: { | ||
class: 'foo' | ||
} | ||
}; | ||
// My own custom magic goes here | ||
return { | ||
tagName: 'ul', | ||
attribs: { | ||
class: 'foo' | ||
} | ||
}; | ||
} | ||
@@ -326,6 +335,6 @@ } | ||
'a': function(tagName, attribs) { | ||
return { | ||
tagName: 'a', | ||
text: 'Some text' | ||
}; | ||
return { | ||
tagName: 'a', | ||
text: 'Some text' | ||
}; | ||
} | ||
@@ -354,3 +363,3 @@ } | ||
```javascript | ||
```js | ||
sanitizeHtml( | ||
@@ -360,3 +369,3 @@ '<p>This is <a href="http://www.linux.org"></a><br/>Linux</p>', | ||
exclusiveFilter: function(frame) { | ||
return frame.tag === 'a' && !frame.text.trim(); | ||
return frame.tag === 'a' && !frame.text.trim(); | ||
} | ||
@@ -383,3 +392,3 @@ } | ||
```javascript | ||
```js | ||
sanitizeHtml( | ||
@@ -407,5 +416,5 @@ '<p>some text...</p>', | ||
```javascript | ||
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'], | ||
allowedIframeDomains: ['zoom.us'] | ||
```js | ||
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'], | ||
allowedIframeDomains: ['zoom.us'] | ||
``` | ||
@@ -415,4 +424,4 @@ | ||
```javascript | ||
allowIframeRelativeUrls: true | ||
```js | ||
allowIframeRelativeUrls: true | ||
``` | ||
@@ -498,3 +507,3 @@ | ||
```javascript | ||
```js | ||
sanitizeHtml( | ||
@@ -512,3 +521,3 @@ // teeny-tiny valid transparent GIF in a data URL | ||
```javascript | ||
```js | ||
allowedSchemes: [ 'http', 'https' ], | ||
@@ -522,3 +531,3 @@ allowedSchemesByTag: { | ||
```javascript | ||
```js | ||
allowProtocolRelative: false | ||
@@ -538,3 +547,3 @@ ``` | ||
```javascript | ||
```js | ||
nonTextTags: [ 'style', 'script', 'textarea', 'option', 'noscript' ] | ||
@@ -555,3 +564,3 @@ ``` | ||
```javascript | ||
```js | ||
disallowedTagsMode: 'escape' | ||
@@ -564,2 +573,12 @@ ``` | ||
### Restricting deep nesting | ||
You can limit the depth of HTML tags in the document with the `nestingLimit` option: | ||
```javascript | ||
nestingLimit: 6 | ||
``` | ||
This will prevent the user from nesting tags more than 6 levels deep. Tags deeper than that are stripped out exactly as if they were disallowed. Note that this means text is preserved in the usual ways where appropriate. | ||
## About ApostropheCMS | ||
@@ -566,0 +585,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
65066
674
1
572
+ Addedparse-srcset@^1.0.2
+ Addedis-plain-object@5.0.0(transitive)
+ Addednanoid@3.3.7(transitive)
+ Addedparse-srcset@1.0.2(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedpostcss@8.4.47(transitive)
+ Addedsource-map-js@1.2.1(transitive)
- Removedsrcset@^3.0.0
- Removedis-plain-object@4.1.1(transitive)
- Removedpicocolors@0.2.1(transitive)
- Removedpostcss@7.0.39(transitive)
- Removedsource-map@0.6.1(transitive)
- Removedsrcset@3.0.1(transitive)
Updatedis-plain-object@^5.0.0
Updatedpostcss@^8.0.2