reformat by prettier
This commit is contained in:
27
lib/cli.js
27
lib/cli.js
@@ -4,42 +4,39 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var xss = require('./');
|
||||
var readline = require('readline');
|
||||
|
||||
var xss = require("./");
|
||||
var readline = require("readline");
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
|
||||
console.log('Enter a blank line to do xss(), enter "@quit" to exit.\n');
|
||||
|
||||
function take (c, n) {
|
||||
var ret = '';
|
||||
function take(c, n) {
|
||||
var ret = "";
|
||||
for (var i = 0; i < n; i++) {
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function setPrompt (line) {
|
||||
function setPrompt(line) {
|
||||
line = line.toString();
|
||||
rl.setPrompt('[' + line + ']' + take(' ', 5 - line.length));
|
||||
rl.setPrompt("[" + line + "]" + take(" ", 5 - line.length));
|
||||
rl.prompt();
|
||||
}
|
||||
|
||||
|
||||
setPrompt(1);
|
||||
|
||||
var html = [];
|
||||
rl.on('line', function (line) {
|
||||
if (line === '@quit') return process.exit();
|
||||
if (line === '') {
|
||||
console.log('');
|
||||
console.log(xss(html.join('\r\n')));
|
||||
console.log('');
|
||||
rl.on("line", function(line) {
|
||||
if (line === "@quit") return process.exit();
|
||||
if (line === "") {
|
||||
console.log("");
|
||||
console.log(xss(html.join("\r\n")));
|
||||
console.log("");
|
||||
html = [];
|
||||
} else {
|
||||
html.push(line);
|
||||
|
||||
261
lib/default.js
261
lib/default.js
@@ -4,75 +4,75 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var FilterCSS = require('cssfilter').FilterCSS;
|
||||
var getDefaultCSSWhiteList = require('cssfilter').getDefaultWhiteList;
|
||||
var _ = require('./util');
|
||||
var FilterCSS = require("cssfilter").FilterCSS;
|
||||
var getDefaultCSSWhiteList = require("cssfilter").getDefaultWhiteList;
|
||||
var _ = require("./util");
|
||||
|
||||
function getDefaultWhiteList () {
|
||||
function getDefaultWhiteList() {
|
||||
return {
|
||||
a: ['target', 'href', 'title'],
|
||||
abbr: ['title'],
|
||||
a: ["target", "href", "title"],
|
||||
abbr: ["title"],
|
||||
address: [],
|
||||
area: ['shape', 'coords', 'href', 'alt'],
|
||||
area: ["shape", "coords", "href", "alt"],
|
||||
article: [],
|
||||
aside: [],
|
||||
audio: ['autoplay', 'controls', 'loop', 'preload', 'src'],
|
||||
b: [],
|
||||
bdi: ['dir'],
|
||||
bdo: ['dir'],
|
||||
big: [],
|
||||
blockquote: ['cite'],
|
||||
br: [],
|
||||
aside: [],
|
||||
audio: ["autoplay", "controls", "loop", "preload", "src"],
|
||||
b: [],
|
||||
bdi: ["dir"],
|
||||
bdo: ["dir"],
|
||||
big: [],
|
||||
blockquote: ["cite"],
|
||||
br: [],
|
||||
caption: [],
|
||||
center: [],
|
||||
cite: [],
|
||||
code: [],
|
||||
col: ['align', 'valign', 'span', 'width'],
|
||||
colgroup: ['align', 'valign', 'span', 'width'],
|
||||
dd: [],
|
||||
del: ['datetime'],
|
||||
details: ['open'],
|
||||
div: [],
|
||||
dl: [],
|
||||
dt: [],
|
||||
em: [],
|
||||
font: ['color', 'size', 'face'],
|
||||
cite: [],
|
||||
code: [],
|
||||
col: ["align", "valign", "span", "width"],
|
||||
colgroup: ["align", "valign", "span", "width"],
|
||||
dd: [],
|
||||
del: ["datetime"],
|
||||
details: ["open"],
|
||||
div: [],
|
||||
dl: [],
|
||||
dt: [],
|
||||
em: [],
|
||||
font: ["color", "size", "face"],
|
||||
footer: [],
|
||||
h1: [],
|
||||
h2: [],
|
||||
h3: [],
|
||||
h4: [],
|
||||
h5: [],
|
||||
h6: [],
|
||||
h1: [],
|
||||
h2: [],
|
||||
h3: [],
|
||||
h4: [],
|
||||
h5: [],
|
||||
h6: [],
|
||||
header: [],
|
||||
hr: [],
|
||||
i: [],
|
||||
img: ['src', 'alt', 'title', 'width', 'height'],
|
||||
ins: ['datetime'],
|
||||
li: [],
|
||||
mark: [],
|
||||
nav: [],
|
||||
ol: [],
|
||||
p: [],
|
||||
pre: [],
|
||||
s: [],
|
||||
section:[],
|
||||
small: [],
|
||||
span: [],
|
||||
sub: [],
|
||||
sup: [],
|
||||
hr: [],
|
||||
i: [],
|
||||
img: ["src", "alt", "title", "width", "height"],
|
||||
ins: ["datetime"],
|
||||
li: [],
|
||||
mark: [],
|
||||
nav: [],
|
||||
ol: [],
|
||||
p: [],
|
||||
pre: [],
|
||||
s: [],
|
||||
section: [],
|
||||
small: [],
|
||||
span: [],
|
||||
sub: [],
|
||||
sup: [],
|
||||
strong: [],
|
||||
table: ['width', 'border', 'align', 'valign'],
|
||||
tbody: ['align', 'valign'],
|
||||
td: ['width', 'rowspan', 'colspan', 'align', 'valign'],
|
||||
tfoot: ['align', 'valign'],
|
||||
th: ['width', 'rowspan', 'colspan', 'align', 'valign'],
|
||||
thead: ['align', 'valign'],
|
||||
tr: ['rowspan', 'align', 'valign'],
|
||||
tt: [],
|
||||
u: [],
|
||||
ul: [],
|
||||
video: ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width']
|
||||
table: ["width", "border", "align", "valign"],
|
||||
tbody: ["align", "valign"],
|
||||
td: ["width", "rowspan", "colspan", "align", "valign"],
|
||||
tfoot: ["align", "valign"],
|
||||
th: ["width", "rowspan", "colspan", "align", "valign"],
|
||||
thead: ["align", "valign"],
|
||||
tr: ["rowspan", "align", "valign"],
|
||||
tt: [],
|
||||
u: [],
|
||||
ul: [],
|
||||
video: ["autoplay", "controls", "loop", "preload", "src", "height", "width"]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ var defaultCSSFilter = new FilterCSS();
|
||||
* @param {Object} options
|
||||
* @return {String}
|
||||
*/
|
||||
function onTag (tag, html, options) {
|
||||
function onTag(tag, html, options) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ function onTag (tag, html, options) {
|
||||
* @param {Object} options
|
||||
* @return {String}
|
||||
*/
|
||||
function onIgnoreTag (tag, html, options) {
|
||||
function onIgnoreTag(tag, html, options) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ function onIgnoreTag (tag, html, options) {
|
||||
* @param {String} value
|
||||
* @return {String}
|
||||
*/
|
||||
function onTagAttr (tag, name, value) {
|
||||
function onTagAttr(tag, name, value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ function onTagAttr (tag, name, value) {
|
||||
* @param {String} value
|
||||
* @return {String}
|
||||
*/
|
||||
function onIgnoreTagAttr (tag, name, value) {
|
||||
function onIgnoreTagAttr(tag, name, value) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@@ -131,8 +131,8 @@ function onIgnoreTagAttr (tag, name, value) {
|
||||
*
|
||||
* @param {String} html
|
||||
*/
|
||||
function escapeHtml (html) {
|
||||
return html.replace(REGEXP_LT, '<').replace(REGEXP_GT, '>');
|
||||
function escapeHtml(html) {
|
||||
return html.replace(REGEXP_LT, "<").replace(REGEXP_GT, ">");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,42 +144,46 @@ function escapeHtml (html) {
|
||||
* @param {Object} cssFilter
|
||||
* @return {String}
|
||||
*/
|
||||
function safeAttrValue (tag, name, value, cssFilter) {
|
||||
function safeAttrValue(tag, name, value, cssFilter) {
|
||||
// unescape attribute value firstly
|
||||
value = friendlyAttrValue(value);
|
||||
|
||||
if (name === 'href' || name === 'src') {
|
||||
if (name === "href" || name === "src") {
|
||||
// filter `href` and `src` attribute
|
||||
// only allow the value that starts with `http://` | `https://` | `mailto:` | `/` | `#`
|
||||
value = _.trim(value);
|
||||
if (value === '#') return '#';
|
||||
if (!(value.substr(0, 7) === 'http://' ||
|
||||
value.substr(0, 8) === 'https://' ||
|
||||
value.substr(0, 7) === 'mailto:' ||
|
||||
value.substr(0, 4) === 'tel:' ||
|
||||
value[0] === '#' ||
|
||||
value[0] === '/')) {
|
||||
return '';
|
||||
if (value === "#") return "#";
|
||||
if (
|
||||
!(
|
||||
value.substr(0, 7) === "http://" ||
|
||||
value.substr(0, 8) === "https://" ||
|
||||
value.substr(0, 7) === "mailto:" ||
|
||||
value.substr(0, 4) === "tel:" ||
|
||||
value[0] === "#" ||
|
||||
value[0] === "/"
|
||||
)
|
||||
) {
|
||||
return "";
|
||||
}
|
||||
} else if (name === 'background') {
|
||||
} else if (name === "background") {
|
||||
// filter `background` attribute (maybe no use)
|
||||
// `javascript:`
|
||||
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
||||
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
} else if (name === 'style') {
|
||||
} else if (name === "style") {
|
||||
// `expression()`
|
||||
REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0;
|
||||
if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
// `url()`
|
||||
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;
|
||||
if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {
|
||||
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
|
||||
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
}
|
||||
if (cssFilter !== false) {
|
||||
@@ -198,15 +202,15 @@ var REGEXP_LT = /</g;
|
||||
var REGEXP_GT = />/g;
|
||||
var REGEXP_QUOTE = /"/g;
|
||||
var REGEXP_QUOTE_2 = /"/g;
|
||||
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/img;
|
||||
var REGEXP_ATTR_VALUE_COLON = /:?/img;
|
||||
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/img;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//mg;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_4 = /((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/ig;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/ig;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//ig;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_7 = /e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/ig;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/ig;
|
||||
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/gim;
|
||||
var REGEXP_ATTR_VALUE_COLON = /:?/gim;
|
||||
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/gim;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//gm;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_4 = /((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/gi;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/gi;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//gi;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_7 = /e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi;
|
||||
var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/gi;
|
||||
|
||||
/**
|
||||
* escape doube quote
|
||||
@@ -214,8 +218,8 @@ var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/ig;
|
||||
* @param {String} str
|
||||
* @return {String} str
|
||||
*/
|
||||
function escapeQuote (str) {
|
||||
return str.replace(REGEXP_QUOTE, '"');
|
||||
function escapeQuote(str) {
|
||||
return str.replace(REGEXP_QUOTE, """);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +228,7 @@ function escapeQuote (str) {
|
||||
* @param {String} str
|
||||
* @return {String} str
|
||||
*/
|
||||
function unescapeQuote (str) {
|
||||
function unescapeQuote(str) {
|
||||
return str.replace(REGEXP_QUOTE_2, '"');
|
||||
}
|
||||
|
||||
@@ -234,11 +238,11 @@ function unescapeQuote (str) {
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
function escapeHtmlEntities (str) {
|
||||
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode (str, code) {
|
||||
return (code[0] === 'x' || code[0] === 'X')
|
||||
? String.fromCharCode(parseInt(code.substr(1), 16))
|
||||
: String.fromCharCode(parseInt(code, 10));
|
||||
function escapeHtmlEntities(str) {
|
||||
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode(str, code) {
|
||||
return code[0] === "x" || code[0] === "X"
|
||||
? String.fromCharCode(parseInt(code.substr(1), 16))
|
||||
: String.fromCharCode(parseInt(code, 10));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -248,9 +252,10 @@ function escapeHtmlEntities (str) {
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
function escapeDangerHtml5Entities (str) {
|
||||
return str.replace(REGEXP_ATTR_VALUE_COLON, ':')
|
||||
.replace(REGEXP_ATTR_VALUE_NEWLINE, ' ');
|
||||
function escapeDangerHtml5Entities(str) {
|
||||
return str
|
||||
.replace(REGEXP_ATTR_VALUE_COLON, ":")
|
||||
.replace(REGEXP_ATTR_VALUE_NEWLINE, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,10 +264,10 @@ function escapeDangerHtml5Entities (str) {
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
function clearNonPrintableCharacter (str) {
|
||||
var str2 = '';
|
||||
function clearNonPrintableCharacter(str) {
|
||||
var str2 = "";
|
||||
for (var i = 0, len = str.length; i < len; i++) {
|
||||
str2 += str.charCodeAt(i) < 32 ? ' ' : str.charAt(i);
|
||||
str2 += str.charCodeAt(i) < 32 ? " " : str.charAt(i);
|
||||
}
|
||||
return _.trim(str2);
|
||||
}
|
||||
@@ -273,7 +278,7 @@ function clearNonPrintableCharacter (str) {
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
function friendlyAttrValue (str) {
|
||||
function friendlyAttrValue(str) {
|
||||
str = unescapeQuote(str);
|
||||
str = escapeHtmlEntities(str);
|
||||
str = escapeDangerHtml5Entities(str);
|
||||
@@ -287,7 +292,7 @@ function friendlyAttrValue (str) {
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
*/
|
||||
function escapeAttrValue (str) {
|
||||
function escapeAttrValue(str) {
|
||||
str = escapeQuote(str);
|
||||
str = escapeHtml(str);
|
||||
return str;
|
||||
@@ -296,8 +301,8 @@ function escapeAttrValue (str) {
|
||||
/**
|
||||
* `onIgnoreTag` function for removing all the tags that are not in whitelist
|
||||
*/
|
||||
function onIgnoreTagStripAll () {
|
||||
return '';
|
||||
function onIgnoreTagStripAll() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,43 +312,46 @@ function onIgnoreTagStripAll () {
|
||||
* @param {array} tags
|
||||
* @param {function} next
|
||||
*/
|
||||
function StripTagBody (tags, next) {
|
||||
if (typeof(next) !== 'function') {
|
||||
next = function () {};
|
||||
function StripTagBody(tags, next) {
|
||||
if (typeof next !== "function") {
|
||||
next = function() {};
|
||||
}
|
||||
|
||||
var isRemoveAllTag = !Array.isArray(tags);
|
||||
function isRemoveTag (tag) {
|
||||
function isRemoveTag(tag) {
|
||||
if (isRemoveAllTag) return true;
|
||||
return (_.indexOf(tags, tag) !== -1);
|
||||
return _.indexOf(tags, tag) !== -1;
|
||||
}
|
||||
|
||||
var removeList = [];
|
||||
var posStart = false;
|
||||
|
||||
return {
|
||||
onIgnoreTag: function (tag, html, options) {
|
||||
onIgnoreTag: function(tag, html, options) {
|
||||
if (isRemoveTag(tag)) {
|
||||
if (options.isClosing) {
|
||||
var ret = '[/removed]';
|
||||
var ret = "[/removed]";
|
||||
var end = options.position + ret.length;
|
||||
removeList.push([posStart !== false ? posStart : options.position, end]);
|
||||
removeList.push([
|
||||
posStart !== false ? posStart : options.position,
|
||||
end
|
||||
]);
|
||||
posStart = false;
|
||||
return ret;
|
||||
} else {
|
||||
if (!posStart) {
|
||||
posStart = options.position;
|
||||
}
|
||||
return '[removed]';
|
||||
return "[removed]";
|
||||
}
|
||||
} else {
|
||||
return next(tag, html, options);
|
||||
}
|
||||
},
|
||||
remove: function (html) {
|
||||
var rethtml = '';
|
||||
remove: function(html) {
|
||||
var rethtml = "";
|
||||
var lastPos = 0;
|
||||
_.forEach(removeList, function (pos) {
|
||||
_.forEach(removeList, function(pos) {
|
||||
rethtml += html.slice(lastPos, pos[0]);
|
||||
lastPos = pos[1];
|
||||
});
|
||||
@@ -359,8 +367,8 @@ function StripTagBody (tags, next) {
|
||||
* @param {String} html
|
||||
* @return {String}
|
||||
*/
|
||||
function stripCommentTag (html) {
|
||||
return html.replace(STRIP_COMMENT_TAG_REGEXP, '');
|
||||
function stripCommentTag(html) {
|
||||
return html.replace(STRIP_COMMENT_TAG_REGEXP, "");
|
||||
}
|
||||
var STRIP_COMMENT_TAG_REGEXP = /<!--[\s\S]*?-->/g;
|
||||
|
||||
@@ -370,9 +378,9 @@ var STRIP_COMMENT_TAG_REGEXP = /<!--[\s\S]*?-->/g;
|
||||
* @param {String} html
|
||||
* @return {String}
|
||||
*/
|
||||
function stripBlankChar (html) {
|
||||
var chars = html.split('');
|
||||
chars = chars.filter(function (char) {
|
||||
function stripBlankChar(html) {
|
||||
var chars = html.split("");
|
||||
chars = chars.filter(function(char) {
|
||||
var c = char.charCodeAt(0);
|
||||
if (c === 127) return false;
|
||||
if (c <= 31) {
|
||||
@@ -381,10 +389,9 @@ function stripBlankChar (html) {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return chars.join('');
|
||||
return chars.join("");
|
||||
}
|
||||
|
||||
|
||||
exports.whiteList = getDefaultWhiteList();
|
||||
exports.getDefaultWhiteList = getDefaultWhiteList;
|
||||
exports.onTag = onTag;
|
||||
|
||||
13
lib/index.js
13
lib/index.js
@@ -4,10 +4,9 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var DEFAULT = require('./default');
|
||||
var parser = require('./parser');
|
||||
var FilterXSS = require('./xss');
|
||||
|
||||
var DEFAULT = require("./default");
|
||||
var parser = require("./parser");
|
||||
var FilterXSS = require("./xss");
|
||||
|
||||
/**
|
||||
* filter xss function
|
||||
@@ -16,19 +15,17 @@ var FilterXSS = require('./xss');
|
||||
* @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
|
||||
* @return {String}
|
||||
*/
|
||||
function filterXSS (html, options) {
|
||||
function filterXSS(html, options) {
|
||||
var xss = new FilterXSS(options);
|
||||
return xss.process(html);
|
||||
}
|
||||
|
||||
|
||||
exports = module.exports = filterXSS;
|
||||
exports.FilterXSS = FilterXSS;
|
||||
for (var i in DEFAULT) exports[i] = DEFAULT[i];
|
||||
for (var i in parser) exports[i] = parser[i];
|
||||
|
||||
|
||||
// using `xss` on the browser, output `filterXSS` to the globals
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window !== "undefined") {
|
||||
window.filterXSS = module.exports;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var _ = require('./util');
|
||||
var _ = require("./util");
|
||||
|
||||
/**
|
||||
* get tag name
|
||||
@@ -12,7 +12,7 @@ var _ = require('./util');
|
||||
* @param {String} html e.g. '<a hef="#">'
|
||||
* @return {String}
|
||||
*/
|
||||
function getTagName (html) {
|
||||
function getTagName(html) {
|
||||
var i = _.spaceIndex(html);
|
||||
if (i === -1) {
|
||||
var tagName = html.slice(1, -1);
|
||||
@@ -20,8 +20,8 @@ function getTagName (html) {
|
||||
var tagName = html.slice(1, i + 1);
|
||||
}
|
||||
tagName = _.trim(tagName).toLowerCase();
|
||||
if (tagName.slice(0, 1) === '/') tagName = tagName.slice(1);
|
||||
if (tagName.slice(-1) === '/') tagName = tagName.slice(0, -1);
|
||||
if (tagName.slice(0, 1) === "/") tagName = tagName.slice(1);
|
||||
if (tagName.slice(-1) === "/") tagName = tagName.slice(0, -1);
|
||||
return tagName;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ function getTagName (html) {
|
||||
* @param {String} html 如:'<a hef="#">'
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isClosing (html) {
|
||||
return (html.slice(0, 2) === '</');
|
||||
function isClosing(html) {
|
||||
return html.slice(0, 2) === "</";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,46 +43,48 @@ function isClosing (html) {
|
||||
* @param {Function} escapeHtml
|
||||
* @return {String}
|
||||
*/
|
||||
function parseTag (html, onTag, escapeHtml) {
|
||||
'user strict';
|
||||
function parseTag(html, onTag, escapeHtml) {
|
||||
"user strict";
|
||||
|
||||
var rethtml = '';
|
||||
var rethtml = "";
|
||||
var lastPos = 0;
|
||||
var tagStart = false;
|
||||
var quoteStart = false;
|
||||
var currentPos = 0;
|
||||
var len = html.length;
|
||||
var currentTagName = '';
|
||||
var currentTagName = "";
|
||||
|
||||
for (currentPos = 0; currentPos < len; currentPos++) {
|
||||
var c = html.charAt(currentPos);
|
||||
if (tagStart === false) {
|
||||
if (c === '<') {
|
||||
if (c === "<") {
|
||||
tagStart = currentPos;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (quoteStart === false) {
|
||||
if (c === '<') {
|
||||
if (c === "<") {
|
||||
rethtml += escapeHtml(html.slice(lastPos, currentPos));
|
||||
tagStart = currentPos;
|
||||
lastPos = currentPos;
|
||||
continue;
|
||||
}
|
||||
if (c === '>') {
|
||||
if (c === ">") {
|
||||
rethtml += escapeHtml(html.slice(lastPos, tagStart));
|
||||
currentHtml = html.slice(tagStart, currentPos + 1);
|
||||
currentTagName = getTagName(currentHtml);
|
||||
rethtml += onTag(tagStart,
|
||||
rethtml.length,
|
||||
currentTagName,
|
||||
currentHtml,
|
||||
isClosing(currentHtml));
|
||||
rethtml += onTag(
|
||||
tagStart,
|
||||
rethtml.length,
|
||||
currentTagName,
|
||||
currentHtml,
|
||||
isClosing(currentHtml)
|
||||
);
|
||||
lastPos = currentPos + 1;
|
||||
tagStart = false;
|
||||
continue;
|
||||
}
|
||||
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === '=') {
|
||||
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === "=") {
|
||||
quoteStart = c;
|
||||
continue;
|
||||
}
|
||||
@@ -101,7 +103,7 @@ function parseTag (html, onTag, escapeHtml) {
|
||||
return rethtml;
|
||||
}
|
||||
|
||||
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img;
|
||||
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/gim;
|
||||
|
||||
/**
|
||||
* parse input attributes and returns processed attributes
|
||||
@@ -110,33 +112,37 @@ var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img;
|
||||
* @param {Function} onAttr e.g. `function (name, value)`
|
||||
* @return {String}
|
||||
*/
|
||||
function parseAttr (html, onAttr) {
|
||||
'user strict';
|
||||
function parseAttr(html, onAttr) {
|
||||
"user strict";
|
||||
|
||||
var lastPos = 0;
|
||||
var retAttrs = [];
|
||||
var tmpName = false;
|
||||
var len = html.length;
|
||||
|
||||
function addAttr (name, value) {
|
||||
function addAttr(name, value) {
|
||||
name = _.trim(name);
|
||||
name = name.replace(REGEXP_ILLEGAL_ATTR_NAME, '').toLowerCase();
|
||||
name = name.replace(REGEXP_ILLEGAL_ATTR_NAME, "").toLowerCase();
|
||||
if (name.length < 1) return;
|
||||
var ret = onAttr(name, value || '');
|
||||
var ret = onAttr(name, value || "");
|
||||
if (ret) retAttrs.push(ret);
|
||||
};
|
||||
}
|
||||
|
||||
// 逐个分析字符
|
||||
for (var i = 0; i < len; i++) {
|
||||
var c = html.charAt(i);
|
||||
var v, j;
|
||||
if (tmpName === false && c === '=') {
|
||||
if (tmpName === false && c === "=") {
|
||||
tmpName = html.slice(lastPos, i);
|
||||
lastPos = i + 1;
|
||||
continue;
|
||||
}
|
||||
if (tmpName !== false) {
|
||||
if (i === lastPos && (c === '"' || c === "'") && html.charAt(i - 1) === '=') {
|
||||
if (
|
||||
i === lastPos &&
|
||||
(c === '"' || c === "'") &&
|
||||
html.charAt(i - 1) === "="
|
||||
) {
|
||||
j = html.indexOf(c, i + 1);
|
||||
if (j === -1) {
|
||||
break;
|
||||
@@ -151,7 +157,7 @@ function parseAttr (html, onAttr) {
|
||||
}
|
||||
}
|
||||
if (/\s|\n|\t/.test(c)) {
|
||||
html = html.replace(/\s|\n|\t/g, ' ');
|
||||
html = html.replace(/\s|\n|\t/g, " ");
|
||||
if (tmpName === false) {
|
||||
j = findNextEqual(html, i);
|
||||
if (j === -1) {
|
||||
@@ -188,44 +194,45 @@ function parseAttr (html, onAttr) {
|
||||
}
|
||||
}
|
||||
|
||||
return _.trim(retAttrs.join(' '));
|
||||
return _.trim(retAttrs.join(" "));
|
||||
}
|
||||
|
||||
function findNextEqual (str, i) {
|
||||
function findNextEqual(str, i) {
|
||||
for (; i < str.length; i++) {
|
||||
var c = str[i];
|
||||
if (c === ' ') continue;
|
||||
if (c === '=') return i;
|
||||
if (c === " ") continue;
|
||||
if (c === "=") return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function findBeforeEqual (str, i) {
|
||||
function findBeforeEqual(str, i) {
|
||||
for (; i > 0; i--) {
|
||||
var c = str[i];
|
||||
if (c === ' ') continue;
|
||||
if (c === '=') return i;
|
||||
if (c === " ") continue;
|
||||
if (c === "=") return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function isQuoteWrapString (text) {
|
||||
if ((text[0] === '"' && text[text.length - 1] === '"') ||
|
||||
(text[0] === '\'' && text[text.length - 1] === '\'')) {
|
||||
function isQuoteWrapString(text) {
|
||||
if (
|
||||
(text[0] === '"' && text[text.length - 1] === '"') ||
|
||||
(text[0] === "'" && text[text.length - 1] === "'")
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function stripQuoteWrap (text) {
|
||||
function stripQuoteWrap(text) {
|
||||
if (isQuoteWrapString(text)) {
|
||||
return text.substr(1, text.length - 2);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
exports.parseTag = parseTag;
|
||||
exports.parseAttr = parseAttr;
|
||||
|
||||
16
lib/util.js
16
lib/util.js
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
indexOf: function (arr, item) {
|
||||
indexOf: function(arr, item) {
|
||||
var i, j;
|
||||
if (Array.prototype.indexOf) {
|
||||
return arr.indexOf(item);
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
forEach: function (arr, fn, scope) {
|
||||
forEach: function(arr, fn, scope) {
|
||||
var i, j;
|
||||
if (Array.prototype.forEach) {
|
||||
return arr.forEach(fn, scope);
|
||||
@@ -20,15 +20,15 @@ module.exports = {
|
||||
fn.call(scope, arr[i], i, arr);
|
||||
}
|
||||
},
|
||||
trim: function (str) {
|
||||
trim: function(str) {
|
||||
if (String.prototype.trim) {
|
||||
return str.trim();
|
||||
}
|
||||
return str.replace(/(^\s*)|(\s*$)/g, '');
|
||||
return str.replace(/(^\s*)|(\s*$)/g, "");
|
||||
},
|
||||
spaceIndex: function (str) {
|
||||
var reg = /\s|\n|\t/;
|
||||
var match = reg.exec(str);
|
||||
return match ? match.index : -1;
|
||||
spaceIndex: function(str) {
|
||||
var reg = /\s|\n|\t/;
|
||||
var match = reg.exec(str);
|
||||
return match ? match.index : -1;
|
||||
}
|
||||
};
|
||||
|
||||
152
lib/xss.js
152
lib/xss.js
@@ -4,13 +4,12 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var FilterCSS = require('cssfilter').FilterCSS;
|
||||
var DEFAULT = require('./default');
|
||||
var parser = require('./parser');
|
||||
var FilterCSS = require("cssfilter").FilterCSS;
|
||||
var DEFAULT = require("./default");
|
||||
var parser = require("./parser");
|
||||
var parseTag = parser.parseTag;
|
||||
var parseAttr = parser.parseAttr;
|
||||
var _ = require('./util');
|
||||
|
||||
var _ = require("./util");
|
||||
|
||||
/**
|
||||
* returns `true` if the input value is `undefined` or `null`
|
||||
@@ -18,8 +17,8 @@ var _ = require('./util');
|
||||
* @param {Object} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNull (obj) {
|
||||
return (obj === undefined || obj === null);
|
||||
function isNull(obj) {
|
||||
return obj === undefined || obj === null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,19 +29,19 @@ function isNull (obj) {
|
||||
* - {String} html
|
||||
* - {Boolean} closing
|
||||
*/
|
||||
function getAttrs (html) {
|
||||
function getAttrs(html) {
|
||||
var i = _.spaceIndex(html);
|
||||
if (i === -1) {
|
||||
return {
|
||||
html: '',
|
||||
closing: (html[html.length - 2] === '/')
|
||||
html: "",
|
||||
closing: html[html.length - 2] === "/"
|
||||
};
|
||||
}
|
||||
html = _.trim(html.slice(i + 1, -1));
|
||||
var isClosing = (html[html.length - 1] === '/');
|
||||
var isClosing = html[html.length - 1] === "/";
|
||||
if (isClosing) html = _.trim(html.slice(0, -1));
|
||||
return {
|
||||
html: html,
|
||||
html: html,
|
||||
closing: isClosing
|
||||
};
|
||||
}
|
||||
@@ -53,7 +52,7 @@ function getAttrs (html) {
|
||||
* @param {Object} obj
|
||||
* @return {Object}
|
||||
*/
|
||||
function shallowCopyObject (obj) {
|
||||
function shallowCopyObject(obj) {
|
||||
var ret = {};
|
||||
for (var i in obj) {
|
||||
ret[i] = obj[i];
|
||||
@@ -70,12 +69,14 @@ function shallowCopyObject (obj) {
|
||||
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
|
||||
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
|
||||
*/
|
||||
function FilterXSS (options) {
|
||||
function FilterXSS(options) {
|
||||
options = shallowCopyObject(options || {});
|
||||
|
||||
if (options.stripIgnoreTag) {
|
||||
if (options.onIgnoreTag) {
|
||||
console.error('Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time');
|
||||
console.error(
|
||||
'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
|
||||
);
|
||||
}
|
||||
options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
|
||||
}
|
||||
@@ -103,11 +104,11 @@ function FilterXSS (options) {
|
||||
* @param {String} html
|
||||
* @return {String}
|
||||
*/
|
||||
FilterXSS.prototype.process = function (html) {
|
||||
FilterXSS.prototype.process = function(html) {
|
||||
// compatible with the input
|
||||
html = html || '';
|
||||
html = html || "";
|
||||
html = html.toString();
|
||||
if (!html) return '';
|
||||
if (!html) return "";
|
||||
|
||||
var me = this;
|
||||
var options = me.options;
|
||||
@@ -133,67 +134,71 @@ FilterXSS.prototype.process = function (html) {
|
||||
// if enable stripIgnoreTagBody
|
||||
var stripIgnoreTagBody = false;
|
||||
if (options.stripIgnoreTagBody) {
|
||||
var stripIgnoreTagBody = DEFAULT.StripTagBody(options.stripIgnoreTagBody, onIgnoreTag);
|
||||
var stripIgnoreTagBody = DEFAULT.StripTagBody(
|
||||
options.stripIgnoreTagBody,
|
||||
onIgnoreTag
|
||||
);
|
||||
onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
|
||||
}
|
||||
|
||||
var retHtml = parseTag(html, function (sourcePosition, position, tag, html, isClosing) {
|
||||
var info = {
|
||||
sourcePosition: sourcePosition,
|
||||
position: position,
|
||||
isClosing: isClosing,
|
||||
isWhite: (tag in whiteList)
|
||||
};
|
||||
var retHtml = parseTag(
|
||||
html,
|
||||
function(sourcePosition, position, tag, html, isClosing) {
|
||||
var info = {
|
||||
sourcePosition: sourcePosition,
|
||||
position: position,
|
||||
isClosing: isClosing,
|
||||
isWhite: tag in whiteList
|
||||
};
|
||||
|
||||
// call `onTag()`
|
||||
var ret = onTag(tag, html, info);
|
||||
if (!isNull(ret)) return ret;
|
||||
|
||||
if (info.isWhite) {
|
||||
if (info.isClosing) {
|
||||
return '</' + tag + '>';
|
||||
}
|
||||
|
||||
var attrs = getAttrs(html);
|
||||
var whiteAttrList = whiteList[tag];
|
||||
var attrsHtml = parseAttr(attrs.html, function (name, value) {
|
||||
|
||||
// call `onTagAttr()`
|
||||
var isWhiteAttr = (_.indexOf(whiteAttrList, name) !== -1);
|
||||
var ret = onTagAttr(tag, name, value, isWhiteAttr);
|
||||
if (!isNull(ret)) return ret;
|
||||
|
||||
if (isWhiteAttr) {
|
||||
// call `safeAttrValue()`
|
||||
value = safeAttrValue(tag, name, value, cssFilter);
|
||||
if (value) {
|
||||
return name + '="' + value + '"';
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
} else {
|
||||
// call `onIgnoreTagAttr()`
|
||||
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
||||
if (!isNull(ret)) return ret;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// build new tag html
|
||||
var html = '<' + tag;
|
||||
if (attrsHtml) html += ' ' + attrsHtml;
|
||||
if (attrs.closing) html += ' /';
|
||||
html += '>';
|
||||
return html;
|
||||
|
||||
} else {
|
||||
// call `onIgnoreTag()`
|
||||
var ret = onIgnoreTag(tag, html, info);
|
||||
// call `onTag()`
|
||||
var ret = onTag(tag, html, info);
|
||||
if (!isNull(ret)) return ret;
|
||||
return escapeHtml(html);
|
||||
}
|
||||
|
||||
}, escapeHtml);
|
||||
if (info.isWhite) {
|
||||
if (info.isClosing) {
|
||||
return "</" + tag + ">";
|
||||
}
|
||||
|
||||
var attrs = getAttrs(html);
|
||||
var whiteAttrList = whiteList[tag];
|
||||
var attrsHtml = parseAttr(attrs.html, function(name, value) {
|
||||
// call `onTagAttr()`
|
||||
var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
|
||||
var ret = onTagAttr(tag, name, value, isWhiteAttr);
|
||||
if (!isNull(ret)) return ret;
|
||||
|
||||
if (isWhiteAttr) {
|
||||
// call `safeAttrValue()`
|
||||
value = safeAttrValue(tag, name, value, cssFilter);
|
||||
if (value) {
|
||||
return name + '="' + value + '"';
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
} else {
|
||||
// call `onIgnoreTagAttr()`
|
||||
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
|
||||
if (!isNull(ret)) return ret;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// build new tag html
|
||||
var html = "<" + tag;
|
||||
if (attrsHtml) html += " " + attrsHtml;
|
||||
if (attrs.closing) html += " /";
|
||||
html += ">";
|
||||
return html;
|
||||
} else {
|
||||
// call `onIgnoreTag()`
|
||||
var ret = onIgnoreTag(tag, html, info);
|
||||
if (!isNull(ret)) return ret;
|
||||
return escapeHtml(html);
|
||||
}
|
||||
},
|
||||
escapeHtml
|
||||
);
|
||||
|
||||
// if enable stripIgnoreTagBody
|
||||
if (stripIgnoreTagBody) {
|
||||
@@ -203,5 +208,4 @@ FilterXSS.prototype.process = function (html) {
|
||||
return retHtml;
|
||||
};
|
||||
|
||||
|
||||
module.exports = FilterXSS;
|
||||
|
||||
@@ -4,65 +4,63 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var xss = require('../');
|
||||
var debug = require('debug')('xss:test');
|
||||
var assert = require("assert");
|
||||
var xss = require("../");
|
||||
var debug = require("debug")("xss:test");
|
||||
|
||||
|
||||
describe('test custom XSS method', function () {
|
||||
|
||||
it('#onTag - match tag', function () {
|
||||
describe("test custom XSS method", function() {
|
||||
it("#onTag - match tag", function() {
|
||||
var source = 'dd<a href="#"><b><c>haha</c></b></a><br>ff';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onTag: function (tag, html, options) {
|
||||
onTag: function(tag, html, options) {
|
||||
debug(arguments);
|
||||
i++;
|
||||
if (i === 1) {
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(tag, "a");
|
||||
assert.equal(html, '<a href="#">');
|
||||
assert.equal(options.isClosing, false);
|
||||
assert.equal(options.position, 2);
|
||||
assert.equal(options.sourcePosition, 2);
|
||||
assert.equal(options.isWhite, true);
|
||||
} else if (i === 2) {
|
||||
assert.equal(tag, 'b');
|
||||
assert.equal(html, '<b>');
|
||||
assert.equal(tag, "b");
|
||||
assert.equal(html, "<b>");
|
||||
assert.equal(options.isClosing, false);
|
||||
assert.equal(options.position, 14);
|
||||
assert.equal(options.sourcePosition, 14);
|
||||
assert.equal(options.isWhite, true);
|
||||
} else if (i === 3) {
|
||||
assert.equal(tag, 'c');
|
||||
assert.equal(html, '<c>');
|
||||
assert.equal(tag, "c");
|
||||
assert.equal(html, "<c>");
|
||||
assert.equal(options.isClosing, false);
|
||||
assert.equal(options.position, 17);
|
||||
assert.equal(options.sourcePosition, 17);
|
||||
assert.equal(options.isWhite, false);
|
||||
} else if (i === 4) {
|
||||
assert.equal(tag, 'c');
|
||||
assert.equal(html, '</c>');
|
||||
assert.equal(tag, "c");
|
||||
assert.equal(html, "</c>");
|
||||
assert.equal(options.isClosing, true);
|
||||
assert.equal(options.position, 30);
|
||||
assert.equal(options.sourcePosition, 24);
|
||||
assert.equal(options.isWhite, false);
|
||||
} else if (i === 5) {
|
||||
assert.equal(tag, 'b');
|
||||
assert.equal(html, '</b>');
|
||||
assert.equal(tag, "b");
|
||||
assert.equal(html, "</b>");
|
||||
assert.equal(options.isClosing, true);
|
||||
assert.equal(options.position, 40);
|
||||
assert.equal(options.sourcePosition, 28);
|
||||
assert.equal(options.isWhite, true);
|
||||
} else if (i === 6) {
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(html, '</a>');
|
||||
assert.equal(tag, "a");
|
||||
assert.equal(html, "</a>");
|
||||
assert.equal(options.isClosing, true);
|
||||
assert.equal(options.position, 44);
|
||||
assert.equal(options.sourcePosition, 32);
|
||||
assert.equal(options.isWhite, true);
|
||||
} else if (i === 7) {
|
||||
assert.equal(tag, 'br');
|
||||
assert.equal(html, '<br>');
|
||||
assert.equal(tag, "br");
|
||||
assert.equal(html, "<br>");
|
||||
assert.equal(options.isClosing, false);
|
||||
assert.equal(options.position, 48);
|
||||
assert.equal(options.sourcePosition, 36);
|
||||
@@ -73,15 +71,17 @@ describe('test custom XSS method', function () {
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, 'dd<a href="#"><b><c>haha</c></b></a><br>ff');
|
||||
|
||||
assert.equal(
|
||||
html,
|
||||
'dd<a href="#"><b><c>haha</c></b></a><br>ff'
|
||||
);
|
||||
});
|
||||
|
||||
it('#onTag - return new html', function () {
|
||||
it("#onTag - return new html", function() {
|
||||
var source = 'dd<a href="#"><b><c>haha</c></b></a><br>ff';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onTag: function (tag, html, options) {
|
||||
onTag: function(tag, html, options) {
|
||||
debug(html);
|
||||
return html;
|
||||
}
|
||||
@@ -90,23 +90,23 @@ describe('test custom XSS method', function () {
|
||||
assert.equal(html, source);
|
||||
});
|
||||
|
||||
it('#onIgnoreTag - match tag', function () {
|
||||
it("#onIgnoreTag - match tag", function() {
|
||||
var source = 'dd<a href="#"><b><c>haha</c></b></a><br>ff';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onIgnoreTag: function (tag, html, options) {
|
||||
onIgnoreTag: function(tag, html, options) {
|
||||
debug(arguments);
|
||||
i++;
|
||||
if (i === 1) {
|
||||
assert.equal(tag, 'c');
|
||||
assert.equal(html, '<c>');
|
||||
assert.equal(tag, "c");
|
||||
assert.equal(html, "<c>");
|
||||
assert.equal(options.isClosing, false);
|
||||
assert.equal(options.position, 17);
|
||||
assert.equal(options.sourcePosition, 17);
|
||||
assert.equal(options.isWhite, false);
|
||||
} else if (i === 2) {
|
||||
assert.equal(tag, 'c');
|
||||
assert.equal(html, '</c>');
|
||||
assert.equal(tag, "c");
|
||||
assert.equal(html, "</c>");
|
||||
assert.equal(options.isClosing, true);
|
||||
assert.equal(options.position, 30);
|
||||
assert.equal(options.sourcePosition, 24);
|
||||
@@ -117,45 +117,52 @@ describe('test custom XSS method', function () {
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, 'dd<a href="#"><b><c>haha</c></b></a><br>ff');
|
||||
assert.equal(
|
||||
html,
|
||||
'dd<a href="#"><b><c>haha</c></b></a><br>ff'
|
||||
);
|
||||
});
|
||||
|
||||
it('#onIgnoreTag - return new html', function () {
|
||||
it("#onIgnoreTag - return new html", function() {
|
||||
var source = 'dd<a href="#"><b><c>haha</c></b></a><br>ff';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onIgnoreTag: function (tag, html, options) {
|
||||
onIgnoreTag: function(tag, html, options) {
|
||||
debug(html);
|
||||
return '[' + (options.isClosing ? '/' : '') + 'removed]';
|
||||
return "[" + (options.isClosing ? "/" : "") + "removed]";
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, 'dd<a href="#"><b>[removed]haha[/removed]</b></a><br>ff');
|
||||
assert.equal(
|
||||
html,
|
||||
'dd<a href="#"><b>[removed]haha[/removed]</b></a><br>ff'
|
||||
);
|
||||
});
|
||||
|
||||
it('#onTagAttr - match attr', function () {
|
||||
var source = '<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
it("#onTagAttr - match attr", function() {
|
||||
var source =
|
||||
'<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
onTagAttr: function(tag, name, value, isWhiteAttr) {
|
||||
debug(arguments);
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(tag, "a");
|
||||
i++;
|
||||
if (i === 1) {
|
||||
assert.equal(name, 'href');
|
||||
assert.equal(value, '#');
|
||||
assert.equal(name, "href");
|
||||
assert.equal(value, "#");
|
||||
assert.equal(isWhiteAttr, true);
|
||||
} else if (i === 2) {
|
||||
assert.equal(name, 'target');
|
||||
assert.equal(value, '_blank');
|
||||
assert.equal(name, "target");
|
||||
assert.equal(value, "_blank");
|
||||
assert.equal(isWhiteAttr, true);
|
||||
} else if (i === 3) {
|
||||
assert.equal(name, 'checked');
|
||||
assert.equal(value, '');
|
||||
assert.equal(name, "checked");
|
||||
assert.equal(value, "");
|
||||
assert.equal(isWhiteAttr, false);
|
||||
} else if (i === 4) {
|
||||
assert.equal(name, 'data-a');
|
||||
assert.equal(value, 'b');
|
||||
assert.equal(name, "data-a");
|
||||
assert.equal(value, "b");
|
||||
assert.equal(isWhiteAttr, false);
|
||||
} else {
|
||||
throw new Error();
|
||||
@@ -166,34 +173,36 @@ describe('test custom XSS method', function () {
|
||||
assert.equal(html, '<a href="#" target="_blank">hi</a>');
|
||||
});
|
||||
|
||||
it('#onTagAttr - match attr', function () {
|
||||
var source = '<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
it("#onTagAttr - match attr", function() {
|
||||
var source =
|
||||
'<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
onTagAttr: function(tag, name, value, isWhiteAttr) {
|
||||
debug(arguments);
|
||||
return '$' + name + '$';
|
||||
return "$" + name + "$";
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a $href$ $target$ $checked$ $data-a$>hi</a>');
|
||||
assert.equal(html, "<a $href$ $target$ $checked$ $data-a$>hi</a>");
|
||||
});
|
||||
|
||||
it('#onIgnoreTagAttr - match attr', function () {
|
||||
var source = '<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
it("#onIgnoreTagAttr - match attr", function() {
|
||||
var source =
|
||||
'<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
|
||||
debug(arguments);
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(tag, "a");
|
||||
i++;
|
||||
if (i === 1) {
|
||||
assert.equal(name, 'checked');
|
||||
assert.equal(value, '');
|
||||
assert.equal(name, "checked");
|
||||
assert.equal(value, "");
|
||||
assert.equal(isWhiteAttr, false);
|
||||
} else if (i === 2) {
|
||||
assert.equal(name, 'data-a');
|
||||
assert.equal(value, 'b');
|
||||
assert.equal(name, "data-a");
|
||||
assert.equal(value, "b");
|
||||
assert.equal(isWhiteAttr, false);
|
||||
} else {
|
||||
throw new Error();
|
||||
@@ -204,136 +213,150 @@ describe('test custom XSS method', function () {
|
||||
assert.equal(html, '<a href="#" target="_blank">hi</a>');
|
||||
});
|
||||
|
||||
it('#onIgnoreTagAttr - match attr', function () {
|
||||
var source = '<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
it("#onIgnoreTagAttr - match attr", function() {
|
||||
var source =
|
||||
'<a href="#" target="_blank" checked data-a="b">hi</a href="d">';
|
||||
var i = 0;
|
||||
var html = xss(source, {
|
||||
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
|
||||
debug(arguments);
|
||||
return '$' + name + '$';
|
||||
return "$" + name + "$";
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a href="#" target="_blank" $checked$ $data-a$>hi</a>');
|
||||
});
|
||||
|
||||
it('#escapeHtml - default', function () {
|
||||
var source = '<x>yy</x><a>bb</a>';
|
||||
it("#escapeHtml - default", function() {
|
||||
var source = "<x>yy</x><a>bb</a>";
|
||||
var html = xss(source);
|
||||
debug(html);
|
||||
assert.equal(html, '<x>yy</x><a>bb</a>');
|
||||
assert.equal(html, "<x>yy</x><a>bb</a>");
|
||||
});
|
||||
|
||||
it('#escapeHtml - return new value', function () {
|
||||
var source = '<x>yy</x><a>bb</a>';
|
||||
it("#escapeHtml - return new value", function() {
|
||||
var source = "<x>yy</x><a>bb</a>";
|
||||
var html = xss(source, {
|
||||
escapeHtml: function (str) {
|
||||
return (str ? '[' + str + ']' : str);
|
||||
escapeHtml: function(str) {
|
||||
return str ? "[" + str + "]" : str;
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '[<x>][yy][</x>]<a>[bb]</a>');
|
||||
assert.equal(html, "[<x>][yy][</x>]<a>[bb]</a>");
|
||||
});
|
||||
|
||||
it('#safeAttrValue - default', function () {
|
||||
it("#safeAttrValue - default", function() {
|
||||
var source = '<a href="javascript:alert(/xss/)" title="hi">link</a>';
|
||||
var html = xss(source);
|
||||
debug(html);
|
||||
assert.equal(html, '<a href title="hi">link</a>');
|
||||
});
|
||||
|
||||
it('#safeAttrValue - return new value', function () {
|
||||
it("#safeAttrValue - return new value", function() {
|
||||
var source = '<a href="javascript:alert(/xss/)" title="hi">link</a>';
|
||||
var html = xss(source, {
|
||||
safeAttrValue: function (tag, name, value) {
|
||||
safeAttrValue: function(tag, name, value) {
|
||||
debug(arguments);
|
||||
assert.equal(tag, 'a');
|
||||
return '$' + name + '$';
|
||||
assert.equal(tag, "a");
|
||||
return "$" + name + "$";
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a href="$href$" title="$title$">link</a>');
|
||||
});
|
||||
|
||||
it('#stripIgnoreTag', function () {
|
||||
var source = '<x>yy</x><a>bb</a>';
|
||||
it("#stripIgnoreTag", function() {
|
||||
var source = "<x>yy</x><a>bb</a>";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTag: true
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, 'yy<a>bb</a>');
|
||||
assert.equal(html, "yy<a>bb</a>");
|
||||
});
|
||||
|
||||
it('#stripTagBody - true', function () {
|
||||
var source = '<a>link</a><x>haha</x><y>a<y></y>b</y>k';
|
||||
it("#stripTagBody - true", function() {
|
||||
var source = "<a>link</a><x>haha</x><y>a<y></y>b</y>k";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTagBody: true
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a>link</a>bk');
|
||||
assert.equal(html, "<a>link</a>bk");
|
||||
});
|
||||
|
||||
it('#stripIgnoreTagBody - *', function () {
|
||||
var source = '<a>link</a><x>haha</x><y>a<y></y>b</y>k';
|
||||
it("#stripIgnoreTagBody - *", function() {
|
||||
var source = "<a>link</a><x>haha</x><y>a<y></y>b</y>k";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTagBody: '*'
|
||||
stripIgnoreTagBody: "*"
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a>link</a>bk');
|
||||
assert.equal(html, "<a>link</a>bk");
|
||||
});
|
||||
|
||||
it('#stripIgnoreTagBody - [\'x\']', function () {
|
||||
var source = '<a>link</a><x>haha</x><y>a<y></y>b</y>k';
|
||||
it("#stripIgnoreTagBody - ['x']", function() {
|
||||
var source = "<a>link</a><x>haha</x><y>a<y></y>b</y>k";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTagBody: ['x']
|
||||
stripIgnoreTagBody: ["x"]
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a>link</a><y>a<y></y>b</y>k');
|
||||
assert.equal(html, "<a>link</a><y>a<y></y>b</y>k");
|
||||
});
|
||||
|
||||
it('#stripIgnoreTagBody - [\'x\'] & onIgnoreTag', function () {
|
||||
var source = '<a>link</a><x>haha</x><y>a<y></y>b</y>k';
|
||||
it("#stripIgnoreTagBody - ['x'] & onIgnoreTag", function() {
|
||||
var source = "<a>link</a><x>haha</x><y>a<y></y>b</y>k";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTagBody: ['x'],
|
||||
onIgnoreTag: function (tag, html, options) {
|
||||
return '$' + tag + '$';
|
||||
stripIgnoreTagBody: ["x"],
|
||||
onIgnoreTag: function(tag, html, options) {
|
||||
return "$" + tag + "$";
|
||||
}
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '<a>link</a>$y$a$y$$y$b$y$k');
|
||||
assert.equal(html, "<a>link</a>$y$a$y$$y$b$y$k");
|
||||
});
|
||||
|
||||
it('#stripIgnoreTag & stripIgnoreTagBody', function () {
|
||||
var source = '<scri' + 'pt>alert(/xss/);</scri' + 'pt>';
|
||||
it("#stripIgnoreTag & stripIgnoreTagBody", function() {
|
||||
var source = "<scri" + "pt>alert(/xss/);</scri" + "pt>";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTag: true,
|
||||
stripIgnoreTagBody: ['script']
|
||||
stripIgnoreTag: true,
|
||||
stripIgnoreTagBody: ["script"]
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, '');
|
||||
assert.equal(html, "");
|
||||
});
|
||||
|
||||
it('#stripIgnoreTag & stripIgnoreTagBody - 2', function () {
|
||||
var source = 'ooxx<scri' + 'pt>alert(/xss/);</scri' + 'pt>';
|
||||
it("#stripIgnoreTag & stripIgnoreTagBody - 2", function() {
|
||||
var source = "ooxx<scri" + "pt>alert(/xss/);</scri" + "pt>";
|
||||
var html = xss(source, {
|
||||
stripIgnoreTag: true,
|
||||
stripIgnoreTagBody: ['script']
|
||||
stripIgnoreTag: true,
|
||||
stripIgnoreTagBody: ["script"]
|
||||
});
|
||||
debug(html);
|
||||
assert.equal(html, 'ooxx');
|
||||
assert.equal(html, "ooxx");
|
||||
});
|
||||
|
||||
it('cssFilter', function () {
|
||||
it("cssFilter", function() {
|
||||
var whiteList = xss.getDefaultWhiteList();
|
||||
whiteList.div.push('style');
|
||||
assert.equal(xss('<div style="width: 50%; vertical-align: top;">hello</div>', { whiteList: whiteList }),
|
||||
'<div style="width:50%;">hello</div>');
|
||||
assert.equal(xss('<div style="width: 50%; vertical-align: top;">hello</div>', { whiteList: whiteList, css: false }),
|
||||
'<div style="width: 50%; vertical-align: top;">hello</div>');
|
||||
whiteList.div.push("style");
|
||||
assert.equal(
|
||||
xss('<div style="width: 50%; vertical-align: top;">hello</div>', {
|
||||
whiteList: whiteList
|
||||
}),
|
||||
'<div style="width:50%;">hello</div>'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<div style="width: 50%; vertical-align: top;">hello</div>', {
|
||||
whiteList: whiteList,
|
||||
css: false
|
||||
}),
|
||||
'<div style="width: 50%; vertical-align: top;">hello</div>'
|
||||
);
|
||||
var css = { whiteList: xss.getDefaultCSSWhiteList() };
|
||||
css.whiteList['vertical-align'] = true;
|
||||
assert.equal(xss('<div style="width: 50%; vertical-align: top;">hello</div>', { whiteList: whiteList, css: css }),
|
||||
'<div style="width:50%; vertical-align:top;">hello</div>');
|
||||
css.whiteList["vertical-align"] = true;
|
||||
assert.equal(
|
||||
xss('<div style="width: 50%; vertical-align: top;">hello</div>', {
|
||||
whiteList: whiteList,
|
||||
css: css
|
||||
}),
|
||||
'<div style="width:50%; vertical-align:top;">hello</div>'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,124 +4,136 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var parser = require('../lib/parser');
|
||||
var assert = require("assert");
|
||||
var parser = require("../lib/parser");
|
||||
var parseTag = parser.parseTag;
|
||||
var parseAttr = parser.parseAttr;
|
||||
var debug = require('debug')('xss:test');
|
||||
var debug = require("debug")("xss:test");
|
||||
|
||||
describe('test HTML parser', function () {
|
||||
|
||||
function escapeHtml (html) {
|
||||
return html.replace(/</g, '<').replace(/>/g, '>');
|
||||
describe("test HTML parser", function() {
|
||||
function escapeHtml(html) {
|
||||
return html.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
|
||||
function attr (n, v) {
|
||||
function attr(n, v) {
|
||||
if (v) {
|
||||
return n + '="' + v.replace(/"/g, '"e;') + '"';
|
||||
return n + '="' + v.replace(/"/g, ""e;") + '"';
|
||||
} else {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
it('#parseTag', function () {
|
||||
it("#parseTag", function() {
|
||||
var i = 0;
|
||||
var html = parseTag('hello<A href="#">www</A>ccc<b><br/>', function (sourcePosition, position, tag, html, isClosing) {
|
||||
i++;
|
||||
debug(arguments);
|
||||
if (i === 1) {
|
||||
// 第1个标签
|
||||
assert.equal(sourcePosition, 5);
|
||||
assert.equal(position, 5);
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(html, '<A href="#">');
|
||||
assert.equal(isClosing, false);
|
||||
return '[link]';
|
||||
} else if (i === 2) {
|
||||
// 第2个标签
|
||||
assert.equal(sourcePosition, 20);
|
||||
assert.equal(position, 14);
|
||||
assert.equal(tag, 'a');
|
||||
assert.equal(html, '</A>');
|
||||
assert.equal(isClosing, true);
|
||||
return '[/link]';
|
||||
} else if (i === 3) {
|
||||
// 第3个标签
|
||||
assert.equal(sourcePosition, 27);
|
||||
assert.equal(position, 24);
|
||||
assert.equal(tag, 'b');
|
||||
assert.equal(html, '<b>');
|
||||
assert.equal(isClosing, false);
|
||||
return '[B]';
|
||||
} else if (i === 4) {
|
||||
// 第4个标签
|
||||
assert.equal(sourcePosition, 30);
|
||||
assert.equal(position, 27);
|
||||
assert.equal(tag, 'br');
|
||||
assert.equal(html, '<br/>');
|
||||
assert.equal(isClosing, false);
|
||||
return '[BR]';
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
}, escapeHtml);
|
||||
var html = parseTag(
|
||||
'hello<A href="#">www</A>ccc<b><br/>',
|
||||
function(sourcePosition, position, tag, html, isClosing) {
|
||||
i++;
|
||||
debug(arguments);
|
||||
if (i === 1) {
|
||||
// 第1个标签
|
||||
assert.equal(sourcePosition, 5);
|
||||
assert.equal(position, 5);
|
||||
assert.equal(tag, "a");
|
||||
assert.equal(html, '<A href="#">');
|
||||
assert.equal(isClosing, false);
|
||||
return "[link]";
|
||||
} else if (i === 2) {
|
||||
// 第2个标签
|
||||
assert.equal(sourcePosition, 20);
|
||||
assert.equal(position, 14);
|
||||
assert.equal(tag, "a");
|
||||
assert.equal(html, "</A>");
|
||||
assert.equal(isClosing, true);
|
||||
return "[/link]";
|
||||
} else if (i === 3) {
|
||||
// 第3个标签
|
||||
assert.equal(sourcePosition, 27);
|
||||
assert.equal(position, 24);
|
||||
assert.equal(tag, "b");
|
||||
assert.equal(html, "<b>");
|
||||
assert.equal(isClosing, false);
|
||||
return "[B]";
|
||||
} else if (i === 4) {
|
||||
// 第4个标签
|
||||
assert.equal(sourcePosition, 30);
|
||||
assert.equal(position, 27);
|
||||
assert.equal(tag, "br");
|
||||
assert.equal(html, "<br/>");
|
||||
assert.equal(isClosing, false);
|
||||
return "[BR]";
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
},
|
||||
escapeHtml
|
||||
);
|
||||
debug(html);
|
||||
assert.equal(html, 'hello[link]www[/link]ccc[B][BR]');
|
||||
assert.equal(html, "hello[link]www[/link]ccc[B][BR]");
|
||||
});
|
||||
|
||||
it('#parseAttr', function () {
|
||||
it("#parseAttr", function() {
|
||||
var i = 0;
|
||||
var html = parseAttr('href="#"attr1=b attr2=c attr3 attr4=\'value4"\'attr5/', function (name, value) {
|
||||
i++;
|
||||
debug(arguments);
|
||||
if (i === 1) {
|
||||
assert.equal(name, 'href');
|
||||
assert.equal(value, '#');
|
||||
return attr(name, value);
|
||||
} else if (i === 2) {
|
||||
assert.equal(name, 'attr1');
|
||||
assert.equal(value, 'b');
|
||||
return attr(name, value);
|
||||
} else if (i === 3) {
|
||||
assert.equal(name, 'attr2');
|
||||
assert.equal(value, 'c');
|
||||
return attr(name, value);
|
||||
} else if (i === 4) {
|
||||
assert.equal(name, 'attr3');
|
||||
assert.equal(value, '');
|
||||
return attr(name, value);
|
||||
} else if (i === 5) {
|
||||
assert.equal(name, 'attr4');
|
||||
assert.equal(value, 'value4"');
|
||||
return attr(name, value);
|
||||
} else if (i === 6) {
|
||||
assert.equal(name, 'attr5');
|
||||
assert.equal(value, '');
|
||||
return attr(name, value);
|
||||
} else {
|
||||
throw new Error();
|
||||
var html = parseAttr(
|
||||
'href="#"attr1=b attr2=c attr3 attr4=\'value4"\'attr5/',
|
||||
function(name, value) {
|
||||
i++;
|
||||
debug(arguments);
|
||||
if (i === 1) {
|
||||
assert.equal(name, "href");
|
||||
assert.equal(value, "#");
|
||||
return attr(name, value);
|
||||
} else if (i === 2) {
|
||||
assert.equal(name, "attr1");
|
||||
assert.equal(value, "b");
|
||||
return attr(name, value);
|
||||
} else if (i === 3) {
|
||||
assert.equal(name, "attr2");
|
||||
assert.equal(value, "c");
|
||||
return attr(name, value);
|
||||
} else if (i === 4) {
|
||||
assert.equal(name, "attr3");
|
||||
assert.equal(value, "");
|
||||
return attr(name, value);
|
||||
} else if (i === 5) {
|
||||
assert.equal(name, "attr4");
|
||||
assert.equal(value, 'value4"');
|
||||
return attr(name, value);
|
||||
} else if (i === 6) {
|
||||
assert.equal(name, "attr5");
|
||||
assert.equal(value, "");
|
||||
return attr(name, value);
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
debug(html);
|
||||
assert.equal(html, 'href="#" attr1="b" attr2="c" attr3 attr4="value4"e;" attr5');
|
||||
assert.equal(
|
||||
html,
|
||||
'href="#" attr1="b" attr2="c" attr3 attr4="value4"e;" attr5'
|
||||
);
|
||||
});
|
||||
|
||||
it('#parseTag & #parseAttr', function () {
|
||||
var html = parseTag('hi:<a href="#"target=_blank title="this is a link">link</a>', function (sourcePosition, position, tag, html, isClosing) {
|
||||
if (tag === 'a') {
|
||||
if (isClosing) return '</a>';
|
||||
var attrhtml = parseAttr(html.slice(2, -1), function (name, value) {
|
||||
if (name === 'href' || name === 'target') {
|
||||
return attr(name, value);
|
||||
}
|
||||
});
|
||||
return '<a ' + attrhtml + '>';
|
||||
} else {
|
||||
return escapeHtml(html);
|
||||
}
|
||||
}, escapeHtml);
|
||||
it("#parseTag & #parseAttr", function() {
|
||||
var html = parseTag(
|
||||
'hi:<a href="#"target=_blank title="this is a link">link</a>',
|
||||
function(sourcePosition, position, tag, html, isClosing) {
|
||||
if (tag === "a") {
|
||||
if (isClosing) return "</a>";
|
||||
var attrhtml = parseAttr(html.slice(2, -1), function(name, value) {
|
||||
if (name === "href" || name === "target") {
|
||||
return attr(name, value);
|
||||
}
|
||||
});
|
||||
return "<a " + attrhtml + ">";
|
||||
} else {
|
||||
return escapeHtml(html);
|
||||
}
|
||||
},
|
||||
escapeHtml
|
||||
);
|
||||
debug(html);
|
||||
assert.equal(html, 'hi:<a href="#" target="_blank">link</a>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
463
test/test_xss.js
463
test/test_xss.js
@@ -4,251 +4,413 @@
|
||||
* @author Zongmin Lei<leizongmin@gmail.com>
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
var _xss = require('../');
|
||||
var debug = require('debug')('xss:test');
|
||||
|
||||
var assert = require("assert");
|
||||
var _xss = require("../");
|
||||
var debug = require("debug")("xss:test");
|
||||
|
||||
function xss(html, options) {
|
||||
debug(JSON.stringify(html));
|
||||
var ret = _xss(html, options);
|
||||
debug('\t' + JSON.stringify(ret));
|
||||
debug("\t" + JSON.stringify(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
describe('test XSS', function () {
|
||||
|
||||
it('#normal', function () {
|
||||
|
||||
describe("test XSS", function() {
|
||||
it("#normal", function() {
|
||||
// 兼容各种奇葩输入
|
||||
assert.equal(xss(), '');
|
||||
assert.equal(xss(null), '');
|
||||
assert.equal(xss(123), '123');
|
||||
assert.equal(xss({ a: 1111 }), '[object Object]');
|
||||
assert.equal(xss(), "");
|
||||
assert.equal(xss(null), "");
|
||||
assert.equal(xss(123), "123");
|
||||
assert.equal(xss({ a: 1111 }), "[object Object]");
|
||||
|
||||
// 清除不可见字符
|
||||
assert.equal(xss('a\u0000\u0001\u0002\u0003\r\n b'), 'a\u0000\u0001\u0002\u0003\r\n b');
|
||||
assert.equal(xss('a\u0000\u0001\u0002\u0003\r\n b', { stripBlankChar: true }), 'a\r\n b');
|
||||
assert.equal(
|
||||
xss("a\u0000\u0001\u0002\u0003\r\n b"),
|
||||
"a\u0000\u0001\u0002\u0003\r\n b"
|
||||
);
|
||||
assert.equal(
|
||||
xss("a\u0000\u0001\u0002\u0003\r\n b", { stripBlankChar: true }),
|
||||
"a\r\n b"
|
||||
);
|
||||
|
||||
// 过滤不在白名单的标签
|
||||
assert.equal(xss('<b>abcd</b>'), '<b>abcd</b>');
|
||||
assert.equal(xss('<o>abcd</o>'), '<o>abcd</o>');
|
||||
assert.equal(xss('<b>abcd</o>'), '<b>abcd</o>');
|
||||
assert.equal(xss('<b><o>abcd</b></o>'), '<b><o>abcd</b></o>');
|
||||
assert.equal(xss('<hr>'), '<hr>');
|
||||
assert.equal(xss('<xss>'), '<xss>');
|
||||
assert.equal(xss("<b>abcd</b>"), "<b>abcd</b>");
|
||||
assert.equal(xss("<o>abcd</o>"), "<o>abcd</o>");
|
||||
assert.equal(xss("<b>abcd</o>"), "<b>abcd</o>");
|
||||
assert.equal(xss("<b><o>abcd</b></o>"), "<b><o>abcd</b></o>");
|
||||
assert.equal(xss("<hr>"), "<hr>");
|
||||
assert.equal(xss("<xss>"), "<xss>");
|
||||
assert.equal(xss('<xss o="x">'), '<xss o="x">');
|
||||
assert.equal(xss('<a><b>c</b></a>'), '<a><b>c</b></a>');
|
||||
assert.equal(xss('<a><c>b</c></a>'), '<a><c>b</c></a>');
|
||||
assert.equal(xss("<a><b>c</b></a>"), "<a><b>c</b></a>");
|
||||
assert.equal(xss("<a><c>b</c></a>"), "<a><c>b</c></a>");
|
||||
|
||||
// 过滤不是标签的<>
|
||||
assert.equal(xss('<>>'), '<>>');
|
||||
assert.equal(xss('<scri' + 'pt>'), '<script>');
|
||||
assert.equal(xss('<<a>b>'), '<<a>b>');
|
||||
assert.equal(xss('<<<a>>b</a><x>'), '<<<a>>b</a><x>');
|
||||
assert.equal(xss("<>>"), "<>>");
|
||||
assert.equal(xss("<scri" + "pt>"), "<script>");
|
||||
assert.equal(xss("<<a>b>"), "<<a>b>");
|
||||
assert.equal(xss("<<<a>>b</a><x>"), "<<<a>>b</a><x>");
|
||||
|
||||
// 过滤不在白名单中的属性
|
||||
assert.equal(xss('<a oo="1" xx="2" title="3">yy</a>'), '<a title="3">yy</a>');
|
||||
assert.equal(xss('<a title xx oo>pp</a>'), '<a title>pp</a>');
|
||||
assert.equal(xss('<a title "">pp</a>'), '<a title>pp</a>');
|
||||
assert.equal(xss('<a t="">'), '<a>');
|
||||
assert.equal(
|
||||
xss('<a oo="1" xx="2" title="3">yy</a>'),
|
||||
'<a title="3">yy</a>'
|
||||
);
|
||||
assert.equal(xss("<a title xx oo>pp</a>"), "<a title>pp</a>");
|
||||
assert.equal(xss('<a title "">pp</a>'), "<a title>pp</a>");
|
||||
assert.equal(xss('<a t="">'), "<a>");
|
||||
|
||||
// 属性内的特殊字符
|
||||
assert.equal(xss('<a title="\'<<>>">'), '<a title="\'<<>>">');
|
||||
assert.equal(xss('<a title=""">'), '<a title>');
|
||||
assert.equal(xss('<a h=title="oo">'), '<a>');
|
||||
assert.equal(xss('<a h= title="oo">'), '<a>');
|
||||
assert.equal(xss('<a title="javascript&colonalert(/xss/)">'), '<a title="javascript:alert(/xss/)">');
|
||||
assert.equal(xss('<a title"hell aa="fdfd title="ok">hello</a>'), '<a>hello</a>');
|
||||
assert.equal(xss('<a title=""">'), "<a title>");
|
||||
assert.equal(xss('<a h=title="oo">'), "<a>");
|
||||
assert.equal(xss('<a h= title="oo">'), "<a>");
|
||||
assert.equal(
|
||||
xss('<a title="javascript&colonalert(/xss/)">'),
|
||||
'<a title="javascript:alert(/xss/)">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<a title"hell aa="fdfd title="ok">hello</a>'),
|
||||
"<a>hello</a>"
|
||||
);
|
||||
|
||||
// 自动将属性值的单引号转为双引号
|
||||
assert.equal(xss('<a title=\'abcd\'>'), '<a title="abcd">');
|
||||
assert.equal(xss('<a title=\'"\'>'), '<a title=""">');
|
||||
assert.equal(xss("<a title='abcd'>"), '<a title="abcd">');
|
||||
assert.equal(xss("<a title='\"'>"), '<a title=""">');
|
||||
|
||||
// 没有双引号括起来的属性值
|
||||
assert.equal(xss('<a title=home>'), '<a title="home">');
|
||||
assert.equal(xss("<a title=home>"), '<a title="home">');
|
||||
assert.equal(xss('<a title=abc("d")>'), '<a title="abc("d")">');
|
||||
assert.equal(xss('<a title=abc(\'d\')>'), '<a title="abc(\'d\')">');
|
||||
assert.equal(xss("<a title=abc('d')>"), "<a title=\"abc('d')\">");
|
||||
|
||||
// 单个闭合标签
|
||||
assert.equal(xss('<img src/>'), '<img src />');
|
||||
assert.equal(xss('<img src />'), '<img src />');
|
||||
assert.equal(xss('<img src//>'), '<img src />');
|
||||
assert.equal(xss('<br/>'), '<br />');
|
||||
assert.equal(xss('<br />'), '<br />');
|
||||
assert.equal(xss("<img src/>"), "<img src />");
|
||||
assert.equal(xss("<img src />"), "<img src />");
|
||||
assert.equal(xss("<img src//>"), "<img src />");
|
||||
assert.equal(xss("<br/>"), "<br />");
|
||||
assert.equal(xss("<br />"), "<br />");
|
||||
|
||||
// 畸形属性格式
|
||||
assert.equal(xss('<a target = "_blank" title ="bbb">'), '<a target="_blank" title="bbb">');
|
||||
assert.equal(xss('<a target = "_blank" title = title = "bbb">'), '<a target="_blank" title="title">');
|
||||
assert.equal(xss('<img width = 100 height =200 title="xxx">'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title=xxx>'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title= xxx>'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title= "xxx">'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title= \'xxx\'>'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title = \'xxx\'>'),
|
||||
'<img width="100" height="200" title="xxx">');
|
||||
assert.equal(xss('<img width = 100 height =200 title= "xxx" no=yes alt="yyy">'),
|
||||
'<img width="100" height="200" title="xxx" alt="yyy">');
|
||||
assert.equal(xss('<img width = 100 height =200 title= "xxx" no=yes alt="\'yyy\'">'),
|
||||
'<img width="100" height="200" title="xxx" alt="\'yyy\'">');
|
||||
assert.equal(
|
||||
xss('<a target = "_blank" title ="bbb">'),
|
||||
'<a target="_blank" title="bbb">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<a target = "_blank" title = title = "bbb">'),
|
||||
'<a target="_blank" title="title">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<img width = 100 height =200 title="xxx">'),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss("<img width = 100 height =200 title=xxx>"),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss("<img width = 100 height =200 title= xxx>"),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<img width = 100 height =200 title= "xxx">'),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss("<img width = 100 height =200 title= 'xxx'>"),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss("<img width = 100 height =200 title = 'xxx'>"),
|
||||
'<img width="100" height="200" title="xxx">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<img width = 100 height =200 title= "xxx" no=yes alt="yyy">'),
|
||||
'<img width="100" height="200" title="xxx" alt="yyy">'
|
||||
);
|
||||
assert.equal(
|
||||
xss(
|
||||
'<img width = 100 height =200 title= "xxx" no=yes alt="\'yyy\'">'
|
||||
),
|
||||
'<img width="100" height="200" title="xxx" alt="\'yyy\'">'
|
||||
);
|
||||
|
||||
// 使用Tab或换行符分隔的属性
|
||||
assert.equal(xss('<img width=100 height=200\nsrc="#"/>'), '<img width="100" height="200" src="#" />');
|
||||
assert.equal(xss('<a\ttarget="_blank"\ntitle="bbb">'), '<a target="_blank" title="bbb">');
|
||||
assert.equal(xss('<a\ntarget="_blank"\ttitle="bbb">'), '<a target="_blank" title="bbb">');
|
||||
assert.equal(xss('<a\n\n\n\ttarget="_blank"\t\t\t\ntitle="bbb">'), '<a target="_blank" title="bbb">');
|
||||
|
||||
assert.equal(
|
||||
xss('<img width=100 height=200\nsrc="#"/>'),
|
||||
'<img width="100" height="200" src="#" />'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<a\ttarget="_blank"\ntitle="bbb">'),
|
||||
'<a target="_blank" title="bbb">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<a\ntarget="_blank"\ttitle="bbb">'),
|
||||
'<a target="_blank" title="bbb">'
|
||||
);
|
||||
assert.equal(
|
||||
xss('<a\n\n\n\ttarget="_blank"\t\t\t\ntitle="bbb">'),
|
||||
'<a target="_blank" title="bbb">'
|
||||
);
|
||||
});
|
||||
|
||||
// 自定义白名单
|
||||
it('#white list', function () {
|
||||
|
||||
it("#white list", function() {
|
||||
// 过滤所有标签
|
||||
assert.equal(xss('<a title="xx">bb</a>', { whiteList: {} }), '<a title="xx">bb</a>');
|
||||
assert.equal(xss('<hr>', { whiteList: {} }), '<hr>');
|
||||
assert.equal(
|
||||
xss('<a title="xx">bb</a>', { whiteList: {} }),
|
||||
'<a title="xx">bb</a>'
|
||||
);
|
||||
assert.equal(xss("<hr>", { whiteList: {} }), "<hr>");
|
||||
// 增加白名单标签及属性
|
||||
assert.equal(xss('<ooxx yy="ok" cc="no">uu</ooxx>', { whiteList: { ooxx: ['yy'] } }), '<ooxx yy="ok">uu</ooxx>');
|
||||
|
||||
assert.equal(
|
||||
xss('<ooxx yy="ok" cc="no">uu</ooxx>', { whiteList: { ooxx: ["yy"] } }),
|
||||
'<ooxx yy="ok">uu</ooxx>'
|
||||
);
|
||||
});
|
||||
|
||||
// XSS攻击测试:https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
||||
it('#XSS_Filter_Evasion_Cheat_Sheet', function () {
|
||||
|
||||
assert.equal(xss('></SCRI' + 'PT>">\'><SCRI' + 'PT>alert(String.fromCharCode(88,83,83))</SCRI' + 'PT>'),
|
||||
'></SCRIPT>">\'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>');
|
||||
it("#XSS_Filter_Evasion_Cheat_Sheet", function() {
|
||||
assert.equal(
|
||||
xss(
|
||||
"></SCRI" +
|
||||
"PT>\">'><SCRI" +
|
||||
"PT>alert(String.fromCharCode(88,83,83))</SCRI" +
|
||||
"PT>"
|
||||
),
|
||||
"></SCRIPT>\">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>"
|
||||
);
|
||||
|
||||
assert.equal(xss(';!--"<XSS>=&{()}'), ';!--"<XSS>=&{()}');
|
||||
|
||||
assert.equal(xss('<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRI' + 'PT>'),
|
||||
'<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>');
|
||||
assert.equal(
|
||||
xss("<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRI" + "PT>"),
|
||||
"<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC="javascript:alert(\'XSS\');">'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC=\"javascript:alert('XSS');\">"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC=javascript:alert(\'XSS\')>'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC=javascript:alert('XSS')>"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC=JaVaScRiPt:alert(\'XSS\')>'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC=JaVaScRiPt:alert('XSS')>"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC=`javascript:alert("RSnake says, \'XSS\'")`>'), '<img src>');
|
||||
assert.equal(
|
||||
xss("<IMG SRC=`javascript:alert(\"RSnake says, 'XSS'\")`>"),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG """><SCRI' + 'PT>alert("XSS")</SCRI' + 'PT>">'), '<img><SCRIPT>alert("XSS")</SCRIPT>">');
|
||||
assert.equal(
|
||||
xss('<IMG """><SCRI' + 'PT>alert("XSS")</SCRI' + 'PT>">'),
|
||||
'<img><SCRIPT>alert("XSS")</SCRIPT>">'
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>'), '<img src>');
|
||||
assert.equal(
|
||||
xss("<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>"),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC=javascript:alert('XSS')>'),
|
||||
'<img src>');
|
||||
assert.equal(
|
||||
xss(
|
||||
"<IMG SRC=javascript:alert('XSS')>"
|
||||
),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC=javascript:alert('XSS')>'),
|
||||
'<img src>');
|
||||
assert.equal(
|
||||
xss(
|
||||
"<IMG SRC=javascript:alert('XSS')>"
|
||||
),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC=javascript:alert('XSS')>'),
|
||||
'<img src>');
|
||||
assert.equal(
|
||||
xss(
|
||||
"<IMG SRC=javascript:alert('XSS')>"
|
||||
),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC="jav ascript:alert(\'XSS\');">'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC=\"jav ascript:alert('XSS');\">"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC="jav	ascript:alert(\'XSS\');">'), '<img src>');
|
||||
assert.equal(
|
||||
xss("<IMG SRC=\"jav	ascript:alert('XSS');\">"),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC="jav\nascript:alert(\'XSS\');">'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC=\"jav\nascript:alert('XSS');\">"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC=java\0script:alert(\"XSS\")>'), '<img src>');
|
||||
assert.equal(xss('<IMG SRC=java\0script:alert("XSS")>'), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC="  javascript:alert(\'XSS\');">'), '<img src>');
|
||||
assert.equal(
|
||||
xss("<IMG SRC=\"  javascript:alert('XSS');\">"),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
assert.equal(xss('<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRI' + 'PT>'),
|
||||
'<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>');
|
||||
assert.equal(
|
||||
xss('<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRI' + "PT>"),
|
||||
'<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>'
|
||||
);
|
||||
|
||||
assert.equal(xss('<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>'),
|
||||
'<BODY onload!#$%&()*~+-_.,:;?@[/|]^`=alert(\"XSS\")>');
|
||||
assert.equal(
|
||||
xss('<BODY onload!#$%&()*~+-_.,:;?@[/|]^`=alert("XSS")>'),
|
||||
'<BODY onload!#$%&()*~+-_.,:;?@[/|]^`=alert("XSS")>'
|
||||
);
|
||||
|
||||
assert.equal(xss('<<SCRI' + 'PT>alert("XSS");//<</SCRI' + 'PT>'),
|
||||
'<<SCRIPT>alert(\"XSS\");//<</SCRIPT>');
|
||||
assert.equal(
|
||||
xss("<<SCRI" + 'PT>alert("XSS");//<</SCRI' + "PT>"),
|
||||
'<<SCRIPT>alert("XSS");//<</SCRIPT>'
|
||||
);
|
||||
|
||||
assert.equal(xss('<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >'),
|
||||
'<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >');
|
||||
assert.equal(
|
||||
xss("<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"),
|
||||
"<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >"
|
||||
);
|
||||
|
||||
assert.equal(xss('<SCRIPT SRC=//ha.ckers.org/.j'),
|
||||
'<SCRIPT SRC=//ha.ckers.org/.j');
|
||||
assert.equal(
|
||||
xss("<SCRIPT SRC=//ha.ckers.org/.j"),
|
||||
"<SCRIPT SRC=//ha.ckers.org/.j"
|
||||
);
|
||||
|
||||
assert.equal(xss('<ſcript src="https://xss.haozi.me/j.js"></ſcript>'),
|
||||
'<ſcript src="https://xss.haozi.me/j.js"></ſcript>');
|
||||
assert.equal(
|
||||
xss('<ſcript src="https://xss.haozi.me/j.js"></ſcript>'),
|
||||
'<ſcript src="https://xss.haozi.me/j.js"></ſcript>'
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC="javascript:alert(\'XSS\')"'),
|
||||
'<IMG SRC=\"javascript:alert(\'XSS\')"');
|
||||
assert.equal(
|
||||
xss("<IMG SRC=\"javascript:alert('XSS')\""),
|
||||
"<IMG SRC=\"javascript:alert('XSS')\""
|
||||
);
|
||||
|
||||
assert.equal(xss('<iframe src=http://ha.ckers.org/scriptlet.html <'),
|
||||
'<iframe src=http://ha.ckers.org/scriptlet.html <');
|
||||
assert.equal(
|
||||
xss("<iframe src=http://ha.ckers.org/scriptlet.html <"),
|
||||
"<iframe src=http://ha.ckers.org/scriptlet.html <"
|
||||
);
|
||||
|
||||
// 过滤 javascript:
|
||||
assert.equal(xss('<a style="url(\'javascript:alert(1)\')">', { whiteList: { a: ['style'] } }), '<a style>');
|
||||
assert.equal(xss('<td background="url(\'javascript:alert(1)\')">', { whiteList: { td: ['background'] } }), '<td background>');
|
||||
assert.equal(
|
||||
xss("<a style=\"url('javascript:alert(1)')\">", {
|
||||
whiteList: { a: ["style"] }
|
||||
}),
|
||||
"<a style>"
|
||||
);
|
||||
assert.equal(
|
||||
xss("<td background=\"url('javascript:alert(1)')\">", {
|
||||
whiteList: { td: ["background"] }
|
||||
}),
|
||||
"<td background>"
|
||||
);
|
||||
|
||||
// 过滤 style
|
||||
assert.equal(xss('<DIV STYLE="width: \nexpression(alert(1));">', { whiteList: { div: ['style'] } }), '<div style>');
|
||||
assert.equal(
|
||||
xss('<DIV STYLE="width: \nexpression(alert(1));">', {
|
||||
whiteList: { div: ["style"] }
|
||||
}),
|
||||
"<div style>"
|
||||
);
|
||||
// 不正常的url
|
||||
assert.equal(xss('<DIV STYLE="background:\n url (javascript:ooxx);">', { whiteList: { div: ['style'] } }), '<div style>');
|
||||
assert.equal(xss('<DIV STYLE="background:url (javascript:ooxx);">', { whiteList: { div: ['style'] } }), '<div style>');
|
||||
assert.equal(
|
||||
xss('<DIV STYLE="background:\n url (javascript:ooxx);">', {
|
||||
whiteList: { div: ["style"] }
|
||||
}),
|
||||
"<div style>"
|
||||
);
|
||||
assert.equal(
|
||||
xss('<DIV STYLE="background:url (javascript:ooxx);">', {
|
||||
whiteList: { div: ["style"] }
|
||||
}),
|
||||
"<div style>"
|
||||
);
|
||||
// 正常的url
|
||||
assert.equal(xss('<DIV STYLE="background: url (ooxx);">', { whiteList: { div: ['style'] } }), '<div style="background:url (ooxx);">');
|
||||
assert.equal(
|
||||
xss('<DIV STYLE="background: url (ooxx);">', {
|
||||
whiteList: { div: ["style"] }
|
||||
}),
|
||||
'<div style="background:url (ooxx);">'
|
||||
);
|
||||
|
||||
assert.equal(xss('<IMG SRC=\'vbscript:msgbox("XSS")\'>'), '<img src>');
|
||||
assert.equal(xss("<IMG SRC='vbscript:msgbox(\"XSS\")'>"), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC="livescript:[code]">'), '<img src>');
|
||||
assert.equal(xss('<IMG SRC="livescript:[code]">'), "<img src>");
|
||||
|
||||
assert.equal(xss('<IMG SRC="mocha:[code]">'), '<img src>');
|
||||
assert.equal(xss('<IMG SRC="mocha:[code]">'), "<img src>");
|
||||
|
||||
assert.equal(xss('<a href="javas/**/cript:alert(\'XSS\');">'), '<a href>');
|
||||
assert.equal(xss("<a href=\"javas/**/cript:alert('XSS');\">"), "<a href>");
|
||||
|
||||
assert.equal(xss('<a href="javascript">'), '<a href>');
|
||||
assert.equal(xss('<a href="javascript">'), "<a href>");
|
||||
assert.equal(xss('<a href="/javascript/a">'), '<a href="/javascript/a">');
|
||||
assert.equal(xss('<a href="/javascript/a">'), '<a href="/javascript/a">');
|
||||
assert.equal(xss('<a href="http://aa.com">'), '<a href="http://aa.com">');
|
||||
assert.equal(xss('<a href="https://aa.com">'), '<a href="https://aa.com">');
|
||||
assert.equal(xss('<a href="mailto:me@ucdok.com">'), '<a href="mailto:me@ucdok.com">');
|
||||
assert.equal(
|
||||
xss('<a href="mailto:me@ucdok.com">'),
|
||||
'<a href="mailto:me@ucdok.com">'
|
||||
);
|
||||
assert.equal(xss('<a href="tel:0123456789">'), '<a href="tel:0123456789">');
|
||||
assert.equal(xss('<a href="#hello">'), '<a href="#hello">');
|
||||
assert.equal(xss('<a href="other">'), '<a href>');
|
||||
assert.equal(xss('<a href="other">'), "<a href>");
|
||||
|
||||
// 这个暂时不知道怎么处理
|
||||
//assert.equal(xss('¼script¾alert(¢XSS¢)¼/script¾'), '');
|
||||
|
||||
assert.equal(xss('<!--[if gte IE 4]><SCRI' + 'PT>alert(\'XSS\');</SCRI' + 'PT><![endif]--> END', { allowCommentTag: true }),
|
||||
'<!--[if gte IE 4]><SCRIPT>alert(\'XSS\');</SCRIPT><![endif]--> END');
|
||||
assert.equal(xss('<!--[if gte IE 4]><SCRI' + 'PT>alert(\'XSS\');</SCRI' + 'PT><![endif]--> END'), ' END');
|
||||
assert.equal(
|
||||
xss(
|
||||
"<!--[if gte IE 4]><SCRI" +
|
||||
"PT>alert('XSS');</SCRI" +
|
||||
"PT><![endif]--> END",
|
||||
{ allowCommentTag: true }
|
||||
),
|
||||
"<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]--> END"
|
||||
);
|
||||
assert.equal(
|
||||
xss(
|
||||
"<!--[if gte IE 4]><SCRI" +
|
||||
"PT>alert('XSS');</SCRI" +
|
||||
"PT><![endif]--> END"
|
||||
),
|
||||
" END"
|
||||
);
|
||||
|
||||
// HTML5新增实体编码 冒号: 换行

|
||||
assert.equal(xss('<a href="javascript:alert(/xss/)">'), '<a href>');
|
||||
assert.equal(xss('<a href="javascript&colonalert(/xss/)">'), '<a href>');
|
||||
assert.equal(xss('<a href="a
b">'), '<a href>');
|
||||
assert.equal(xss('<a href="a&NewLineb">'), '<a href>');
|
||||
assert.equal(xss('<a href="javasc
ript:alert(1)">'), '<a href>');
|
||||
assert.equal(xss('<a href="javascript:alert(/xss/)">'), "<a href>");
|
||||
assert.equal(xss('<a href="javascript&colonalert(/xss/)">'), "<a href>");
|
||||
assert.equal(xss('<a href="a
b">'), "<a href>");
|
||||
assert.equal(xss('<a href="a&NewLineb">'), "<a href>");
|
||||
assert.equal(
|
||||
xss('<a href="javasc
ript:alert(1)">'),
|
||||
"<a href>"
|
||||
);
|
||||
|
||||
// data URI 协议过滤
|
||||
assert.equal(xss('<a href="data:">'), '<a href>');
|
||||
assert.equal(xss('<a href="d a t a : ">'), '<a href>');
|
||||
assert.equal(xss('<a href="data: html/text;">'), '<a href>');
|
||||
assert.equal(xss('<a href="data:html/text;">'), '<a href>');
|
||||
assert.equal(xss('<a href="data:html /text;">'), '<a href>');
|
||||
assert.equal(xss('<a href="data: image/text;">'), '<a href>');
|
||||
assert.equal(xss('<img src="data: aaa/text;">'), '<img src>');
|
||||
assert.equal(xss('<img src="data:image/png; base64; ofdkofiodiofl">'), '<img src>');
|
||||
assert.equal(xss('<a href="data:">'), "<a href>");
|
||||
assert.equal(xss('<a href="d a t a : ">'), "<a href>");
|
||||
assert.equal(xss('<a href="data: html/text;">'), "<a href>");
|
||||
assert.equal(xss('<a href="data:html/text;">'), "<a href>");
|
||||
assert.equal(xss('<a href="data:html /text;">'), "<a href>");
|
||||
assert.equal(xss('<a href="data: image/text;">'), "<a href>");
|
||||
assert.equal(xss('<img src="data: aaa/text;">'), "<img src>");
|
||||
assert.equal(
|
||||
xss('<img src="data:image/png; base64; ofdkofiodiofl">'),
|
||||
"<img src>"
|
||||
);
|
||||
|
||||
// HTML备注处理
|
||||
assert.equal(xss('<!-- -->', { allowCommentTag: false }), '');
|
||||
assert.equal(xss('<!-- a -->', { allowCommentTag: false }), '');
|
||||
assert.equal(xss('<!--sa -->ss', { allowCommentTag: false }), 'ss');
|
||||
assert.equal(xss('<!-- ', { allowCommentTag: false }), '<!-- ');
|
||||
|
||||
assert.equal(
|
||||
xss("<!-- -->", { allowCommentTag: false }),
|
||||
""
|
||||
);
|
||||
assert.equal(
|
||||
xss("<!-- a -->", { allowCommentTag: false }),
|
||||
""
|
||||
);
|
||||
assert.equal(xss("<!--sa -->ss", { allowCommentTag: false }), "ss");
|
||||
assert.equal(
|
||||
xss("<!-- ", { allowCommentTag: false }),
|
||||
"<!-- "
|
||||
);
|
||||
});
|
||||
|
||||
it('no options mutated', function () {
|
||||
it("no options mutated", function() {
|
||||
var options = {};
|
||||
|
||||
var ret = xss('test', options);
|
||||
var ret = xss("test", options);
|
||||
// console.log(options);
|
||||
assert.deepEqual(options, {});
|
||||
|
||||
@@ -256,5 +418,4 @@ describe('test XSS', function () {
|
||||
// console.log(options);
|
||||
assert.deepEqual(options, {});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user