Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

sanitize-html

Package Overview
Dependencies
Maintainers
10
Versions
114
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sanitize-html - npm Package Compare versions

Comparing version 1.1.8 to 1.2.0

82

index.js

@@ -9,3 +9,19 @@ var htmlparser = require('htmlparser2');

var result = '';
if (!options) {
function Frame(tag) {
var that = this;
this.tag = tag;
this.tagPosition = result.length;
this.text = ''; // Node inner text
this.updateParentNodeText = function() {
if (stack.length) {
var parentFrame = stack[stack.length - 1];
parentFrame.text += that.text;
}
};
}
if (!options) {
options = sanitizeHtml.defaults;

@@ -37,2 +53,16 @@ } else {

});
var allowedClassesMap = {};
_.each(options.allowedClasses, function(classes, tag) {
// Implicitly allows the class attribute
if (!allowedAttributesMap[tag]) {
allowedAttributesMap[tag] = {};
}
allowedAttributesMap[tag]['class'] = true;
allowedClassesMap[tag] = {};
_.each(classes, function(name) {
allowedClassesMap[tag][name] = true;
});
});
var transformTagsMap = {};

@@ -54,8 +84,4 @@ _.each(options.transformTags, function(transform, tag){

onopentag: function(name, attribs) {
stack.push({
tag: name,
attribs: attribs,
text: '',
tagPosition: result.length
});
var frame = new Frame(name);
stack.push(frame);

@@ -89,3 +115,2 @@ var skip = false;

if (_.has(allowedAttributesMap[name], a)) {
result += ' ' + a;
if ((a === 'href') || (a === 'src')) {

@@ -96,2 +121,9 @@ if (naughtyHref(value)) {

}
if (a === 'class') {
value = filterClasses(value, allowedClassesMap[name]);
if (!value.length) {
return;
}
}
result += ' ' + a;
if (value.length) {

@@ -115,9 +147,9 @@ // Values are ALREADY escaped, calling escapeHtml here

}
if (depth) {
var frame = stack[depth - 1];
frame.text += text;
}
// It is NOT actually raw text, entities are already escaped.
// If we call escapeHtml here we wind up double-escaping.
result += text;
if (stack.length) {
var frame = stack[stack.length - 1];
frame.text += text;
}
},

@@ -130,8 +162,6 @@ onclosetag: function(name) {

delete skipMap[depth];
frame.updateParentNodeText();
return;
}
if (_.has(selfClosingMap, name)) {
// Already output />
return;
}
if (transformMap[depth]) {

@@ -141,2 +171,3 @@ name = transformMap[depth];

}
if (options.exclusiveFilter && options.exclusiveFilter(frame)) {

@@ -146,2 +177,10 @@ result = result.substr(0, frame.tagPosition);

}
frame.updateParentNodeText();
if (_.has(selfClosingMap, name)) {
// Already output />
return;
}
result += "</" + name + ">";

@@ -180,2 +219,13 @@ }

}
function filterClasses(classes, allowed) {
if (!allowed) {
// The class attribute is allowed without filtering on this tag
return classes;
}
classes = classes.split(/\s+/);
return _.filter(classes, function(c) {
return _.has(allowed, c);
}).join(' ');
}
}

@@ -182,0 +232,0 @@

2

package.json
{
"name": "sanitize-html",
"version": "1.1.8",
"version": "1.2.0",
"description": "Clean up user-submitted HTML, preserving whitelisted elements and whitelisted attributes on a per-element basis",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -130,2 +130,18 @@ # sanitize-html

### Allowed CSS Classes
If you wish to allow specific CSS classes on a particular element, you can do so with the `allowedClasses` option. Any other CSS classes are discarded.
This implies that the `class` attribute is allowed on that element.
```javascript
// Allow only a restricted set of CSS classes and only on the p tag
clean = sanitizeHtml(dirty, {
allowedTags: [ 'p', 'em', 'strong' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ]
}
});
```
### Allowed URL schemes

@@ -152,2 +168,10 @@

1.2.0:
* The `allowedClasses` option now allows you to permit CSS classes in a fine-grained way.
* Text passed to your `exclusiveFilter` function now includes the text of child elements, making it more useful for identifying elements that truly lack any inner text.
1.1.7: use `he` for entity decoding, because it is more actively maintained.
1.1.6: `allowedSchemes` option for those who want to permit `data` URLs and such.

@@ -154,0 +178,0 @@

@@ -29,6 +29,6 @@ var assert = require("assert");

it('should reject hrefs that are not relative, ftp, http, https or mailto', function() {
assert.equal(sanitizeHtml('<a href="http://google.com">google</a><a href="https://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href="javascript:alert(0)">javascript</a>'), '<a href="http://google.com">google</a><a href="https://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href>javascript</a>');
assert.equal(sanitizeHtml('<a href="http://google.com">google</a><a href="https://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href="javascript:alert(0)">javascript</a>'), '<a href="http://google.com">google</a><a href="https://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a>javascript</a>');
});
it('should cope identically with capitalized attributes and tags and should tolerate capitalized schemes', function() {
assert.equal(sanitizeHtml('<A HREF="http://google.com">google</a><a href="HTTPS://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href="javascript:alert(0)">javascript</a>'), '<a href="http://google.com">google</a><a href="HTTPS://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href>javascript</a>');
assert.equal(sanitizeHtml('<A HREF="http://google.com">google</a><a href="HTTPS://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a href="javascript:alert(0)">javascript</a>'), '<a href="http://google.com">google</a><a href="HTTPS://google.com">https google</a><a href="ftp://example.com">ftp</a><a href="mailto:test@test.com">mailto</a><a href="/relative.html">relative</a><a>javascript</a>');
});

@@ -48,12 +48,12 @@ it('should drop the content of script elements', function() {

it('should dump a sneaky encoded javascript url', function() {
assert.equal(sanitizeHtml('<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;">Hax</a>'), '<a href>Hax</a>');
assert.equal(sanitizeHtml('<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;">Hax</a>'), '<a>Hax</a>');
});
it('should dump an uppercase javascript url', function() {
assert.equal(sanitizeHtml('<a href="JAVASCRIPT:alert(\'foo\')">Hax</a>'), '<a href>Hax</a>');
assert.equal(sanitizeHtml('<a href="JAVASCRIPT:alert(\'foo\')">Hax</a>'), '<a>Hax</a>');
});
it('should dump character codes 1-32 before testing scheme', function() {
assert.equal(sanitizeHtml('<a href="java\0&#14;\t\r\n script:alert(\'foo\')">Hax</a>'), '<a href>Hax</a>');
assert.equal(sanitizeHtml('<a href="java\0&#14;\t\r\n script:alert(\'foo\')">Hax</a>'), '<a>Hax</a>');
});
it('should dump character codes 1-32 even when escaped with padding rather than trailing ;', function() {
assert.equal(sanitizeHtml('<a href="java&#0000000script:alert(\'foo\')">Hax</a>'), '<a href>Hax</a>');
assert.equal(sanitizeHtml('<a href="java&#0000000script:alert(\'foo\')">Hax</a>'), '<a>Hax</a>');
});

@@ -88,13 +88,56 @@ it('should still like nice schemes', function() {

});
it('should skip empty a', function() {
assert.equal(
sanitizeHtml('<p>This is <a href="http://www.linux.org"></a><br/>Linux</p>',
{
exclusiveFilter : function(frame) {
return frame.tag === 'a' && !frame.text.trim();
}
}),
'<p>This is <br />Linux</p>'
);
it('should skip an empty link', function() {
assert.strictEqual(
sanitizeHtml('<p>This is <a href="http://www.linux.org"></a><br/>Linux</p>', {
exclusiveFilter: function (frame) {
return frame.tag === 'a' && !frame.text.trim();
}
}),
'<p>This is <br />Linux</p>'
);
});
it("Should expose a node's inner text and inner HTML to the filter", function() {
assert.strictEqual(
sanitizeHtml('<p>12<a href="http://www.linux.org"><br/>3<br></a><span>4</span></p>', {
exclusiveFilter : function(frame) {
if (frame.tag === 'p') {
assert.strictEqual(frame.text, '124');
} else if (frame.tag === 'a') {
assert.strictEqual(frame.text, '3');
return true;
} else if (frame.tag === 'br') {
assert.strictEqual(frame.text, '');
} else {
assert.fail('p, a, br', frame.tag);
}
return false;
}
}),
'<p>124</p>'
);
});
it('Should collapse nested empty elements', function() {
assert.strictEqual(
sanitizeHtml('<p><a href="http://www.linux.org"><br/></a></p>', {
exclusiveFilter : function(frame) {
return (frame.tag === 'a' || frame.tag === 'p' ) && !frame.text.trim();
}
}),
''
);
});
it('Exclusive filter should not affect elements which do not match the filter condition', function () {
assert.strictEqual(
sanitizeHtml('I love <a href="www.linux.org" target="_hplink">Linux</a> OS',
{
exclusiveFilter: function (frame) {
return (frame.tag === 'a') && !frame.text.trim();
}
}),
'I love <a href="www.linux.org" target="_hplink">Linux</a> OS'
);
});
it('should disallow data URLs with default allowedSchemes', function() {

@@ -109,3 +152,3 @@ assert.equal(

),
'<img src />'
'<img />'
);

@@ -126,3 +169,31 @@ });

});
it('should allow specific classes when whitelisted with allowedClasses', function() {
assert.equal(
sanitizeHtml(
'<p class="nifty simple dippy">whee</p>',
{
allowedTags: [ 'p' ],
allowedClasses: {
p: [ 'nifty' ]
}
}
),
'<p class="nifty">whee</p>'
);
});
it('should not act weird when the class attribute is empty', function() {
assert.equal(
sanitizeHtml(
'<p class="">whee</p>',
{
allowedTags: [ 'p' ],
allowedClasses: {
p: [ 'nifty' ]
}
}
),
'<p>whee</p>'
);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc