422 lines
13 KiB
JavaScript
422 lines
13 KiB
JavaScript
|
/**
|
|||
|
* tests for xss() function
|
|||
|
*
|
|||
|
* @author Zongmin Lei<leizongmin@gmail.com>
|
|||
|
*/
|
|||
|
|
|||
|
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));
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
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("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('<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("<>>"), "<>>");
|
|||
|
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 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='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=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('<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">'
|
|||
|
);
|
|||
|
});
|
|||
|
|
|||
|
// 自定义白名单
|
|||
|
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('<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>"
|
|||
|
);
|
|||
|
|
|||
|
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("<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 """><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('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\nascript: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('<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("<<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=//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("<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 <"
|
|||
|
);
|
|||
|
|
|||
|
// 过滤 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>"
|
|||
|
);
|
|||
|
|
|||
|
// 过滤 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>"
|
|||
|
);
|
|||
|
// 正常的url
|
|||
|
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="livescript:[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="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="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('¼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"
|
|||
|
);
|
|||
|
|
|||
|
// 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>"
|
|||
|
);
|
|||
|
|
|||
|
// 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="data:image/png; base64; ofdkofiodiofl">'
|
|||
|
);
|
|||
|
|
|||
|
// 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 }),
|
|||
|
""
|
|||
|
);
|
|||
|
});
|
|||
|
|
|||
|
it("no options mutated", function() {
|
|||
|
var options = {};
|
|||
|
|
|||
|
var ret = xss("test", options);
|
|||
|
// console.log(options);
|
|||
|
assert.deepEqual(options, {});
|
|||
|
|
|||
|
var ret2 = new _xss.FilterXSS(options);
|
|||
|
// console.log(options);
|
|||
|
assert.deepEqual(options, {});
|
|||
|
});
|
|||
|
});
|