resanitize
Advanced tools
Comparing version 0.1.11 to 0.2.0
@@ -1,18 +0,41 @@ | ||
{ "name" : "resanitize" | ||
, "author" : "Dan MacTough <danmactough@gmail.com>" | ||
, "description" : "Regular expression-based HTML sanitizer and ad remover, geared toward RSS feed descriptions" | ||
, "version" : "0.1.11" | ||
, "keywords" : ["sanitize", "html", "regexp", "security"] | ||
, "homepage" : "http://github.com/danmactough/node-resanitize" | ||
, "repository" : | ||
{ "type" : "git" | ||
, "url" : "git://github.com/danmactough/node-resanitize.git" | ||
} | ||
, "bugs" : | ||
{ "url" : "http://github.com/danmactough/node-resanitize/issues"} | ||
, "main" : "./resanitize.js" | ||
, "engines" : | ||
{ "node" : ">= 0.6.0" } | ||
, "dependencies" : {} | ||
, "devDependencies": {} | ||
{ | ||
"name": "resanitize", | ||
"author": "Dan MacTough <danmactough@gmail.com>", | ||
"description": "Regular expression-based HTML sanitizer and ad remover, geared toward RSS feed descriptions", | ||
"version": "0.2.0", | ||
"keywords": [ | ||
"sanitize", | ||
"html", | ||
"regexp", | ||
"security" | ||
], | ||
"homepage": "http://github.com/danmactough/node-resanitize", | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/danmactough/node-resanitize.git" | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/danmactough/node-resanitize/issues" | ||
}, | ||
"main": "./resanitize.js", | ||
"engines": { | ||
"node": ">= 0.6.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"mocha": "~1.13.0" | ||
}, | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://raw.github.com/danmactough/node-resanitize/master/LICENSE" | ||
} | ||
], | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"scripts": { | ||
"test": "mocha" | ||
}, | ||
"license": "MIT" | ||
} |
@@ -19,2 +19,15 @@ # Resanitize - Regular expression-based HTML sanitizer and ad remover, geared toward RSS feed descriptions | ||
resanitize(html); // => '<div>Headline</div>' | ||
``` | ||
``` | ||
## Notes | ||
This module's opinion of "sanitized" might not meet your security requirements. | ||
The mere fact that it uses regular expressions should make this disclaimer | ||
unnecessary, but just to be clear: if you intend to display arbitrary user input | ||
that includes HTML, you're going to want something more robust. | ||
Note that the `stripUnsafeTags` method will loop over the strip an arbitrary | ||
number of times (10) to try to strip maliciously nested html tags. After the | ||
maximum number of iterations is reached, if the string still appears to contain | ||
any unsafe tags, it is deemed unsafe and set to an empty string. If this seems | ||
unexpected and/or is causing any problems, please raise an [issue](//github.com/danmactough/node-resanitize/issues). |
@@ -87,3 +87,3 @@ /*! | ||
function stripComments (str) { | ||
return str.replace(/<!--[^>]*?>.*?<![^>]*?-->/g, ''); | ||
return str.replace(/<!--[^>]*?-->/g, ''); | ||
} | ||
@@ -96,3 +96,4 @@ module.exports.stripComments = stripComments; | ||
function filterAttrs () { | ||
var allowed = []; | ||
var allowed = [] | ||
, script = /javascript:/i; | ||
if (Array.isArray(arguments[0])) { | ||
@@ -104,3 +105,3 @@ allowed = arguments[0]; | ||
return function (attr, name) { | ||
if ( ~allowed.indexOf(name && name.toLowerCase()) ) { | ||
if ( ~allowed.indexOf(name && name.toLowerCase()) && !script.test(attr) ) { | ||
return attr; | ||
@@ -118,10 +119,26 @@ } else { | ||
function stripAttrs () { | ||
var banned = []; | ||
var banned = [] | ||
, regexes = [] | ||
, script = /javascript:/i; | ||
if (Array.isArray(arguments[0])) { | ||
banned = arguments[0]; | ||
banned = arguments[0].filter(function (attr) { | ||
if ('string' === typeof attr) { | ||
return true; | ||
} | ||
else if (attr.constructor && 'RegExp' === attr.constructor.name) { | ||
regexes.push(attr); | ||
} | ||
}); | ||
} else { | ||
banned = Array.prototype.slice.call(arguments); | ||
banned = Array.prototype.slice.call(arguments).filter(function (attr) { | ||
if ('string' === typeof attr) { | ||
return true; | ||
} | ||
else if (attr.constructor && 'RegExp' === attr.constructor.name) { | ||
regexes.push(attr); | ||
} | ||
}); | ||
} | ||
return function (attr, name) { | ||
if ( ~banned.indexOf(name && name.toLowerCase()) ) { | ||
if ( ~banned.indexOf(name && name.toLowerCase()) || script.test(attr) || regexes.some(function (re) { return re.test(name); }) ) { | ||
return ''; | ||
@@ -141,3 +158,3 @@ } else { | ||
if ('function' === typeof nextFilter) { | ||
rematch = rematch.replace(/(\S+)=("|')[^>]+?\2/g, nextFilter); | ||
rematch = rematch.replace(/([^\s"']+?)=("|')[^>]+?\2/g, nextFilter); | ||
} | ||
@@ -160,26 +177,16 @@ // Cleanup extra whitespace | ||
, 'style' | ||
, 'accesskey' | ||
, 'action' | ||
, 'autocomplete' | ||
, 'autofocus' | ||
, 'clear' | ||
, 'contextmenu' | ||
, 'contenteditable' | ||
, 'draggable' | ||
, 'dropzone' | ||
, 'method' | ||
, 'tabindex' | ||
, 'target' | ||
, 'onclick' | ||
, 'ondblclick' | ||
, 'onmousedown' | ||
, 'onmousemove' | ||
, 'onmouseover' | ||
, 'onmouseout' | ||
, 'onmouseup' | ||
, 'onkeydown' | ||
, 'onkeypress' | ||
, 'onkeyup' | ||
, 'onabort' | ||
, 'onerror' | ||
, 'onload' | ||
, 'onresize' | ||
, 'onscroll' | ||
, 'onunload' | ||
, 'onblur' | ||
, 'onchange' | ||
, 'onfocus' | ||
, 'onreset' | ||
, 'onselect' | ||
, 'onsubmit' | ||
, /on\w+/i | ||
, /data-\w+/i | ||
]; | ||
@@ -191,15 +198,26 @@ return str.replace(/<([^ >]+?) [^>]*?>/g, filterTag(stripAttrs(unsafe))); | ||
function stripUnsafeTags (str) { | ||
return str.replace(/<form[^>]*?>[\s\S]*?<\/form>/gi, '') | ||
.replace(/<input[^>]*?>[\s\S]*?<\/input>/gi, '') | ||
.replace(/<\/?(?:form|input|font|blink)[^>]*?>/gi, '') | ||
// These are XSS/security risks | ||
.replace(/<script[^>]*?>[\s\S]*?<\/script>/gi, '') | ||
.replace(/<style[^>]*?>[\s\S]*?<\/style>/gi, '') // shouldn't work anyway... | ||
.replace(/<comment[^>]*?>[\s\S]*?<\/comment>/gi, '') | ||
.replace(/<plaintext[^>]*?>[\s\S]*?<\/plaintext>/gi, '') | ||
.replace(/<xmp[^>]*?>[\s\S]*?<\/xmp>/gi, '') | ||
.replace(/<\/?(?:link|listing|meta|body|frame|frameset)[^>]*?>/gi, '') | ||
// Delete iframes, except those inserted by Google in lieu of video embeds | ||
.replace(/<iframe(?![^>]*?src=("|')\S+?reader.googleusercontent.com\/reader\/embediframe.+?\1)[^>]*?>[\s\S]*?<\/iframe>/gi, '') | ||
; | ||
var el = /<(?:form|input|font|blink|script|style|comment|plaintext|xmp|link|listing|meta|body|frame|frameset)\b/; | ||
var ct = 0, max = 10; | ||
// We'll repeatedly try to strip any maliciously nested elements up to [max] times | ||
while (el.test(str) && ct++ < max) { | ||
str = str.replace(/<form[^>]*?>[\s\S]*?<\/form>/gi, '') | ||
.replace(/<input[^>]*?>[\s\S]*?<\/input>/gi, '') | ||
.replace(/<\/?(?:form|input|font|blink)[^>]*?>/gi, '') | ||
// These are XSS/security risks | ||
.replace(/<script[^>]*?>[\s\S]*?<\/script>/gi, '') | ||
.replace(/<style[^>]*?>[\s\S]*?<\/style>/gi, '') // shouldn't work anyway... | ||
.replace(/<comment[^>]*?>[\s\S]*?<\/comment>/gi, '') | ||
.replace(/<plaintext[^>]*?>[\s\S]*?<\/plaintext>/gi, '') | ||
.replace(/<xmp[^>]*?>[\s\S]*?<\/xmp>/gi, '') | ||
.replace(/<\/?(?:link|listing|meta|body|frame|frameset)[^>]*?>/gi, '') | ||
// Delete iframes, except those inserted by Google in lieu of video embeds | ||
.replace(/<iframe(?![^>]*?src=("|')\S+?reader.googleusercontent.com\/reader\/embediframe.+?\1)[^>]*?>[\s\S]*?<\/iframe>/gi, '') | ||
; | ||
} | ||
if (el.test(str)) { | ||
// We couldn't safely strip the HTML, so we return an empty string | ||
return ''; | ||
} | ||
return str; | ||
} | ||
@@ -206,0 +224,0 @@ module.exports.stripUnsafeTags = stripUnsafeTags; |
@@ -10,2 +10,22 @@ describe('resanitize', function (){ | ||
}); | ||
describe('unsafe elements', function () { | ||
it('should strip unsafe elements', function () { | ||
var item = require('./fixtures/unsafeElements.json'); | ||
assert.equal(resanitize(item.original), item.expected); | ||
}) | ||
}); | ||
describe('unsafe attributes', function () { | ||
it('should strip unsafe attributes', function () { | ||
var items = require('./fixtures/unsafeAttributes.json'); | ||
items.forEach(function (item) { | ||
assert.equal(resanitize(item.original), item.expected); | ||
}); | ||
}) | ||
}); | ||
describe('comments', function () { | ||
it('should strip comments', function () { | ||
var item = require('./fixtures/comments.json'); | ||
assert.equal(resanitize(item.original), item.expected); | ||
}) | ||
}); | ||
}); |
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
18030
9
323
32
1