🚀 Big News:Socket Has Acquired Secure Annex.Learn More
Socket
Book a DemoSign in
Socket

xss

Package Overview
Dependencies
Maintainers
1
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xss - npm Package Compare versions

Comparing version
0.3.5
to
0.3.6
+23
LICENSE
Copyright (c) 2012-2017 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
http://ucdok.com
The MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+348
-351
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/**
* 默认配置
* default settings
*
* @author 老雷<leizongmin@gmail.com>
* @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"]
};
}
// 默认CSS Filter
var defaultCSSFilter = new FilterCSS();
/**
* 匹配到标签时的处理方法
* default onTag function
*

@@ -92,3 +90,3 @@ * @param {String} tag

*/
function onTag (tag, html, options) {
function onTag(tag, html, options) {
// do nothing

@@ -98,3 +96,3 @@ }

/**
* 匹配到不在白名单上的标签时的处理方法
* default onIgnoreTag function
*

@@ -106,3 +104,3 @@ * @param {String} tag

*/
function onIgnoreTag (tag, html, options) {
function onIgnoreTag(tag, html, options) {
// do nothing

@@ -112,3 +110,3 @@ }

/**
* 匹配到标签属性时的处理方法
* default onTagAttr function
*

@@ -120,3 +118,3 @@ * @param {String} tag

*/
function onTagAttr (tag, name, value) {
function onTagAttr(tag, name, value) {
// do nothing

@@ -126,3 +124,3 @@ }

/**
* 匹配到不在白名单上的标签属性时的处理方法
* default onIgnoreTagAttr function
*

@@ -134,3 +132,3 @@ * @param {String} tag

*/
function onIgnoreTagAttr (tag, name, value) {
function onIgnoreTagAttr(tag, name, value) {
// do nothing

@@ -140,12 +138,12 @@ }

/**
* HTML转义
* default escapeHtml function
*
* @param {String} html
*/
function escapeHtml (html) {
return html.replace(REGEXP_LT, '&lt;').replace(REGEXP_GT, '&gt;');
function escapeHtml(html) {
return html.replace(REGEXP_LT, "&lt;").replace(REGEXP_GT, "&gt;");
}
/**
* 安全的标签属性值
* default safeAttrValue function
*

@@ -158,38 +156,37 @@ * @param {String} tag

*/
function safeAttrValue (tag, name, value, cssFilter) {
// 转换为友好的属性值,再做判断
function safeAttrValue(tag, name, value, cssFilter) {
// unescape attribute value firstly
value = friendlyAttrValue(value);
if (name === 'href' || name === 'src') {
// 过滤 href 和 src 属性
// 仅允许 http:// | https:// | mailto: | / | # 开头的地址
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') {
// 过滤 background 属性 (这个xss漏洞较老了,可能已经不适用)
// javascript:
} 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') {
// /*注释*/
/*REGEXP_DEFAULT_ON_TAG_ATTR_3.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_3.test(value)) {
return '';
}*/
// expression()
} 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()
// `url()`
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;

@@ -199,3 +196,3 @@ if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {

if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
return '';
return "";
}

@@ -209,3 +206,3 @@ }

// 输出时需要转义<>"
// escape `<>"` before returns
value = escapeAttrValue(value);

@@ -215,3 +212,3 @@ return value;

// 正则表达式
// RegExp list
var REGEXP_LT = /</g;

@@ -221,14 +218,14 @@ var REGEXP_GT = />/g;

var REGEXP_QUOTE_2 = /&quot;/g;
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/img;
var REGEXP_ATTR_VALUE_COLON = /&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 = /&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
*

@@ -238,8 +235,8 @@ * @param {String} str

*/
function escapeQuote (str) {
return str.replace(REGEXP_QUOTE, '&quot;');
function escapeQuote(str) {
return str.replace(REGEXP_QUOTE, "&quot;");
}
/**
* 对双引号进行转义
* unescape double quote
*

@@ -249,3 +246,3 @@ * @param {String} str

*/
function unescapeQuote (str) {
function unescapeQuote(str) {
return str.replace(REGEXP_QUOTE_2, '"');

@@ -255,3 +252,3 @@ }

/**
* 对html实体编码进行转义
* escape html entities
*

@@ -261,7 +258,7 @@ * @param {String} str

*/
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));
});

@@ -271,3 +268,3 @@ }

/**
* 对html5新增的危险实体编码进行转义
* escape html5 new danger entities
*

@@ -277,9 +274,10 @@ * @param {String} str

*/
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, " ");
}
/**
* 清除不可见字符
* clear nonprintable characters
*

@@ -289,6 +287,6 @@ * @param {String} str

*/
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);
}

@@ -299,3 +297,3 @@ return _.trim(str2);

/**
* 将标签的属性值转换成一般字符,便于分析
* get friendly attribute value
*

@@ -305,7 +303,7 @@ * @param {String} str

*/
function friendlyAttrValue (str) {
str = unescapeQuote(str); // 双引号
str = escapeHtmlEntities(str); // 转换HTML实体编码
str = escapeDangerHtml5Entities(str); // 转换危险的HTML5新增实体编码
str = clearNonPrintableCharacter(str); // 清除不可见字符
function friendlyAttrValue(str) {
str = unescapeQuote(str);
str = escapeHtmlEntities(str);
str = escapeDangerHtml5Entities(str);
str = clearNonPrintableCharacter(str);
return str;

@@ -315,3 +313,3 @@ }

/**
* 转义用于输出的标签属性值
* unescape attribute value
*

@@ -321,3 +319,3 @@ * @param {String} str

*/
function escapeAttrValue (str) {
function escapeAttrValue(str) {
str = escapeQuote(str);

@@ -329,35 +327,39 @@ str = escapeHtml(str);

/**
* 去掉不在白名单中的标签onIgnoreTag处理方法
* `onIgnoreTag` function for removing all the tags that are not in whitelist
*/
function onIgnoreTagStripAll () {
return '';
function onIgnoreTagStripAll() {
return "";
}
/**
* 删除标签体
* remove tag body
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
*
* @param {array} tags 要删除的标签列表
* @param {function} next 对不在列表中的标签的处理函数,可选
* @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; // 当前标签开始位置
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;

@@ -369,3 +371,3 @@ return ret;

}
return '[removed]';
return "[removed]";
}

@@ -376,6 +378,6 @@ } else {

},
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]);

@@ -391,3 +393,3 @@ lastPos = pos[1];

/**
* 去除备注标签
* remove html comments
*

@@ -397,4 +399,4 @@ * @param {String} html

*/
function stripCommentTag (html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP, '');
function stripCommentTag(html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP, "");
}

@@ -404,3 +406,3 @@ var STRIP_COMMENT_TAG_REGEXP = /<!--[\s\S]*?-->/g;

/**
* 去除不可见字符
* remove invisible characters
*

@@ -410,5 +412,5 @@ * @param {String} html

*/
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);

@@ -422,6 +424,5 @@ if (c === 127) return false;

});
return chars.join('');
return chars.join("");
}
exports.whiteList = getDefaultWhiteList();

@@ -451,20 +452,19 @@ exports.getDefaultWhiteList = getDefaultWhiteList;

/**
* 模块入口
* xss
*
* @author 老雷<leizongmin@gmail.com>
* @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");
/**
* XSS过滤
* filter xss function
*
* @param {String} html 要过滤的HTML代码
* @param {Object} options 选项:whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml
* @param {String} html
* @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);

@@ -474,4 +474,2 @@ return xss.process(html);

// 输出
exports = module.exports = filterXSS;

@@ -482,5 +480,4 @@ exports.FilterXSS = FilterXSS;

// 在浏览器端使用
if (typeof window !== 'undefined') {
// using `xss` on the browser, output `filterXSS` to the globals
if (typeof window !== "undefined") {
window.filterXSS = module.exports;

@@ -491,16 +488,16 @@ }

/**
* 简单 HTML Parser
* Simple HTML Parser
*
* @author 老雷<leizongmin@gmail.com>
* @author Zongmin Lei<leizongmin@gmail.com>
*/
var _ = require('./util');
var _ = require("./util");
/**
* 获取标签的名称
* get tag name
*
* @param {String} html 如:'<a hef="#">'
* @param {String} html e.g. '<a hef="#">'
* @return {String}
*/
function getTagName (html) {
function getTagName(html) {
var i = _.spaceIndex(html);

@@ -513,4 +510,4 @@ if (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;

@@ -520,3 +517,3 @@ }

/**
* 是否为闭合标签
* is close tag?
*

@@ -526,32 +523,29 @@ * @param {String} html 如:'<a hef="#">'

*/
function isClosing (html) {
return (html.slice(0, 2) === '</');
function isClosing(html) {
return html.slice(0, 2) === "</";
}
/**
* 分析HTML代码,调用相应的函数处理,返回处理后的HTML
* parse input html and returns processed html
*
* @param {String} html
* @param {Function} onTag 处理标签的函数
* 参数格式: function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml 对HTML进行转义的函数
* @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml
* @return {String}
*/
function parseTag (html, onTag, escapeHtml) {
'user strict';
function parseTag(html, onTag, escapeHtml) {
"user strict";
var rethtml = ''; // 待返回的HTML
var lastPos = 0; // 上一个标签结束位置
var tagStart = false; // 当前标签开始位置
var quoteStart = false; // 引号开始位置
var currentPos = 0; // 当前位置
var len = html.length; // HTML长度
var currentHtml = ''; // 当前标签的HTML代码
var currentTagName = ''; // 当前标签的名称
var rethtml = "";
var lastPos = 0;
var tagStart = false;
var quoteStart = false;
var currentPos = 0;
var len = html.length;
var currentTagName = "";
// 逐个分析字符
for (currentPos = 0; currentPos < len; currentPos++) {
var c = html.charAt(currentPos);
if (tagStart === false) {
if (c === '<') {
if (c === "<") {
tagStart = currentPos;

@@ -562,3 +556,3 @@ continue;

if (quoteStart === false) {
if (c === '<') {
if (c === "<") {
rethtml += escapeHtml(html.slice(lastPos, currentPos));

@@ -569,11 +563,13 @@ tagStart = currentPos;

}
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;

@@ -583,4 +579,3 @@ tagStart = false;

}
// HTML标签内的引号仅当前一个字符是等于号时才有效
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === '=') {
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === "=") {
quoteStart = c;

@@ -604,28 +599,26 @@ continue;

// 不符合属性名称规则的正则表达式
var REGEXP_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img;
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/gim;
/**
* 分析标签HTML代码,调用相应的函数处理,返回HTML
* parse input attributes and returns processed attributes
*
* @param {String} html 如标签'<a href="#" target="_blank">' 则为 'href="#" target="_blank"'
* @param {Function} onAttr 处理属性值的函数
* 函数格式: function (name, value)
* @param {String} html e.g. `href="#" target="_blank"`
* @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; // HTML代码长度
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_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);
};
}

@@ -636,3 +629,3 @@ // 逐个分析字符

var v, j;
if (tmpName === false && c === '=') {
if (tmpName === false && c === "=") {
tmpName = html.slice(lastPos, i);

@@ -643,4 +636,7 @@ lastPos = i + 1;

if (tmpName !== false) {
// HTML标签内的引号仅当前一个字符是等于号时才有效
if (i === lastPos && (c === '"' || c === "'") && html.charAt(i - 1) === '=') {
if (
i === lastPos &&
(c === '"' || c === "'") &&
html.charAt(i - 1) === "="
) {
j = html.indexOf(c, i + 1);

@@ -660,3 +656,3 @@ if (j === -1) {

if (/\s|\n|\t/.test(c)) {
html = html.replace(/\s|\n|\t/g, ' ');
html = html.replace(/\s|\n|\t/g, " ");
if (tmpName === false) {

@@ -698,10 +694,10 @@ j = findNextEqual(html, i);

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;

@@ -711,7 +707,7 @@ }

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;

@@ -721,5 +717,7 @@ }

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;

@@ -729,5 +727,5 @@ } else {

}
};
}
function stripQuoteWrap (text) {
function stripQuoteWrap(text) {
if (isQuoteWrapString(text)) {

@@ -738,5 +736,4 @@ return text.substr(1, text.length - 2);

}
};
}
exports.parseTag = parseTag;

@@ -747,3 +744,3 @@ exports.parseAttr = parseAttr;

module.exports = {
indexOf: function (arr, item) {
indexOf: function(arr, item) {
var i, j;

@@ -760,3 +757,3 @@ if (Array.prototype.indexOf) {

},
forEach: function (arr, fn, scope) {
forEach: function(arr, fn, scope) {
var i, j;

@@ -770,12 +767,12 @@ if (Array.prototype.forEach) {

},
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;
}

@@ -786,17 +783,16 @@ };

/**
* 过滤XSS
* filter xss
*
* @author 老雷<leizongmin@gmail.com>
* @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`
*

@@ -806,8 +802,8 @@ * @param {Object} obj

*/
function isNull (obj) {
return (obj === undefined || obj === null);
function isNull(obj) {
return obj === undefined || obj === null;
}
/**
* 取标签内的属性列表字符串
* get attributes for a tag
*

@@ -819,15 +815,15 @@ * @param {String} html

*/
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

@@ -838,3 +834,3 @@ };

/**
* 浅拷贝对象
* shallow copy
*

@@ -844,3 +840,3 @@ * @param {Object} obj

*/
function shallowCopyObject (obj) {
function shallowCopyObject(obj) {
var ret = {};

@@ -854,11 +850,11 @@ for (var i in obj) {

/**
* XSS过滤对象
* FilterXSS class
*
* @param {Object} options
* 选项:whiteList, onTag, onTagAttr, onIgnoreTag,
* whiteList, onTag, onTagAttr, onIgnoreTag,
* onIgnoreTagAttr, safeAttrValue, escapeHtml
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
* css{whiteList, onAttr, onIgnoreAttr} css=false表示禁用cssfilter
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
*/
function FilterXSS (options) {
function FilterXSS(options) {
options = shallowCopyObject(options || {});

@@ -868,3 +864,5 @@

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'
);
}

@@ -892,3 +890,3 @@ options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;

/**
* 开始处理
* start process and returns result
*

@@ -898,7 +896,7 @@ * @param {String} html

*/
FilterXSS.prototype.process = function (html) {
// 兼容各种奇葩输入
html = html || '';
FilterXSS.prototype.process = function(html) {
// compatible with the input
html = html || "";
html = html.toString();
if (!html) return '';
if (!html) return "";

@@ -916,3 +914,3 @@ var me = this;

// 是否清除不可见字符
// remove invisible characters
if (options.stripBlankChar) {

@@ -922,3 +920,3 @@ html = DEFAULT.stripBlankChar(html);

// 是否禁止备注标签
// remove html comments
if (!options.allowCommentTag) {

@@ -928,72 +926,72 @@ html = DEFAULT.stripCommentTag(html);

// 如果开启了stripIgnoreTagBody
// 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: whiteList.hasOwnProperty(tag)
};
// 调用onTag处理
var ret = onTag(tag, html, info);
if (!isNull(ret)) return ret;
// call `onTag()`
var ret = onTag(tag, html, info);
if (!isNull(ret)) return ret;
// 默认标签处理方法
if (info.isWhite) {
// 白名单标签,解析标签属性
// 如果是闭合标签,则不需要解析属性
if (info.isClosing) {
return '</' + tag + '>';
}
if (info.isWhite) {
if (info.isClosing) {
return "</" + tag + ">";
}
var attrs = getAttrs(html);
var whiteAttrList = whiteList[tag];
var attrsHtml = parseAttr(attrs.html, function (name, value) {
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;
// 调用onTagAttr处理
var isWhiteAttr = (_.indexOf(whiteAttrList, name) !== -1);
var ret = onTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
// 默认的属性处理方法
if (isWhiteAttr) {
// 白名单属性,调用safeAttrValue过滤属性值
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
return name + '="' + value + '"';
if (isWhiteAttr) {
// call `safeAttrValue()`
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
return name + '="' + value + '"';
} else {
return name;
}
} else {
return name;
// call `onIgnoreTagAttr()`
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
return;
}
} else {
// 非白名单属性,调用onIgnoreTagAttr处理
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
return;
}
});
});
// 构造新的标签代码
var html = '<' + tag;
if (attrsHtml) html += ' ' + attrsHtml;
if (attrs.closing) html += ' /';
html += '>';
return html;
// 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
);
} else {
// 非白名单标签,调用onIgnoreTag处理
var ret = onIgnoreTag(tag, html, info);
if (!isNull(ret)) return ret;
return escapeHtml(html);
}
}, escapeHtml);
// 如果开启了stripIgnoreTagBody,需要对结果再进行处理
// if enable stripIgnoreTagBody
if (stripIgnoreTagBody) {

@@ -1006,3 +1004,2 @@ retHtml = stripIgnoreTagBody.remove(retHtml);

module.exports = FilterXSS;

@@ -1009,0 +1006,0 @@

@@ -1,1 +0,1 @@

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){var FilterCSS=require("cssfilter").FilterCSS;var getDefaultCSSWhiteList=require("cssfilter").getDefaultWhiteList;var _=require("./util");function getDefaultWhiteList(){return{a:["target","href","title"],abbr:["title"],address:[],area:["shape","coords","href","alt"],article:[],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"],footer:[],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:[],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"]}}var defaultCSSFilter=new FilterCSS;function onTag(tag,html,options){}function onIgnoreTag(tag,html,options){}function onTagAttr(tag,name,value){}function onIgnoreTagAttr(tag,name,value){}function escapeHtml(html){return html.replace(REGEXP_LT,"&lt;").replace(REGEXP_GT,"&gt;")}function safeAttrValue(tag,name,value,cssFilter){value=friendlyAttrValue(value);if(name==="href"||name==="src"){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""}}else if(name==="background"){REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex=0;if(REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)){return""}}else if(name==="style"){REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex=0;if(REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)){return""}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""}}if(cssFilter!==false){cssFilter=cssFilter||defaultCSSFilter;value=cssFilter.process(value)}}value=escapeAttrValue(value);return value}var REGEXP_LT=/</g;var REGEXP_GT=/>/g;var REGEXP_QUOTE=/"/g;var REGEXP_QUOTE_2=/&quot;/g;var REGEXP_ATTR_VALUE_1=/&#([a-zA-Z0-9]*);?/gim;var REGEXP_ATTR_VALUE_COLON=/&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;function escapeQuote(str){return str.replace(REGEXP_QUOTE,"&quot;")}function unescapeQuote(str){return str.replace(REGEXP_QUOTE_2,'"')}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 escapeDangerHtml5Entities(str){return str.replace(REGEXP_ATTR_VALUE_COLON,":").replace(REGEXP_ATTR_VALUE_NEWLINE," ")}function clearNonPrintableCharacter(str){var str2="";for(var i=0,len=str.length;i<len;i++){str2+=str.charCodeAt(i)<32?" ":str.charAt(i)}return _.trim(str2)}function friendlyAttrValue(str){str=unescapeQuote(str);str=escapeHtmlEntities(str);str=escapeDangerHtml5Entities(str);str=clearNonPrintableCharacter(str);return str}function escapeAttrValue(str){str=escapeQuote(str);str=escapeHtml(str);return str}function onIgnoreTagStripAll(){return""}function StripTagBody(tags,next){if(typeof next!=="function"){next=function(){}}var isRemoveAllTag=!Array.isArray(tags);function isRemoveTag(tag){if(isRemoveAllTag)return true;return _.indexOf(tags,tag)!==-1}var removeList=[];var posStart=false;return{onIgnoreTag:function(tag,html,options){if(isRemoveTag(tag)){if(options.isClosing){var ret="[/removed]";var end=options.position+ret.length;removeList.push([posStart!==false?posStart:options.position,end]);posStart=false;return ret}else{if(!posStart){posStart=options.position}return"[removed]"}}else{return next(tag,html,options)}},remove:function(html){var rethtml="";var lastPos=0;_.forEach(removeList,function(pos){rethtml+=html.slice(lastPos,pos[0]);lastPos=pos[1]});rethtml+=html.slice(lastPos);return rethtml}}}function stripCommentTag(html){return html.replace(STRIP_COMMENT_TAG_REGEXP,"")}var STRIP_COMMENT_TAG_REGEXP=/<!--[\s\S]*?-->/g;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){if(c===10||c===13)return true;return false}return true});return chars.join("")}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onTag=onTag;exports.onIgnoreTag=onIgnoreTag;exports.onTagAttr=onTagAttr;exports.onIgnoreTagAttr=onIgnoreTagAttr;exports.safeAttrValue=safeAttrValue;exports.escapeHtml=escapeHtml;exports.escapeQuote=escapeQuote;exports.unescapeQuote=unescapeQuote;exports.escapeHtmlEntities=escapeHtmlEntities;exports.escapeDangerHtml5Entities=escapeDangerHtml5Entities;exports.clearNonPrintableCharacter=clearNonPrintableCharacter;exports.friendlyAttrValue=friendlyAttrValue;exports.escapeAttrValue=escapeAttrValue;exports.onIgnoreTagStripAll=onIgnoreTagStripAll;exports.StripTagBody=StripTagBody;exports.stripCommentTag=stripCommentTag;exports.stripBlankChar=stripBlankChar;exports.cssFilter=defaultCSSFilter;exports.getDefaultCSSWhiteList=getDefaultCSSWhiteList},{"./util":4,cssfilter:8}],2:[function(require,module,exports){var DEFAULT=require("./default");var parser=require("./parser");var FilterXSS=require("./xss");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];if(typeof window!=="undefined"){window.filterXSS=module.exports}},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){var _=require("./util");function getTagName(html){var i=_.spaceIndex(html);if(i===-1){var tagName=html.slice(1,-1)}else{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);return tagName}function isClosing(html){return html.slice(0,2)==="</"}function parseTag(html,onTag,escapeHtml){"user strict";var rethtml="";var lastPos=0;var tagStart=false;var quoteStart=false;var currentPos=0;var len=html.length;var currentHtml="";var currentTagName="";for(currentPos=0;currentPos<len;currentPos++){var c=html.charAt(currentPos);if(tagStart===false){if(c==="<"){tagStart=currentPos;continue}}else{if(quoteStart===false){if(c==="<"){rethtml+=escapeHtml(html.slice(lastPos,currentPos));tagStart=currentPos;lastPos=currentPos;continue}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));lastPos=currentPos+1;tagStart=false;continue}if((c==='"'||c==="'")&&html.charAt(currentPos-1)==="="){quoteStart=c;continue}}else{if(c===quoteStart){quoteStart=false;continue}}}}if(lastPos<html.length){rethtml+=escapeHtml(html.substr(lastPos))}return rethtml}var REGEXP_ATTR_NAME=/[^a-zA-Z0-9_:\.\-]/gim;function parseAttr(html,onAttr){"user strict";var lastPos=0;var retAttrs=[];var tmpName=false;var len=html.length;function addAttr(name,value){name=_.trim(name);name=name.replace(REGEXP_ATTR_NAME,"").toLowerCase();if(name.length<1)return;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==="="){tmpName=html.slice(lastPos,i);lastPos=i+1;continue}if(tmpName!==false){if(i===lastPos&&(c==='"'||c==="'")&&html.charAt(i-1)==="="){j=html.indexOf(c,i+1);if(j===-1){break}else{v=_.trim(html.slice(lastPos+1,j));addAttr(tmpName,v);tmpName=false;i=j;lastPos=i+1;continue}}}if(/\s|\n|\t/.test(c)){html=html.replace(/\s|\n|\t/g," ");if(tmpName===false){j=findNextEqual(html,i);if(j===-1){v=_.trim(html.slice(lastPos,i));addAttr(v);tmpName=false;lastPos=i+1;continue}else{i=j-1;continue}}else{j=findBeforeEqual(html,i-1);if(j===-1){v=_.trim(html.slice(lastPos,i));v=stripQuoteWrap(v);addAttr(tmpName,v);tmpName=false;lastPos=i+1;continue}else{continue}}}}if(lastPos<html.length){if(tmpName===false){addAttr(html.slice(lastPos))}else{addAttr(tmpName,stripQuoteWrap(_.trim(html.slice(lastPos))))}}return _.trim(retAttrs.join(" "))}function findNextEqual(str,i){for(;i<str.length;i++){var c=str[i];if(c===" ")continue;if(c==="=")return i;return-1}}function findBeforeEqual(str,i){for(;i>0;i--){var c=str[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]==="'"){return true}else{return false}}function stripQuoteWrap(text){if(isQuoteWrapString(text)){return text.substr(1,text.length-2)}else{return text}}exports.parseTag=parseTag;exports.parseAttr=parseAttr},{"./util":4}],4:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i<j;i++){if(arr[i]===item){return i}}return-1},forEach:function(arr,fn,scope){var i,j;if(Array.prototype.forEach){return arr.forEach(fn,scope)}for(i=0,j=arr.length;i<j;i++){fn.call(scope,arr[i],i,arr)}},trim:function(str){if(String.prototype.trim){return str.trim()}return str.replace(/(^\s*)|(\s*$)/g,"")},spaceIndex:function(str){var reg=/\s|\n|\t/;var match=reg.exec(str);return match?match.index:-1}}},{}],5:[function(require,module,exports){var FilterCSS=require("cssfilter").FilterCSS;var DEFAULT=require("./default");var parser=require("./parser");var parseTag=parser.parseTag;var parseAttr=parser.parseAttr;var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function getAttrs(html){var i=_.spaceIndex(html);if(i===-1){return{html:"",closing:html[html.length-2]==="/"}}html=_.trim(html.slice(i+1,-1));var isClosing=html[html.length-1]==="/";if(isClosing)html=_.trim(html.slice(0,-1));return{html:html,closing:isClosing}}function shallowCopyObject(obj){var ret={};for(var i in obj){ret[i]=obj[i]}return ret}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')}options.onIgnoreTag=DEFAULT.onIgnoreTagStripAll}options.whiteList=options.whiteList||DEFAULT.whiteList;options.onTag=options.onTag||DEFAULT.onTag;options.onTagAttr=options.onTagAttr||DEFAULT.onTagAttr;options.onIgnoreTag=options.onIgnoreTag||DEFAULT.onIgnoreTag;options.onIgnoreTagAttr=options.onIgnoreTagAttr||DEFAULT.onIgnoreTagAttr;options.safeAttrValue=options.safeAttrValue||DEFAULT.safeAttrValue;options.escapeHtml=options.escapeHtml||DEFAULT.escapeHtml;this.options=options;if(options.css===false){this.cssFilter=false}else{options.css=options.css||{};this.cssFilter=new FilterCSS(options.css)}}FilterXSS.prototype.process=function(html){html=html||"";html=html.toString();if(!html)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onTag=options.onTag;var onIgnoreTag=options.onIgnoreTag;var onTagAttr=options.onTagAttr;var onIgnoreTagAttr=options.onIgnoreTagAttr;var safeAttrValue=options.safeAttrValue;var escapeHtml=options.escapeHtml;var cssFilter=me.cssFilter;if(options.stripBlankChar){html=DEFAULT.stripBlankChar(html)}if(!options.allowCommentTag){html=DEFAULT.stripCommentTag(html)}var stripIgnoreTagBody=false;if(options.stripIgnoreTagBody){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 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){var isWhiteAttr=_.indexOf(whiteAttrList,name)!==-1;var ret=onTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;if(isWhiteAttr){value=safeAttrValue(tag,name,value,cssFilter);if(value){return name+'="'+value+'"'}else{return name}}else{var ret=onIgnoreTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;return}});var html="<"+tag;if(attrsHtml)html+=" "+attrsHtml;if(attrs.closing)html+=" /";html+=">";return html}else{var ret=onIgnoreTag(tag,html,info);if(!isNull(ret))return ret;return escapeHtml(html)}},escapeHtml);if(stripIgnoreTagBody){retHtml=stripIgnoreTagBody.remove(retHtml)}return retHtml};module.exports=FilterXSS},{"./default":1,"./parser":3,"./util":4,cssfilter:8}],6:[function(require,module,exports){var DEFAULT=require("./default");var parseStyle=require("./parser");var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function shallowCopyObject(obj){var ret={};for(var i in obj){ret[i]=obj[i]}return ret}function FilterCSS(options){options=shallowCopyObject(options||{});options.whiteList=options.whiteList||DEFAULT.whiteList;options.onAttr=options.onAttr||DEFAULT.onAttr;options.onIgnoreAttr=options.onIgnoreAttr||DEFAULT.onIgnoreAttr;options.safeAttrValue=options.safeAttrValue||DEFAULT.safeAttrValue;this.options=options}FilterCSS.prototype.process=function(css){css=css||"";css=css.toString();if(!css)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onAttr=options.onAttr;var onIgnoreAttr=options.onIgnoreAttr;var safeAttrValue=options.safeAttrValue;var retCSS=parseStyle(css,function(sourcePosition,position,name,value,source){var check=whiteList[name];var isWhite=false;if(check===true)isWhite=check;else if(typeof check==="function")isWhite=check(value);else if(check instanceof RegExp)isWhite=check.test(value);if(isWhite!==true)isWhite=false;value=safeAttrValue(name,value);if(!value)return;var opts={position:position,sourcePosition:sourcePosition,source:source,isWhite:isWhite};if(isWhite){var ret=onAttr(name,value,opts);if(isNull(ret)){return name+":"+value}else{return ret}}else{var ret=onIgnoreAttr(name,value,opts);if(!isNull(ret)){return ret}}});return retCSS};module.exports=FilterCSS},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){function getDefaultWhiteList(){var whiteList={};whiteList["align-content"]=false;whiteList["align-items"]=false;whiteList["align-self"]=false;whiteList["alignment-adjust"]=false;whiteList["alignment-baseline"]=false;whiteList["all"]=false;whiteList["anchor-point"]=false;whiteList["animation"]=false;whiteList["animation-delay"]=false;whiteList["animation-direction"]=false;whiteList["animation-duration"]=false;whiteList["animation-fill-mode"]=false;whiteList["animation-iteration-count"]=false;whiteList["animation-name"]=false;whiteList["animation-play-state"]=false;whiteList["animation-timing-function"]=false;whiteList["azimuth"]=false;whiteList["backface-visibility"]=false;whiteList["background"]=true;whiteList["background-attachment"]=true;whiteList["background-clip"]=true;whiteList["background-color"]=true;whiteList["background-image"]=true;whiteList["background-origin"]=true;whiteList["background-position"]=true;whiteList["background-repeat"]=true;whiteList["background-size"]=true;whiteList["baseline-shift"]=false;whiteList["binding"]=false;whiteList["bleed"]=false;whiteList["bookmark-label"]=false;whiteList["bookmark-level"]=false;whiteList["bookmark-state"]=false;whiteList["border"]=true;whiteList["border-bottom"]=true;whiteList["border-bottom-color"]=true;whiteList["border-bottom-left-radius"]=true;whiteList["border-bottom-right-radius"]=true;whiteList["border-bottom-style"]=true;whiteList["border-bottom-width"]=true;whiteList["border-collapse"]=true;whiteList["border-color"]=true;whiteList["border-image"]=true;whiteList["border-image-outset"]=true;whiteList["border-image-repeat"]=true;whiteList["border-image-slice"]=true;whiteList["border-image-source"]=true;whiteList["border-image-width"]=true;whiteList["border-left"]=true;whiteList["border-left-color"]=true;whiteList["border-left-style"]=true;whiteList["border-left-width"]=true;whiteList["border-radius"]=true;whiteList["border-right"]=true;whiteList["border-right-color"]=true;whiteList["border-right-style"]=true;whiteList["border-right-width"]=true;whiteList["border-spacing"]=true;whiteList["border-style"]=true;whiteList["border-top"]=true;whiteList["border-top-color"]=true;whiteList["border-top-left-radius"]=true;whiteList["border-top-right-radius"]=true;whiteList["border-top-style"]=true;whiteList["border-top-width"]=true;whiteList["border-width"]=true;whiteList["bottom"]=false;whiteList["box-decoration-break"]=true;whiteList["box-shadow"]=true;whiteList["box-sizing"]=true;whiteList["box-snap"]=true;whiteList["box-suppress"]=true;whiteList["break-after"]=true;whiteList["break-before"]=true;whiteList["break-inside"]=true;whiteList["caption-side"]=false;whiteList["chains"]=false;whiteList["clear"]=true;whiteList["clip"]=false;whiteList["clip-path"]=false;whiteList["clip-rule"]=false;whiteList["color"]=true;whiteList["color-interpolation-filters"]=true;whiteList["column-count"]=false;whiteList["column-fill"]=false;whiteList["column-gap"]=false;whiteList["column-rule"]=false;whiteList["column-rule-color"]=false;whiteList["column-rule-style"]=false;whiteList["column-rule-width"]=false;whiteList["column-span"]=false;whiteList["column-width"]=false;whiteList["columns"]=false;whiteList["contain"]=false;whiteList["content"]=false;whiteList["counter-increment"]=false;whiteList["counter-reset"]=false;whiteList["counter-set"]=false;whiteList["crop"]=false;whiteList["cue"]=false;whiteList["cue-after"]=false;whiteList["cue-before"]=false;whiteList["cursor"]=false;whiteList["direction"]=false;whiteList["display"]=true;whiteList["display-inside"]=true;whiteList["display-list"]=true;whiteList["display-outside"]=true;whiteList["dominant-baseline"]=false;whiteList["elevation"]=false;whiteList["empty-cells"]=false;whiteList["filter"]=false;whiteList["flex"]=false;whiteList["flex-basis"]=false;whiteList["flex-direction"]=false;whiteList["flex-flow"]=false;whiteList["flex-grow"]=false;whiteList["flex-shrink"]=false;whiteList["flex-wrap"]=false;whiteList["float"]=false;whiteList["float-offset"]=false;whiteList["flood-color"]=false;whiteList["flood-opacity"]=false;whiteList["flow-from"]=false;whiteList["flow-into"]=false;whiteList["font"]=true;whiteList["font-family"]=true;whiteList["font-feature-settings"]=true;whiteList["font-kerning"]=true;whiteList["font-language-override"]=true;whiteList["font-size"]=true;whiteList["font-size-adjust"]=true;whiteList["font-stretch"]=true;whiteList["font-style"]=true;whiteList["font-synthesis"]=true;whiteList["font-variant"]=true;whiteList["font-variant-alternates"]=true;whiteList["font-variant-caps"]=true;whiteList["font-variant-east-asian"]=true;whiteList["font-variant-ligatures"]=true;whiteList["font-variant-numeric"]=true;whiteList["font-variant-position"]=true;whiteList["font-weight"]=true;whiteList["grid"]=false;whiteList["grid-area"]=false;whiteList["grid-auto-columns"]=false;whiteList["grid-auto-flow"]=false;whiteList["grid-auto-rows"]=false;whiteList["grid-column"]=false;whiteList["grid-column-end"]=false;whiteList["grid-column-start"]=false;whiteList["grid-row"]=false;whiteList["grid-row-end"]=false;whiteList["grid-row-start"]=false;whiteList["grid-template"]=false;whiteList["grid-template-areas"]=false;whiteList["grid-template-columns"]=false;whiteList["grid-template-rows"]=false;whiteList["hanging-punctuation"]=false;whiteList["height"]=true;whiteList["hyphens"]=false;whiteList["icon"]=false;whiteList["image-orientation"]=false;whiteList["image-resolution"]=false;whiteList["ime-mode"]=false;whiteList["initial-letters"]=false;whiteList["inline-box-align"]=false;whiteList["justify-content"]=false;whiteList["justify-items"]=false;whiteList["justify-self"]=false;whiteList["left"]=false;whiteList["letter-spacing"]=true;whiteList["lighting-color"]=true;whiteList["line-box-contain"]=false;whiteList["line-break"]=false;whiteList["line-grid"]=false;whiteList["line-height"]=false;whiteList["line-snap"]=false;whiteList["line-stacking"]=false;whiteList["line-stacking-ruby"]=false;whiteList["line-stacking-shift"]=false;whiteList["line-stacking-strategy"]=false;whiteList["list-style"]=true;whiteList["list-style-image"]=true;whiteList["list-style-position"]=true;whiteList["list-style-type"]=true;whiteList["margin"]=true;whiteList["margin-bottom"]=true;whiteList["margin-left"]=true;whiteList["margin-right"]=true;whiteList["margin-top"]=true;whiteList["marker-offset"]=false;whiteList["marker-side"]=false;whiteList["marks"]=false;whiteList["mask"]=false;whiteList["mask-box"]=false;whiteList["mask-box-outset"]=false;whiteList["mask-box-repeat"]=false;whiteList["mask-box-slice"]=false;whiteList["mask-box-source"]=false;whiteList["mask-box-width"]=false;whiteList["mask-clip"]=false;whiteList["mask-image"]=false;whiteList["mask-origin"]=false;whiteList["mask-position"]=false;whiteList["mask-repeat"]=false;whiteList["mask-size"]=false;whiteList["mask-source-type"]=false;whiteList["mask-type"]=false;whiteList["max-height"]=true;whiteList["max-lines"]=false;whiteList["max-width"]=true;whiteList["min-height"]=true;whiteList["min-width"]=true;whiteList["move-to"]=false;whiteList["nav-down"]=false;whiteList["nav-index"]=false;whiteList["nav-left"]=false;whiteList["nav-right"]=false;whiteList["nav-up"]=false;whiteList["object-fit"]=false;whiteList["object-position"]=false;whiteList["opacity"]=false;whiteList["order"]=false;whiteList["orphans"]=false;whiteList["outline"]=false;whiteList["outline-color"]=false;whiteList["outline-offset"]=false;whiteList["outline-style"]=false;whiteList["outline-width"]=false;whiteList["overflow"]=false;whiteList["overflow-wrap"]=false;whiteList["overflow-x"]=false;whiteList["overflow-y"]=false;whiteList["padding"]=true;whiteList["padding-bottom"]=true;whiteList["padding-left"]=true;whiteList["padding-right"]=true;whiteList["padding-top"]=true;whiteList["page"]=false;whiteList["page-break-after"]=false;whiteList["page-break-before"]=false;whiteList["page-break-inside"]=false;whiteList["page-policy"]=false;whiteList["pause"]=false;whiteList["pause-after"]=false;whiteList["pause-before"]=false;whiteList["perspective"]=false;whiteList["perspective-origin"]=false;whiteList["pitch"]=false;whiteList["pitch-range"]=false;whiteList["play-during"]=false;whiteList["position"]=false;whiteList["presentation-level"]=false;whiteList["quotes"]=false;whiteList["region-fragment"]=false;whiteList["resize"]=false;whiteList["rest"]=false;whiteList["rest-after"]=false;whiteList["rest-before"]=false;whiteList["richness"]=false;whiteList["right"]=false;whiteList["rotation"]=false;whiteList["rotation-point"]=false;whiteList["ruby-align"]=false;whiteList["ruby-merge"]=false;whiteList["ruby-position"]=false;whiteList["shape-image-threshold"]=false;whiteList["shape-outside"]=false;whiteList["shape-margin"]=false;whiteList["size"]=false;whiteList["speak"]=false;whiteList["speak-as"]=false;whiteList["speak-header"]=false;whiteList["speak-numeral"]=false;whiteList["speak-punctuation"]=false;whiteList["speech-rate"]=false;whiteList["stress"]=false;whiteList["string-set"]=false;whiteList["tab-size"]=false;whiteList["table-layout"]=false;whiteList["text-align"]=true;whiteList["text-align-last"]=true;whiteList["text-combine-upright"]=true;whiteList["text-decoration"]=true;whiteList["text-decoration-color"]=true;whiteList["text-decoration-line"]=true;whiteList["text-decoration-skip"]=true;whiteList["text-decoration-style"]=true;whiteList["text-emphasis"]=true;whiteList["text-emphasis-color"]=true;whiteList["text-emphasis-position"]=true;whiteList["text-emphasis-style"]=true;whiteList["text-height"]=true;whiteList["text-indent"]=true;whiteList["text-justify"]=true;whiteList["text-orientation"]=true;whiteList["text-overflow"]=true;whiteList["text-shadow"]=true;whiteList["text-space-collapse"]=true;whiteList["text-transform"]=true;whiteList["text-underline-position"]=true;whiteList["text-wrap"]=true;whiteList["top"]=false;whiteList["transform"]=false;whiteList["transform-origin"]=false;whiteList["transform-style"]=false;whiteList["transition"]=false;whiteList["transition-delay"]=false;whiteList["transition-duration"]=false;whiteList["transition-property"]=false;whiteList["transition-timing-function"]=false;whiteList["unicode-bidi"]=false;whiteList["vertical-align"]=false;whiteList["visibility"]=false;whiteList["voice-balance"]=false;whiteList["voice-duration"]=false;whiteList["voice-family"]=false;whiteList["voice-pitch"]=false;whiteList["voice-range"]=false;whiteList["voice-rate"]=false;whiteList["voice-stress"]=false;whiteList["voice-volume"]=false;whiteList["volume"]=false;whiteList["white-space"]=false;whiteList["widows"]=false;whiteList["width"]=true;whiteList["will-change"]=false;whiteList["word-break"]=true;whiteList["word-spacing"]=true;whiteList["word-wrap"]=true;whiteList["wrap-flow"]=false;whiteList["wrap-through"]=false;whiteList["writing-mode"]=false;whiteList["z-index"]=false;return whiteList}function onAttr(name,value,options){}function onIgnoreAttr(name,value,options){}var REGEXP_URL_JAVASCRIPT=/javascript\s*\:/gim;function safeAttrValue(name,value){if(REGEXP_URL_JAVASCRIPT.test(value))return"";return value}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onAttr=onAttr;exports.onIgnoreAttr=onIgnoreAttr;exports.safeAttrValue=safeAttrValue},{}],8:[function(require,module,exports){var DEFAULT=require("./default");var FilterCSS=require("./css");function filterCSS(html,options){var xss=new FilterCSS(options);return xss.process(html)}exports=module.exports=filterCSS;exports.FilterCSS=FilterCSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];if(typeof window!=="undefined"){window.filterCSS=module.exports}},{"./css":6,"./default":7}],9:[function(require,module,exports){var _=require("./util");function parseStyle(css,onAttr){css=_.trimRight(css);if(css[css.length-1]!==";")css+=";";var cssLength=css.length;var isParenthesisOpen=false;var lastPos=0;var i=0;var retCSS="";function addNewAttr(){if(!isParenthesisOpen){var source=_.trim(css.slice(lastPos,i));var j=source.indexOf(":");if(j!==-1){var name=_.trim(source.slice(0,j));var value=_.trim(source.slice(j+1));if(name){var ret=onAttr(lastPos,retCSS.length,name,value,source);if(ret)retCSS+=ret+"; "}}}lastPos=i+1}for(;i<cssLength;i++){var c=css[i];if(c==="/"&&css[i+1]==="*"){var j=css.indexOf("*/",i+2);if(j===-1)break;i=j+1;lastPos=i+1;isParenthesisOpen=false}else if(c==="("){isParenthesisOpen=true}else if(c===")"){isParenthesisOpen=false}else if(c===";"){if(isParenthesisOpen){}else{addNewAttr()}}else if(c==="\n"){addNewAttr()}}return _.trim(retCSS)}module.exports=parseStyle},{"./util":10}],10:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i<j;i++){if(arr[i]===item){return i}}return-1},forEach:function(arr,fn,scope){var i,j;if(Array.prototype.forEach){return arr.forEach(fn,scope)}for(i=0,j=arr.length;i<j;i++){fn.call(scope,arr[i],i,arr)}},trim:function(str){if(String.prototype.trim){return str.trim()}return str.replace(/(^\s*)|(\s*$)/g,"")},trimRight:function(str){if(String.prototype.trimRight){return str.trimRight()}return str.replace(/(\s*$)/g,"")}}},{}]},{},[2]);
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){var FilterCSS=require("cssfilter").FilterCSS;var getDefaultCSSWhiteList=require("cssfilter").getDefaultWhiteList;var _=require("./util");function getDefaultWhiteList(){return{a:["target","href","title"],abbr:["title"],address:[],area:["shape","coords","href","alt"],article:[],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"],footer:[],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:[],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"]}}var defaultCSSFilter=new FilterCSS;function onTag(tag,html,options){}function onIgnoreTag(tag,html,options){}function onTagAttr(tag,name,value){}function onIgnoreTagAttr(tag,name,value){}function escapeHtml(html){return html.replace(REGEXP_LT,"&lt;").replace(REGEXP_GT,"&gt;")}function safeAttrValue(tag,name,value,cssFilter){value=friendlyAttrValue(value);if(name==="href"||name==="src"){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""}}else if(name==="background"){REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex=0;if(REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)){return""}}else if(name==="style"){REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex=0;if(REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)){return""}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""}}if(cssFilter!==false){cssFilter=cssFilter||defaultCSSFilter;value=cssFilter.process(value)}}value=escapeAttrValue(value);return value}var REGEXP_LT=/</g;var REGEXP_GT=/>/g;var REGEXP_QUOTE=/"/g;var REGEXP_QUOTE_2=/&quot;/g;var REGEXP_ATTR_VALUE_1=/&#([a-zA-Z0-9]*);?/gim;var REGEXP_ATTR_VALUE_COLON=/&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;function escapeQuote(str){return str.replace(REGEXP_QUOTE,"&quot;")}function unescapeQuote(str){return str.replace(REGEXP_QUOTE_2,'"')}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 escapeDangerHtml5Entities(str){return str.replace(REGEXP_ATTR_VALUE_COLON,":").replace(REGEXP_ATTR_VALUE_NEWLINE," ")}function clearNonPrintableCharacter(str){var str2="";for(var i=0,len=str.length;i<len;i++){str2+=str.charCodeAt(i)<32?" ":str.charAt(i)}return _.trim(str2)}function friendlyAttrValue(str){str=unescapeQuote(str);str=escapeHtmlEntities(str);str=escapeDangerHtml5Entities(str);str=clearNonPrintableCharacter(str);return str}function escapeAttrValue(str){str=escapeQuote(str);str=escapeHtml(str);return str}function onIgnoreTagStripAll(){return""}function StripTagBody(tags,next){if(typeof next!=="function"){next=function(){}}var isRemoveAllTag=!Array.isArray(tags);function isRemoveTag(tag){if(isRemoveAllTag)return true;return _.indexOf(tags,tag)!==-1}var removeList=[];var posStart=false;return{onIgnoreTag:function(tag,html,options){if(isRemoveTag(tag)){if(options.isClosing){var ret="[/removed]";var end=options.position+ret.length;removeList.push([posStart!==false?posStart:options.position,end]);posStart=false;return ret}else{if(!posStart){posStart=options.position}return"[removed]"}}else{return next(tag,html,options)}},remove:function(html){var rethtml="";var lastPos=0;_.forEach(removeList,function(pos){rethtml+=html.slice(lastPos,pos[0]);lastPos=pos[1]});rethtml+=html.slice(lastPos);return rethtml}}}function stripCommentTag(html){return html.replace(STRIP_COMMENT_TAG_REGEXP,"")}var STRIP_COMMENT_TAG_REGEXP=/<!--[\s\S]*?-->/g;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){if(c===10||c===13)return true;return false}return true});return chars.join("")}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onTag=onTag;exports.onIgnoreTag=onIgnoreTag;exports.onTagAttr=onTagAttr;exports.onIgnoreTagAttr=onIgnoreTagAttr;exports.safeAttrValue=safeAttrValue;exports.escapeHtml=escapeHtml;exports.escapeQuote=escapeQuote;exports.unescapeQuote=unescapeQuote;exports.escapeHtmlEntities=escapeHtmlEntities;exports.escapeDangerHtml5Entities=escapeDangerHtml5Entities;exports.clearNonPrintableCharacter=clearNonPrintableCharacter;exports.friendlyAttrValue=friendlyAttrValue;exports.escapeAttrValue=escapeAttrValue;exports.onIgnoreTagStripAll=onIgnoreTagStripAll;exports.StripTagBody=StripTagBody;exports.stripCommentTag=stripCommentTag;exports.stripBlankChar=stripBlankChar;exports.cssFilter=defaultCSSFilter;exports.getDefaultCSSWhiteList=getDefaultCSSWhiteList},{"./util":4,cssfilter:8}],2:[function(require,module,exports){var DEFAULT=require("./default");var parser=require("./parser");var FilterXSS=require("./xss");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];if(typeof window!=="undefined"){window.filterXSS=module.exports}},{"./default":1,"./parser":3,"./xss":5}],3:[function(require,module,exports){var _=require("./util");function getTagName(html){var i=_.spaceIndex(html);if(i===-1){var tagName=html.slice(1,-1)}else{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);return tagName}function isClosing(html){return html.slice(0,2)==="</"}function parseTag(html,onTag,escapeHtml){"user strict";var rethtml="";var lastPos=0;var tagStart=false;var quoteStart=false;var currentPos=0;var len=html.length;var currentTagName="";for(currentPos=0;currentPos<len;currentPos++){var c=html.charAt(currentPos);if(tagStart===false){if(c==="<"){tagStart=currentPos;continue}}else{if(quoteStart===false){if(c==="<"){rethtml+=escapeHtml(html.slice(lastPos,currentPos));tagStart=currentPos;lastPos=currentPos;continue}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));lastPos=currentPos+1;tagStart=false;continue}if((c==='"'||c==="'")&&html.charAt(currentPos-1)==="="){quoteStart=c;continue}}else{if(c===quoteStart){quoteStart=false;continue}}}}if(lastPos<html.length){rethtml+=escapeHtml(html.substr(lastPos))}return rethtml}var REGEXP_ILLEGAL_ATTR_NAME=/[^a-zA-Z0-9_:\.\-]/gim;function parseAttr(html,onAttr){"user strict";var lastPos=0;var retAttrs=[];var tmpName=false;var len=html.length;function addAttr(name,value){name=_.trim(name);name=name.replace(REGEXP_ILLEGAL_ATTR_NAME,"").toLowerCase();if(name.length<1)return;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==="="){tmpName=html.slice(lastPos,i);lastPos=i+1;continue}if(tmpName!==false){if(i===lastPos&&(c==='"'||c==="'")&&html.charAt(i-1)==="="){j=html.indexOf(c,i+1);if(j===-1){break}else{v=_.trim(html.slice(lastPos+1,j));addAttr(tmpName,v);tmpName=false;i=j;lastPos=i+1;continue}}}if(/\s|\n|\t/.test(c)){html=html.replace(/\s|\n|\t/g," ");if(tmpName===false){j=findNextEqual(html,i);if(j===-1){v=_.trim(html.slice(lastPos,i));addAttr(v);tmpName=false;lastPos=i+1;continue}else{i=j-1;continue}}else{j=findBeforeEqual(html,i-1);if(j===-1){v=_.trim(html.slice(lastPos,i));v=stripQuoteWrap(v);addAttr(tmpName,v);tmpName=false;lastPos=i+1;continue}else{continue}}}}if(lastPos<html.length){if(tmpName===false){addAttr(html.slice(lastPos))}else{addAttr(tmpName,stripQuoteWrap(_.trim(html.slice(lastPos))))}}return _.trim(retAttrs.join(" "))}function findNextEqual(str,i){for(;i<str.length;i++){var c=str[i];if(c===" ")continue;if(c==="=")return i;return-1}}function findBeforeEqual(str,i){for(;i>0;i--){var c=str[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]==="'"){return true}else{return false}}function stripQuoteWrap(text){if(isQuoteWrapString(text)){return text.substr(1,text.length-2)}else{return text}}exports.parseTag=parseTag;exports.parseAttr=parseAttr},{"./util":4}],4:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i<j;i++){if(arr[i]===item){return i}}return-1},forEach:function(arr,fn,scope){var i,j;if(Array.prototype.forEach){return arr.forEach(fn,scope)}for(i=0,j=arr.length;i<j;i++){fn.call(scope,arr[i],i,arr)}},trim:function(str){if(String.prototype.trim){return str.trim()}return str.replace(/(^\s*)|(\s*$)/g,"")},spaceIndex:function(str){var reg=/\s|\n|\t/;var match=reg.exec(str);return match?match.index:-1}}},{}],5:[function(require,module,exports){var FilterCSS=require("cssfilter").FilterCSS;var DEFAULT=require("./default");var parser=require("./parser");var parseTag=parser.parseTag;var parseAttr=parser.parseAttr;var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function getAttrs(html){var i=_.spaceIndex(html);if(i===-1){return{html:"",closing:html[html.length-2]==="/"}}html=_.trim(html.slice(i+1,-1));var isClosing=html[html.length-1]==="/";if(isClosing)html=_.trim(html.slice(0,-1));return{html:html,closing:isClosing}}function shallowCopyObject(obj){var ret={};for(var i in obj){ret[i]=obj[i]}return ret}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')}options.onIgnoreTag=DEFAULT.onIgnoreTagStripAll}options.whiteList=options.whiteList||DEFAULT.whiteList;options.onTag=options.onTag||DEFAULT.onTag;options.onTagAttr=options.onTagAttr||DEFAULT.onTagAttr;options.onIgnoreTag=options.onIgnoreTag||DEFAULT.onIgnoreTag;options.onIgnoreTagAttr=options.onIgnoreTagAttr||DEFAULT.onIgnoreTagAttr;options.safeAttrValue=options.safeAttrValue||DEFAULT.safeAttrValue;options.escapeHtml=options.escapeHtml||DEFAULT.escapeHtml;this.options=options;if(options.css===false){this.cssFilter=false}else{options.css=options.css||{};this.cssFilter=new FilterCSS(options.css)}}FilterXSS.prototype.process=function(html){html=html||"";html=html.toString();if(!html)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onTag=options.onTag;var onIgnoreTag=options.onIgnoreTag;var onTagAttr=options.onTagAttr;var onIgnoreTagAttr=options.onIgnoreTagAttr;var safeAttrValue=options.safeAttrValue;var escapeHtml=options.escapeHtml;var cssFilter=me.cssFilter;if(options.stripBlankChar){html=DEFAULT.stripBlankChar(html)}if(!options.allowCommentTag){html=DEFAULT.stripCommentTag(html)}var stripIgnoreTagBody=false;if(options.stripIgnoreTagBody){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:whiteList.hasOwnProperty(tag)};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){var isWhiteAttr=_.indexOf(whiteAttrList,name)!==-1;var ret=onTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;if(isWhiteAttr){value=safeAttrValue(tag,name,value,cssFilter);if(value){return name+'="'+value+'"'}else{return name}}else{var ret=onIgnoreTagAttr(tag,name,value,isWhiteAttr);if(!isNull(ret))return ret;return}});var html="<"+tag;if(attrsHtml)html+=" "+attrsHtml;if(attrs.closing)html+=" /";html+=">";return html}else{var ret=onIgnoreTag(tag,html,info);if(!isNull(ret))return ret;return escapeHtml(html)}},escapeHtml);if(stripIgnoreTagBody){retHtml=stripIgnoreTagBody.remove(retHtml)}return retHtml};module.exports=FilterXSS},{"./default":1,"./parser":3,"./util":4,cssfilter:8}],6:[function(require,module,exports){var DEFAULT=require("./default");var parseStyle=require("./parser");var _=require("./util");function isNull(obj){return obj===undefined||obj===null}function shallowCopyObject(obj){var ret={};for(var i in obj){ret[i]=obj[i]}return ret}function FilterCSS(options){options=shallowCopyObject(options||{});options.whiteList=options.whiteList||DEFAULT.whiteList;options.onAttr=options.onAttr||DEFAULT.onAttr;options.onIgnoreAttr=options.onIgnoreAttr||DEFAULT.onIgnoreAttr;options.safeAttrValue=options.safeAttrValue||DEFAULT.safeAttrValue;this.options=options}FilterCSS.prototype.process=function(css){css=css||"";css=css.toString();if(!css)return"";var me=this;var options=me.options;var whiteList=options.whiteList;var onAttr=options.onAttr;var onIgnoreAttr=options.onIgnoreAttr;var safeAttrValue=options.safeAttrValue;var retCSS=parseStyle(css,function(sourcePosition,position,name,value,source){var check=whiteList[name];var isWhite=false;if(check===true)isWhite=check;else if(typeof check==="function")isWhite=check(value);else if(check instanceof RegExp)isWhite=check.test(value);if(isWhite!==true)isWhite=false;value=safeAttrValue(name,value);if(!value)return;var opts={position:position,sourcePosition:sourcePosition,source:source,isWhite:isWhite};if(isWhite){var ret=onAttr(name,value,opts);if(isNull(ret)){return name+":"+value}else{return ret}}else{var ret=onIgnoreAttr(name,value,opts);if(!isNull(ret)){return ret}}});return retCSS};module.exports=FilterCSS},{"./default":7,"./parser":9,"./util":10}],7:[function(require,module,exports){function getDefaultWhiteList(){var whiteList={};whiteList["align-content"]=false;whiteList["align-items"]=false;whiteList["align-self"]=false;whiteList["alignment-adjust"]=false;whiteList["alignment-baseline"]=false;whiteList["all"]=false;whiteList["anchor-point"]=false;whiteList["animation"]=false;whiteList["animation-delay"]=false;whiteList["animation-direction"]=false;whiteList["animation-duration"]=false;whiteList["animation-fill-mode"]=false;whiteList["animation-iteration-count"]=false;whiteList["animation-name"]=false;whiteList["animation-play-state"]=false;whiteList["animation-timing-function"]=false;whiteList["azimuth"]=false;whiteList["backface-visibility"]=false;whiteList["background"]=true;whiteList["background-attachment"]=true;whiteList["background-clip"]=true;whiteList["background-color"]=true;whiteList["background-image"]=true;whiteList["background-origin"]=true;whiteList["background-position"]=true;whiteList["background-repeat"]=true;whiteList["background-size"]=true;whiteList["baseline-shift"]=false;whiteList["binding"]=false;whiteList["bleed"]=false;whiteList["bookmark-label"]=false;whiteList["bookmark-level"]=false;whiteList["bookmark-state"]=false;whiteList["border"]=true;whiteList["border-bottom"]=true;whiteList["border-bottom-color"]=true;whiteList["border-bottom-left-radius"]=true;whiteList["border-bottom-right-radius"]=true;whiteList["border-bottom-style"]=true;whiteList["border-bottom-width"]=true;whiteList["border-collapse"]=true;whiteList["border-color"]=true;whiteList["border-image"]=true;whiteList["border-image-outset"]=true;whiteList["border-image-repeat"]=true;whiteList["border-image-slice"]=true;whiteList["border-image-source"]=true;whiteList["border-image-width"]=true;whiteList["border-left"]=true;whiteList["border-left-color"]=true;whiteList["border-left-style"]=true;whiteList["border-left-width"]=true;whiteList["border-radius"]=true;whiteList["border-right"]=true;whiteList["border-right-color"]=true;whiteList["border-right-style"]=true;whiteList["border-right-width"]=true;whiteList["border-spacing"]=true;whiteList["border-style"]=true;whiteList["border-top"]=true;whiteList["border-top-color"]=true;whiteList["border-top-left-radius"]=true;whiteList["border-top-right-radius"]=true;whiteList["border-top-style"]=true;whiteList["border-top-width"]=true;whiteList["border-width"]=true;whiteList["bottom"]=false;whiteList["box-decoration-break"]=true;whiteList["box-shadow"]=true;whiteList["box-sizing"]=true;whiteList["box-snap"]=true;whiteList["box-suppress"]=true;whiteList["break-after"]=true;whiteList["break-before"]=true;whiteList["break-inside"]=true;whiteList["caption-side"]=false;whiteList["chains"]=false;whiteList["clear"]=true;whiteList["clip"]=false;whiteList["clip-path"]=false;whiteList["clip-rule"]=false;whiteList["color"]=true;whiteList["color-interpolation-filters"]=true;whiteList["column-count"]=false;whiteList["column-fill"]=false;whiteList["column-gap"]=false;whiteList["column-rule"]=false;whiteList["column-rule-color"]=false;whiteList["column-rule-style"]=false;whiteList["column-rule-width"]=false;whiteList["column-span"]=false;whiteList["column-width"]=false;whiteList["columns"]=false;whiteList["contain"]=false;whiteList["content"]=false;whiteList["counter-increment"]=false;whiteList["counter-reset"]=false;whiteList["counter-set"]=false;whiteList["crop"]=false;whiteList["cue"]=false;whiteList["cue-after"]=false;whiteList["cue-before"]=false;whiteList["cursor"]=false;whiteList["direction"]=false;whiteList["display"]=true;whiteList["display-inside"]=true;whiteList["display-list"]=true;whiteList["display-outside"]=true;whiteList["dominant-baseline"]=false;whiteList["elevation"]=false;whiteList["empty-cells"]=false;whiteList["filter"]=false;whiteList["flex"]=false;whiteList["flex-basis"]=false;whiteList["flex-direction"]=false;whiteList["flex-flow"]=false;whiteList["flex-grow"]=false;whiteList["flex-shrink"]=false;whiteList["flex-wrap"]=false;whiteList["float"]=false;whiteList["float-offset"]=false;whiteList["flood-color"]=false;whiteList["flood-opacity"]=false;whiteList["flow-from"]=false;whiteList["flow-into"]=false;whiteList["font"]=true;whiteList["font-family"]=true;whiteList["font-feature-settings"]=true;whiteList["font-kerning"]=true;whiteList["font-language-override"]=true;whiteList["font-size"]=true;whiteList["font-size-adjust"]=true;whiteList["font-stretch"]=true;whiteList["font-style"]=true;whiteList["font-synthesis"]=true;whiteList["font-variant"]=true;whiteList["font-variant-alternates"]=true;whiteList["font-variant-caps"]=true;whiteList["font-variant-east-asian"]=true;whiteList["font-variant-ligatures"]=true;whiteList["font-variant-numeric"]=true;whiteList["font-variant-position"]=true;whiteList["font-weight"]=true;whiteList["grid"]=false;whiteList["grid-area"]=false;whiteList["grid-auto-columns"]=false;whiteList["grid-auto-flow"]=false;whiteList["grid-auto-rows"]=false;whiteList["grid-column"]=false;whiteList["grid-column-end"]=false;whiteList["grid-column-start"]=false;whiteList["grid-row"]=false;whiteList["grid-row-end"]=false;whiteList["grid-row-start"]=false;whiteList["grid-template"]=false;whiteList["grid-template-areas"]=false;whiteList["grid-template-columns"]=false;whiteList["grid-template-rows"]=false;whiteList["hanging-punctuation"]=false;whiteList["height"]=true;whiteList["hyphens"]=false;whiteList["icon"]=false;whiteList["image-orientation"]=false;whiteList["image-resolution"]=false;whiteList["ime-mode"]=false;whiteList["initial-letters"]=false;whiteList["inline-box-align"]=false;whiteList["justify-content"]=false;whiteList["justify-items"]=false;whiteList["justify-self"]=false;whiteList["left"]=false;whiteList["letter-spacing"]=true;whiteList["lighting-color"]=true;whiteList["line-box-contain"]=false;whiteList["line-break"]=false;whiteList["line-grid"]=false;whiteList["line-height"]=false;whiteList["line-snap"]=false;whiteList["line-stacking"]=false;whiteList["line-stacking-ruby"]=false;whiteList["line-stacking-shift"]=false;whiteList["line-stacking-strategy"]=false;whiteList["list-style"]=true;whiteList["list-style-image"]=true;whiteList["list-style-position"]=true;whiteList["list-style-type"]=true;whiteList["margin"]=true;whiteList["margin-bottom"]=true;whiteList["margin-left"]=true;whiteList["margin-right"]=true;whiteList["margin-top"]=true;whiteList["marker-offset"]=false;whiteList["marker-side"]=false;whiteList["marks"]=false;whiteList["mask"]=false;whiteList["mask-box"]=false;whiteList["mask-box-outset"]=false;whiteList["mask-box-repeat"]=false;whiteList["mask-box-slice"]=false;whiteList["mask-box-source"]=false;whiteList["mask-box-width"]=false;whiteList["mask-clip"]=false;whiteList["mask-image"]=false;whiteList["mask-origin"]=false;whiteList["mask-position"]=false;whiteList["mask-repeat"]=false;whiteList["mask-size"]=false;whiteList["mask-source-type"]=false;whiteList["mask-type"]=false;whiteList["max-height"]=true;whiteList["max-lines"]=false;whiteList["max-width"]=true;whiteList["min-height"]=true;whiteList["min-width"]=true;whiteList["move-to"]=false;whiteList["nav-down"]=false;whiteList["nav-index"]=false;whiteList["nav-left"]=false;whiteList["nav-right"]=false;whiteList["nav-up"]=false;whiteList["object-fit"]=false;whiteList["object-position"]=false;whiteList["opacity"]=false;whiteList["order"]=false;whiteList["orphans"]=false;whiteList["outline"]=false;whiteList["outline-color"]=false;whiteList["outline-offset"]=false;whiteList["outline-style"]=false;whiteList["outline-width"]=false;whiteList["overflow"]=false;whiteList["overflow-wrap"]=false;whiteList["overflow-x"]=false;whiteList["overflow-y"]=false;whiteList["padding"]=true;whiteList["padding-bottom"]=true;whiteList["padding-left"]=true;whiteList["padding-right"]=true;whiteList["padding-top"]=true;whiteList["page"]=false;whiteList["page-break-after"]=false;whiteList["page-break-before"]=false;whiteList["page-break-inside"]=false;whiteList["page-policy"]=false;whiteList["pause"]=false;whiteList["pause-after"]=false;whiteList["pause-before"]=false;whiteList["perspective"]=false;whiteList["perspective-origin"]=false;whiteList["pitch"]=false;whiteList["pitch-range"]=false;whiteList["play-during"]=false;whiteList["position"]=false;whiteList["presentation-level"]=false;whiteList["quotes"]=false;whiteList["region-fragment"]=false;whiteList["resize"]=false;whiteList["rest"]=false;whiteList["rest-after"]=false;whiteList["rest-before"]=false;whiteList["richness"]=false;whiteList["right"]=false;whiteList["rotation"]=false;whiteList["rotation-point"]=false;whiteList["ruby-align"]=false;whiteList["ruby-merge"]=false;whiteList["ruby-position"]=false;whiteList["shape-image-threshold"]=false;whiteList["shape-outside"]=false;whiteList["shape-margin"]=false;whiteList["size"]=false;whiteList["speak"]=false;whiteList["speak-as"]=false;whiteList["speak-header"]=false;whiteList["speak-numeral"]=false;whiteList["speak-punctuation"]=false;whiteList["speech-rate"]=false;whiteList["stress"]=false;whiteList["string-set"]=false;whiteList["tab-size"]=false;whiteList["table-layout"]=false;whiteList["text-align"]=true;whiteList["text-align-last"]=true;whiteList["text-combine-upright"]=true;whiteList["text-decoration"]=true;whiteList["text-decoration-color"]=true;whiteList["text-decoration-line"]=true;whiteList["text-decoration-skip"]=true;whiteList["text-decoration-style"]=true;whiteList["text-emphasis"]=true;whiteList["text-emphasis-color"]=true;whiteList["text-emphasis-position"]=true;whiteList["text-emphasis-style"]=true;whiteList["text-height"]=true;whiteList["text-indent"]=true;whiteList["text-justify"]=true;whiteList["text-orientation"]=true;whiteList["text-overflow"]=true;whiteList["text-shadow"]=true;whiteList["text-space-collapse"]=true;whiteList["text-transform"]=true;whiteList["text-underline-position"]=true;whiteList["text-wrap"]=true;whiteList["top"]=false;whiteList["transform"]=false;whiteList["transform-origin"]=false;whiteList["transform-style"]=false;whiteList["transition"]=false;whiteList["transition-delay"]=false;whiteList["transition-duration"]=false;whiteList["transition-property"]=false;whiteList["transition-timing-function"]=false;whiteList["unicode-bidi"]=false;whiteList["vertical-align"]=false;whiteList["visibility"]=false;whiteList["voice-balance"]=false;whiteList["voice-duration"]=false;whiteList["voice-family"]=false;whiteList["voice-pitch"]=false;whiteList["voice-range"]=false;whiteList["voice-rate"]=false;whiteList["voice-stress"]=false;whiteList["voice-volume"]=false;whiteList["volume"]=false;whiteList["white-space"]=false;whiteList["widows"]=false;whiteList["width"]=true;whiteList["will-change"]=false;whiteList["word-break"]=true;whiteList["word-spacing"]=true;whiteList["word-wrap"]=true;whiteList["wrap-flow"]=false;whiteList["wrap-through"]=false;whiteList["writing-mode"]=false;whiteList["z-index"]=false;return whiteList}function onAttr(name,value,options){}function onIgnoreAttr(name,value,options){}var REGEXP_URL_JAVASCRIPT=/javascript\s*\:/gim;function safeAttrValue(name,value){if(REGEXP_URL_JAVASCRIPT.test(value))return"";return value}exports.whiteList=getDefaultWhiteList();exports.getDefaultWhiteList=getDefaultWhiteList;exports.onAttr=onAttr;exports.onIgnoreAttr=onIgnoreAttr;exports.safeAttrValue=safeAttrValue},{}],8:[function(require,module,exports){var DEFAULT=require("./default");var FilterCSS=require("./css");function filterCSS(html,options){var xss=new FilterCSS(options);return xss.process(html)}exports=module.exports=filterCSS;exports.FilterCSS=FilterCSS;for(var i in DEFAULT)exports[i]=DEFAULT[i];if(typeof window!=="undefined"){window.filterCSS=module.exports}},{"./css":6,"./default":7}],9:[function(require,module,exports){var _=require("./util");function parseStyle(css,onAttr){css=_.trimRight(css);if(css[css.length-1]!==";")css+=";";var cssLength=css.length;var isParenthesisOpen=false;var lastPos=0;var i=0;var retCSS="";function addNewAttr(){if(!isParenthesisOpen){var source=_.trim(css.slice(lastPos,i));var j=source.indexOf(":");if(j!==-1){var name=_.trim(source.slice(0,j));var value=_.trim(source.slice(j+1));if(name){var ret=onAttr(lastPos,retCSS.length,name,value,source);if(ret)retCSS+=ret+"; "}}}lastPos=i+1}for(;i<cssLength;i++){var c=css[i];if(c==="/"&&css[i+1]==="*"){var j=css.indexOf("*/",i+2);if(j===-1)break;i=j+1;lastPos=i+1;isParenthesisOpen=false}else if(c==="("){isParenthesisOpen=true}else if(c===")"){isParenthesisOpen=false}else if(c===";"){if(isParenthesisOpen){}else{addNewAttr()}}else if(c==="\n"){addNewAttr()}}return _.trim(retCSS)}module.exports=parseStyle},{"./util":10}],10:[function(require,module,exports){module.exports={indexOf:function(arr,item){var i,j;if(Array.prototype.indexOf){return arr.indexOf(item)}for(i=0,j=arr.length;i<j;i++){if(arr[i]===item){return i}}return-1},forEach:function(arr,fn,scope){var i,j;if(Array.prototype.forEach){return arr.forEach(fn,scope)}for(i=0,j=arr.length;i<j;i++){fn.call(scope,arr[i],i,arr)}},trim:function(str){if(String.prototype.trim){return str.trim()}return str.replace(/(^\s*)|(\s*$)/g,"")},trimRight:function(str){if(String.prototype.trimRight){return str.trimRight()}return str.replace(/(\s*$)/g,"")}}},{}]},{},[2]);
/**
* 命令行测试工具
* command line tool
*
* @author 老雷<leizongmin@gmail.com>
* @author Zongmin Lei<leizongmin@gmail.com>
*/
var xss = require('./');
var readline = require('readline');
var xss = require("./");
var readline = require("readline");
var rl = readline.createInterface({

@@ -16,7 +15,6 @@ input: process.stdin,

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++) {

@@ -28,18 +26,17 @@ ret += c;

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 = [];

@@ -46,0 +43,0 @@ } else {

/**
* 默认配置
* default settings
*
* @author 老雷<leizongmin@gmail.com>
* @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"]
};
}
// 默认CSS Filter
var defaultCSSFilter = new FilterCSS();
/**
* 匹配到标签时的处理方法
* default onTag function
*

@@ -91,3 +89,3 @@ * @param {String} tag

*/
function onTag (tag, html, options) {
function onTag(tag, html, options) {
// do nothing

@@ -97,3 +95,3 @@ }

/**
* 匹配到不在白名单上的标签时的处理方法
* default onIgnoreTag function
*

@@ -105,3 +103,3 @@ * @param {String} tag

*/
function onIgnoreTag (tag, html, options) {
function onIgnoreTag(tag, html, options) {
// do nothing

@@ -111,3 +109,3 @@ }

/**
* 匹配到标签属性时的处理方法
* default onTagAttr function
*

@@ -119,3 +117,3 @@ * @param {String} tag

*/
function onTagAttr (tag, name, value) {
function onTagAttr(tag, name, value) {
// do nothing

@@ -125,3 +123,3 @@ }

/**
* 匹配到不在白名单上的标签属性时的处理方法
* default onIgnoreTagAttr function
*

@@ -133,3 +131,3 @@ * @param {String} tag

*/
function onIgnoreTagAttr (tag, name, value) {
function onIgnoreTagAttr(tag, name, value) {
// do nothing

@@ -139,12 +137,12 @@ }

/**
* HTML转义
* default escapeHtml function
*
* @param {String} html
*/
function escapeHtml (html) {
return html.replace(REGEXP_LT, '&lt;').replace(REGEXP_GT, '&gt;');
function escapeHtml(html) {
return html.replace(REGEXP_LT, "&lt;").replace(REGEXP_GT, "&gt;");
}
/**
* 安全的标签属性值
* default safeAttrValue function
*

@@ -157,38 +155,37 @@ * @param {String} tag

*/
function safeAttrValue (tag, name, value, cssFilter) {
// 转换为友好的属性值,再做判断
function safeAttrValue(tag, name, value, cssFilter) {
// unescape attribute value firstly
value = friendlyAttrValue(value);
if (name === 'href' || name === 'src') {
// 过滤 href 和 src 属性
// 仅允许 http:// | https:// | mailto: | / | # 开头的地址
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') {
// 过滤 background 属性 (这个xss漏洞较老了,可能已经不适用)
// javascript:
} 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') {
// /*注释*/
/*REGEXP_DEFAULT_ON_TAG_ATTR_3.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_3.test(value)) {
return '';
}*/
// expression()
} 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()
// `url()`
REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0;

@@ -198,3 +195,3 @@ if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) {

if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
return '';
return "";
}

@@ -208,3 +205,3 @@ }

// 输出时需要转义<>"
// escape `<>"` before returns
value = escapeAttrValue(value);

@@ -214,3 +211,3 @@ return value;

// 正则表达式
// RegExp list
var REGEXP_LT = /</g;

@@ -220,14 +217,14 @@ var REGEXP_GT = />/g;

var REGEXP_QUOTE_2 = /&quot;/g;
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/img;
var REGEXP_ATTR_VALUE_COLON = /&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 = /&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
*

@@ -237,8 +234,8 @@ * @param {String} str

*/
function escapeQuote (str) {
return str.replace(REGEXP_QUOTE, '&quot;');
function escapeQuote(str) {
return str.replace(REGEXP_QUOTE, "&quot;");
}
/**
* 对双引号进行转义
* unescape double quote
*

@@ -248,3 +245,3 @@ * @param {String} str

*/
function unescapeQuote (str) {
function unescapeQuote(str) {
return str.replace(REGEXP_QUOTE_2, '"');

@@ -254,3 +251,3 @@ }

/**
* 对html实体编码进行转义
* escape html entities
*

@@ -260,7 +257,7 @@ * @param {String} str

*/
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));
});

@@ -270,3 +267,3 @@ }

/**
* 对html5新增的危险实体编码进行转义
* escape html5 new danger entities
*

@@ -276,9 +273,10 @@ * @param {String} str

*/
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, " ");
}
/**
* 清除不可见字符
* clear nonprintable characters
*

@@ -288,6 +286,6 @@ * @param {String} str

*/
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);
}

@@ -298,3 +296,3 @@ return _.trim(str2);

/**
* 将标签的属性值转换成一般字符,便于分析
* get friendly attribute value
*

@@ -304,7 +302,7 @@ * @param {String} str

*/
function friendlyAttrValue (str) {
str = unescapeQuote(str); // 双引号
str = escapeHtmlEntities(str); // 转换HTML实体编码
str = escapeDangerHtml5Entities(str); // 转换危险的HTML5新增实体编码
str = clearNonPrintableCharacter(str); // 清除不可见字符
function friendlyAttrValue(str) {
str = unescapeQuote(str);
str = escapeHtmlEntities(str);
str = escapeDangerHtml5Entities(str);
str = clearNonPrintableCharacter(str);
return str;

@@ -314,3 +312,3 @@ }

/**
* 转义用于输出的标签属性值
* unescape attribute value
*

@@ -320,3 +318,3 @@ * @param {String} str

*/
function escapeAttrValue (str) {
function escapeAttrValue(str) {
str = escapeQuote(str);

@@ -328,35 +326,39 @@ str = escapeHtml(str);

/**
* 去掉不在白名单中的标签onIgnoreTag处理方法
* `onIgnoreTag` function for removing all the tags that are not in whitelist
*/
function onIgnoreTagStripAll () {
return '';
function onIgnoreTagStripAll() {
return "";
}
/**
* 删除标签体
* remove tag body
* specify a `tags` list, if the tag is not in the `tags` list then process by the specify function (optional)
*
* @param {array} tags 要删除的标签列表
* @param {function} next 对不在列表中的标签的处理函数,可选
* @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; // 当前标签开始位置
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;

@@ -368,3 +370,3 @@ return ret;

}
return '[removed]';
return "[removed]";
}

@@ -375,6 +377,6 @@ } else {

},
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]);

@@ -390,3 +392,3 @@ lastPos = pos[1];

/**
* 去除备注标签
* remove html comments
*

@@ -396,4 +398,4 @@ * @param {String} html

*/
function stripCommentTag (html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP, '');
function stripCommentTag(html) {
return html.replace(STRIP_COMMENT_TAG_REGEXP, "");
}

@@ -403,3 +405,3 @@ var STRIP_COMMENT_TAG_REGEXP = /<!--[\s\S]*?-->/g;

/**
* 去除不可见字符
* remove invisible characters
*

@@ -409,5 +411,5 @@ * @param {String} html

*/
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);

@@ -421,6 +423,5 @@ if (c === 127) return false;

});
return chars.join('');
return chars.join("");
}
exports.whiteList = getDefaultWhiteList();

@@ -427,0 +428,0 @@ exports.getDefaultWhiteList = getDefaultWhiteList;

/**
* 模块入口
* xss
*
* @author 老雷<leizongmin@gmail.com>
* @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");
/**
* XSS过滤
* filter xss function
*
* @param {String} html 要过滤的HTML代码
* @param {Object} options 选项:whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml
* @param {String} html
* @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);

@@ -24,4 +23,2 @@ return xss.process(html);

// 输出
exports = module.exports = filterXSS;

@@ -32,6 +29,5 @@ exports.FilterXSS = FilterXSS;

// 在浏览器端使用
if (typeof window !== 'undefined') {
// using `xss` on the browser, output `filterXSS` to the globals
if (typeof window !== "undefined") {
window.filterXSS = module.exports;
}
/**
* 简单 HTML Parser
* Simple HTML Parser
*
* @author 老雷<leizongmin@gmail.com>
* @author Zongmin Lei<leizongmin@gmail.com>
*/
var _ = require('./util');
var _ = require("./util");
/**
* 获取标签的名称
* get tag name
*
* @param {String} html 如:'<a hef="#">'
* @param {String} html e.g. '<a hef="#">'
* @return {String}
*/
function getTagName (html) {
function getTagName(html) {
var i = _.spaceIndex(html);

@@ -23,4 +23,4 @@ if (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;

@@ -30,3 +30,3 @@ }

/**
* 是否为闭合标签
* is close tag?
*

@@ -36,32 +36,29 @@ * @param {String} html 如:'<a hef="#">'

*/
function isClosing (html) {
return (html.slice(0, 2) === '</');
function isClosing(html) {
return html.slice(0, 2) === "</";
}
/**
* 分析HTML代码,调用相应的函数处理,返回处理后的HTML
* parse input html and returns processed html
*
* @param {String} html
* @param {Function} onTag 处理标签的函数
* 参数格式: function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml 对HTML进行转义的函数
* @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
* @param {Function} escapeHtml
* @return {String}
*/
function parseTag (html, onTag, escapeHtml) {
'user strict';
function parseTag(html, onTag, escapeHtml) {
"user strict";
var rethtml = ''; // 待返回的HTML
var lastPos = 0; // 上一个标签结束位置
var tagStart = false; // 当前标签开始位置
var quoteStart = false; // 引号开始位置
var currentPos = 0; // 当前位置
var len = html.length; // HTML长度
var currentHtml = ''; // 当前标签的HTML代码
var currentTagName = ''; // 当前标签的名称
var rethtml = "";
var lastPos = 0;
var tagStart = false;
var quoteStart = false;
var currentPos = 0;
var len = html.length;
var currentTagName = "";
// 逐个分析字符
for (currentPos = 0; currentPos < len; currentPos++) {
var c = html.charAt(currentPos);
if (tagStart === false) {
if (c === '<') {
if (c === "<") {
tagStart = currentPos;

@@ -72,3 +69,3 @@ continue;

if (quoteStart === false) {
if (c === '<') {
if (c === "<") {
rethtml += escapeHtml(html.slice(lastPos, currentPos));

@@ -79,11 +76,13 @@ tagStart = currentPos;

}
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;

@@ -93,4 +92,3 @@ tagStart = false;

}
// HTML标签内的引号仅当前一个字符是等于号时才有效
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === '=') {
if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === "=") {
quoteStart = c;

@@ -114,28 +112,26 @@ continue;

// 不符合属性名称规则的正则表达式
var REGEXP_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img;
var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/gim;
/**
* 分析标签HTML代码,调用相应的函数处理,返回HTML
* parse input attributes and returns processed attributes
*
* @param {String} html 如标签'<a href="#" target="_blank">' 则为 'href="#" target="_blank"'
* @param {Function} onAttr 处理属性值的函数
* 函数格式: function (name, value)
* @param {String} html e.g. `href="#" target="_blank"`
* @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; // HTML代码长度
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_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);
};
}

@@ -146,3 +142,3 @@ // 逐个分析字符

var v, j;
if (tmpName === false && c === '=') {
if (tmpName === false && c === "=") {
tmpName = html.slice(lastPos, i);

@@ -153,4 +149,7 @@ lastPos = i + 1;

if (tmpName !== false) {
// HTML标签内的引号仅当前一个字符是等于号时才有效
if (i === lastPos && (c === '"' || c === "'") && html.charAt(i - 1) === '=') {
if (
i === lastPos &&
(c === '"' || c === "'") &&
html.charAt(i - 1) === "="
) {
j = html.indexOf(c, i + 1);

@@ -170,3 +169,3 @@ if (j === -1) {

if (/\s|\n|\t/.test(c)) {
html = html.replace(/\s|\n|\t/g, ' ');
html = html.replace(/\s|\n|\t/g, " ");
if (tmpName === false) {

@@ -208,10 +207,10 @@ j = findNextEqual(html, i);

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;

@@ -221,7 +220,7 @@ }

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;

@@ -231,5 +230,7 @@ }

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;

@@ -239,5 +240,5 @@ } else {

}
};
}
function stripQuoteWrap (text) {
function stripQuoteWrap(text) {
if (isQuoteWrapString(text)) {

@@ -248,6 +249,5 @@ return text.substr(1, text.length - 2);

}
};
}
exports.parseTag = parseTag;
exports.parseAttr = parseAttr;
module.exports = {
indexOf: function (arr, item) {
indexOf: function(arr, item) {
var i, j;

@@ -14,3 +14,3 @@ if (Array.prototype.indexOf) {

},
forEach: function (arr, fn, scope) {
forEach: function(arr, fn, scope) {
var i, j;

@@ -24,13 +24,13 @@ if (Array.prototype.forEach) {

},
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;
}
};
/**
* 过滤XSS
* filter xss
*
* @author 老雷<leizongmin@gmail.com>
* @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`
*

@@ -21,8 +20,8 @@ * @param {Object} obj

*/
function isNull (obj) {
return (obj === undefined || obj === null);
function isNull(obj) {
return obj === undefined || obj === null;
}
/**
* 取标签内的属性列表字符串
* get attributes for a tag
*

@@ -34,15 +33,15 @@ * @param {String} html

*/
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,3 +52,3 @@ };

/**
* 浅拷贝对象
* shallow copy
*

@@ -59,3 +58,3 @@ * @param {Object} obj

*/
function shallowCopyObject (obj) {
function shallowCopyObject(obj) {
var ret = {};

@@ -69,11 +68,11 @@ for (var i in obj) {

/**
* XSS过滤对象
* FilterXSS class
*
* @param {Object} options
* 选项:whiteList, onTag, onTagAttr, onIgnoreTag,
* whiteList, onTag, onTagAttr, onIgnoreTag,
* onIgnoreTagAttr, safeAttrValue, escapeHtml
* stripIgnoreTagBody, allowCommentTag, stripBlankChar
* css{whiteList, onAttr, onIgnoreAttr} css=false表示禁用cssfilter
* css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
*/
function FilterXSS (options) {
function FilterXSS(options) {
options = shallowCopyObject(options || {});

@@ -83,3 +82,5 @@

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'
);
}

@@ -107,3 +108,3 @@ options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;

/**
* 开始处理
* start process and returns result
*

@@ -113,7 +114,7 @@ * @param {String} html

*/
FilterXSS.prototype.process = function (html) {
// 兼容各种奇葩输入
html = html || '';
FilterXSS.prototype.process = function(html) {
// compatible with the input
html = html || "";
html = html.toString();
if (!html) return '';
if (!html) return "";

@@ -131,3 +132,3 @@ var me = this;

// 是否清除不可见字符
// remove invisible characters
if (options.stripBlankChar) {

@@ -137,3 +138,3 @@ html = DEFAULT.stripBlankChar(html);

// 是否禁止备注标签
// remove html comments
if (!options.allowCommentTag) {

@@ -143,72 +144,72 @@ html = DEFAULT.stripCommentTag(html);

// 如果开启了stripIgnoreTagBody
// 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: whiteList.hasOwnProperty(tag)
};
// 调用onTag处理
var ret = onTag(tag, html, info);
if (!isNull(ret)) return ret;
// call `onTag()`
var ret = onTag(tag, html, info);
if (!isNull(ret)) return ret;
// 默认标签处理方法
if (info.isWhite) {
// 白名单标签,解析标签属性
// 如果是闭合标签,则不需要解析属性
if (info.isClosing) {
return '</' + tag + '>';
}
if (info.isWhite) {
if (info.isClosing) {
return "</" + tag + ">";
}
var attrs = getAttrs(html);
var whiteAttrList = whiteList[tag];
var attrsHtml = parseAttr(attrs.html, function (name, value) {
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;
// 调用onTagAttr处理
var isWhiteAttr = (_.indexOf(whiteAttrList, name) !== -1);
var ret = onTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
// 默认的属性处理方法
if (isWhiteAttr) {
// 白名单属性,调用safeAttrValue过滤属性值
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
return name + '="' + value + '"';
if (isWhiteAttr) {
// call `safeAttrValue()`
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
return name + '="' + value + '"';
} else {
return name;
}
} else {
return name;
// call `onIgnoreTagAttr()`
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
return;
}
} else {
// 非白名单属性,调用onIgnoreTagAttr处理
var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
if (!isNull(ret)) return ret;
return;
}
});
});
// 构造新的标签代码
var html = '<' + tag;
if (attrsHtml) html += ' ' + attrsHtml;
if (attrs.closing) html += ' /';
html += '>';
return html;
// 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
);
} else {
// 非白名单标签,调用onIgnoreTag处理
var ret = onIgnoreTag(tag, html, info);
if (!isNull(ret)) return ret;
return escapeHtml(html);
}
}, escapeHtml);
// 如果开启了stripIgnoreTagBody,需要对结果再进行处理
// if enable stripIgnoreTagBody
if (stripIgnoreTagBody) {

@@ -221,3 +222,2 @@ retHtml = stripIgnoreTagBody.remove(retHtml);

module.exports = FilterXSS;

@@ -5,3 +5,3 @@ {

"typings": "./typings/xss.d.ts",
"version": "0.3.5",
"version": "0.3.6",
"description": "Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist",

@@ -8,0 +8,0 @@ "author": "Zongmin Lei <leizongmin@gmail.com> (http://ucdok.com)",

+52
-62

@@ -23,8 +23,7 @@ [![NPM version][npm-image]][npm-url]

Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist.
======
# Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist.
![xss](https://nodei.co/npm/xss.png?downloads=true&stars=true)
--------------
---

@@ -40,32 +39,27 @@ `xss` is a module used to filter input from users to prevent XSS attacks.

---------------
---
## Features
+ Specifies HTML tags and their attributes allowed with whitelist
+ Handle any tags or attributes using custom function.
* Specifies HTML tags and their attributes allowed with whitelist
* Handle any tags or attributes using custom function.
## Reference
+ [XSS Filter Evasion Cheat Sheet](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)
+ [Data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme)
+ [XSS with Data URI Scheme](http://hi.baidu.com/badzzzz/item/bdbafe83144619c199255f7b)
* [XSS Filter Evasion Cheat Sheet](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)
* [Data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme)
* [XSS with Data URI Scheme](http://hi.baidu.com/badzzzz/item/bdbafe83144619c199255f7b)
## Benchmark (for references only)
+ the xss module: 8.2 MB/s
+ `xss()` function from module `validator@0.3.7`: 4.4 MB/s
* the xss module: 8.2 MB/s
* `xss()` function from module `validator@0.3.7`: 4.4 MB/s
For test code please refer to `benchmark` directory.
## They are using xss module
+ **nodeclub** - A Node.js bbs using MongoDB - https://github.com/cnodejs/nodeclub
+ **cnpmjs.org** - Private npm registry and web for Enterprise - https://github.com/cnpm/cnpmjs.org
* **nodeclub** - A Node.js bbs using MongoDB - https://github.com/cnodejs/nodeclub
* **cnpmjs.org** - Private npm registry and web for Enterprise - https://github.com/cnpm/cnpmjs.org
## Install

@@ -91,3 +85,2 @@

## Usages

@@ -98,3 +91,3 @@

```javascript
var xss = require('xss');
var xss = require("xss");
var html = xss('<script>alert("xss");</script>');

@@ -137,3 +130,2 @@ console.log(html);

## Command Line Tool

@@ -166,3 +158,2 @@

## Custom filter rules

@@ -174,3 +165,3 @@

```javascript
options = {}; // Custom rules
options = {}; // Custom rules
html = xss('<script>alert("xss");</script>', options);

@@ -183,3 +174,3 @@ ```

```javascript
options = {}; // Custom rules
options = {}; // Custom rules
myxss = new xss.FilterXSS(options);

@@ -201,3 +192,3 @@ // then apply myxss.process()

whiteList: {
a: ['href', 'title', 'target']
a: ["href", "title", "target"]
}

@@ -218,3 +209,3 @@ };

```javascript
function onTag (tag, html, options) {
function onTag(tag, html, options) {
// tag is the name of current tag, e.g. 'a' for tag <a>

@@ -239,3 +230,3 @@ // html is the HTML of this tag, e.g. '<a>' for tag <a>

```javascript
function onTagAttr (tag, name, value, isWhiteAttr) {
function onTagAttr(tag, name, value, isWhiteAttr) {
// tag is the name of current tag, e.g. 'a' for tag <a>

@@ -256,3 +247,3 @@ // name is the name of current attribute, e.g. 'href' for href="#"

```javascript
function onIgnoreTag (tag, html, options) {
function onIgnoreTag(tag, html, options) {
// Parameters are the same with onTag

@@ -270,3 +261,3 @@ // If a string is returned, the tag would be replaced with the string

```javascript
function onIgnoreTagAttr (tag, name, value, isWhiteAttr) {
function onIgnoreTagAttr(tag, name, value, isWhiteAttr) {
// Parameters are the same with onTagAttr

@@ -284,4 +275,4 @@ // If a string is returned, the value would be replaced with this string

```javascript
function escapeHtml (html) {
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;');
function escapeHtml(html) {
return html.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

@@ -295,3 +286,3 @@ ```

```javascript
function safeAttrValue (tag, name, value) {
function safeAttrValue(tag, name, value) {
// Parameters are the same with onTagAttr (without options)

@@ -312,3 +303,3 @@ // Return the value as a string

top: true,
left: true,
left: true
}

@@ -324,3 +315,3 @@ }

myxss = new xss.FilterXSS({
css: false,
css: false
});

@@ -337,4 +328,4 @@ ```

+ `true` filter out tags not in the whitelist
+ `false`: by default: escape the tag using configured `escape` function
* `true` filter out tags not in the whitelist
* `false`: by default: escape the tag using configured `escape` function

@@ -359,5 +350,5 @@ Example:

+ `false|null|undefined` by default: do nothing
+ `'*'|true`: filter out all tags not in the whitelist
+ `['tag1', 'tag2']`: filter out only specified tags not in the whitelist
* `false|null|undefined` by default: do nothing
* `'*'|true`: filter out all tags not in the whitelist
* `['tag1', 'tag2']`: filter out only specified tags not in the whitelist

@@ -382,4 +373,4 @@ Example:

+ `true`: do nothing
+ `false` by default: filter out HTML comments
* `true`: do nothing
* `false` by default: filter out HTML comments

@@ -400,3 +391,2 @@ Example:

## Examples

@@ -409,4 +399,4 @@

var html = xss(source, {
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
if (name.substr(0, 5) === 'data-') {
onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
if (name.substr(0, 5) === "data-") {
// escape its value using built-in escapeAttrValue function

@@ -418,3 +408,3 @@ return name + '="' + xss.escapeAttrValue(value) + '"';

console.log('%s\nconvert to:\n%s', source, html);
console.log("%s\nconvert to:\n%s", source, html);
```

@@ -433,6 +423,6 @@

```javascript
var source = '<x><x-1>he<x-2 checked></x-2>wwww</x-1><a>';
var source = "<x><x-1>he<x-2 checked></x-2>wwww</x-1><a>";
var html = xss(source, {
onIgnoreTag: function (tag, html, options) {
if (tag.substr(0, 2) === 'x-') {
onIgnoreTag: function(tag, html, options) {
if (tag.substr(0, 2) === "x-") {
// do not filter its attributes

@@ -444,3 +434,3 @@ return html;

console.log('%s\nconvert to:\n%s', source, html);
console.log("%s\nconvert to:\n%s", source, html);
```

@@ -459,7 +449,8 @@

```javascript
var source = '<img src="img1">a<img src="img2">b<img src="img3">c<img src="img4">d';
var source =
'<img src="img1">a<img src="img2">b<img src="img3">c<img src="img4">d';
var list = [];
var html = xss(source, {
onTagAttr: function (tag, name, value, isWhiteAttr) {
if (tag === 'img' && name === 'src') {
onTagAttr: function(tag, name, value, isWhiteAttr) {
if (tag === "img" && name === "src") {
// Use the built-in friendlyAttrValue function to escape attribute

@@ -474,3 +465,3 @@ // values. It supports converting entity tags such as &lt; to printable

console.log('image list:\n%s', list.join(', '));
console.log("image list:\n%s", list.join(", "));
```

@@ -488,11 +479,11 @@

```javascript
var source = '<strong>hello</strong><script>alert(/xss/);</script>end';
var source = "<strong>hello</strong><script>alert(/xss/);</script>end";
var html = xss(source, {
whiteList: [], // empty, means filter out all tags
stripIgnoreTag: true, // filter out all HTML not in the whilelist
stripIgnoreTagBody: ['script'] // the script tag is a special case, we need
// to filter out its content
whiteList: [], // empty, means filter out all tags
stripIgnoreTag: true, // filter out all HTML not in the whilelist
stripIgnoreTagBody: ["script"] // the script tag is a special case, we need
// to filter out its content
});
console.log('text: %s', html);
console.log("text: %s", html);
```

@@ -506,7 +497,6 @@

## License
```
Copyright (c) 2012-2016 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
```text
Copyright (c) 2012-2017 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
http://ucdok.com

@@ -513,0 +503,0 @@

@@ -23,13 +23,9 @@ [![NPM version][npm-image]][npm-url]

根据白名单过滤HTML(防止XSS攻击)
======
# 根据白名单过滤 HTML(防止 XSS 攻击)
![xss](https://nodei.co/npm/xss.png?downloads=true&stars=true)
--------------
---
`xss`是一个用于对用户输入的内容进行过滤,以避免遭受XSS攻击的模块
([什么是XSS攻击?](http://baike.baidu.com/view/2161269.htm))。主要用于论坛、博客、网上商店等等一些可允许用户录入页面排版、
格式控制相关的HTML的场景,`xss`模块通过白名单来控制允许的标签及相关的标签属性,
另外还提供了一系列的接口以便用户扩展,比其他同类模块更为灵活。
`xss`是一个用于对用户输入的内容进行过滤,以避免遭受 XSS 攻击的模块([什么是 XSS 攻击?](http://baike.baidu.com/view/2161269.htm))。主要用于论坛、博客、网上商店等等一些可允许用户录入页面排版、格式控制相关的 HTML 的场景,`xss`模块通过白名单来控制允许的标签及相关的标签属性,另外还提供了一系列的接口以便用户扩展,比其他同类模块更为灵活。

@@ -40,29 +36,25 @@ **项目主页:** http://jsxss.com

---------------
---
## 特性
+ 白名单控制允许的HTML标签及各标签的属性
+ 通过自定义处理函数,可对任意标签及其属性进行处理
* 白名单控制允许的 HTML 标签及各标签的属性
* 通过自定义处理函数,可对任意标签及其属性进行处理
## 参考资料
+ [XSS与字符编码的那些事儿 ---科普文](http://drops.wooyun.org/tips/689)
+ [腾讯实例教程:那些年我们一起学XSS](http://www.wooyun.org/whitehats/%E5%BF%83%E4%BC%A4%E7%9A%84%E7%98%A6%E5%AD%90)
+ [mXSS攻击的成因及常见种类](http://drops.wooyun.org/tips/956)
+ [XSS Filter Evasion Cheat Sheet](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)
+ [Data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme)
+ [XSS with Data URI Scheme](http://hi.baidu.com/badzzzz/item/bdbafe83144619c199255f7b)
* [XSS 与字符编码的那些事儿 ---科普文](http://drops.wooyun.org/tips/689)
* [腾讯实例教程:那些年我们一起学 XSS](http://www.wooyun.org/whitehats/%E5%BF%83%E4%BC%A4%E7%9A%84%E7%98%A6%E5%AD%90)
* [mXSS 攻击的成因及常见种类](http://drops.wooyun.org/tips/956)
* [XSS Filter Evasion Cheat Sheet](https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet)
* [Data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme)
* [XSS with Data URI Scheme](http://hi.baidu.com/badzzzz/item/bdbafe83144619c199255f7b)
## 性能(仅作参考)
+ xss模块:8.2 MB/s
+ validator@0.3.7模块的xss()函数:4.4 MB/s
* xss 模块:8.2 MB/s
* validator@0.3.7 模块的 xss()函数:4.4 MB/s
测试代码参考 benchmark 目录
## 安装

@@ -88,9 +80,8 @@

## 使用方法
### 在Node.js中使用
### 在 Node.js 中使用
```javascript
var xss = require('xss');
var xss = require("xss");
var html = xss('<script>alert("xss");</script>');

@@ -102,3 +93,3 @@ console.log(html);

Shim模式(参考文件 `test/test.html`):
Shim 模式(参考文件 `test/test.html`):

@@ -114,3 +105,3 @@ ```html

AMD模式(参考文件 `test/test_amd.html`):
AMD 模式(参考文件 `test/test_amd.html`):

@@ -135,8 +126,7 @@ ```html

### 使用命令行工具来对文件进行 XSS 处理
### 使用命令行工具来对文件进行XSS处理
### 处理文件
可通过内置的 `xss` 命令来对输入的文件进行XSS处理。使用方法:
可通过内置的 `xss` 命令来对输入的文件进行 XSS 处理。使用方法:

@@ -155,3 +145,3 @@ ```bash

执行以下命令,可在命令行中输入HTML代码,并看到过滤后的代码:
执行以下命令,可在命令行中输入 HTML 代码,并看到过滤后的代码:

@@ -164,3 +154,2 @@ ```bash

## 自定义过滤规则

@@ -171,8 +160,7 @@

```javascript
options = {}; // 自定义规则
options = {}; // 自定义规则
html = xss('<script>alert("xss");</script>', options);
```
如果不想每次都传入一个 `options` 参数,可以创建一个 `FilterXSS` 实例
(使用这种方法速度更快):
如果不想每次都传入一个 `options` 参数,可以创建一个 `FilterXSS` 实例(使用这种方法速度更快):

@@ -190,4 +178,3 @@ ```

通过 `whiteList` 来指定,格式为:`{'标签名': ['属性1', '属性2']}`。不在白名单上
的标签将被过滤,不在白名单上的属性也会被过滤。以下是示例:
通过 `whiteList` 来指定,格式为:`{'标签名': ['属性1', '属性2']}`。不在白名单上的标签将被过滤,不在白名单上的属性也会被过滤。以下是示例:

@@ -198,3 +185,3 @@ ```javascript

whiteList: {
a: ['href', 'title', 'target']
a: ["href", "title", "target"]
}

@@ -215,3 +202,3 @@ };

```javascript
function onTag (tag, html, options) {
function onTag(tag, html, options) {
// tag是当前的标签名称,比如<a>标签,则tag的值是'a'

@@ -236,3 +223,3 @@ // html是该标签的HTML,比如<a>标签,则html的值是'<a>'

```javascript
function onTagAttr (tag, name, value, isWhiteAttr) {
function onTagAttr(tag, name, value, isWhiteAttr) {
// tag是当前的标签名称,比如<a>标签,则tag的值是'a'

@@ -254,3 +241,3 @@ // name是当前属性的名称,比如href="#",则name的值是'href'

```javascript
function onIgnoreTag (tag, html, options) {
function onIgnoreTag(tag, html, options) {
// 参数说明与onTag相同

@@ -267,3 +254,3 @@ // 如果返回一个字符串,则当前标签将被替换为该字符串

```javascript
function onIgnoreTagAttr (tag, name, value, isWhiteAttr) {
function onIgnoreTagAttr(tag, name, value, isWhiteAttr) {
// 参数说明与onTagAttr相同

@@ -275,3 +262,3 @@ // 如果返回一个字符串,则当前属性值将被替换为该字符串

### 自定义HTML转义函数
### 自定义 HTML 转义函数

@@ -281,4 +268,4 @@ 通过 `escapeHtml` 来指定相应的处理函数。以下是默认代码 **(不建议修改)** :

```javascript
function escapeHtml (html) {
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;');
function escapeHtml(html) {
return html.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

@@ -292,3 +279,3 @@ ```

```javascript
function safeAttrValue (tag, name, value) {
function safeAttrValue(tag, name, value) {
// 参数说明与onTagAttr相同(没有options参数)

@@ -299,6 +286,6 @@ // 返回一个字符串表示该属性值

### 自定义CSS过滤器
### 自定义 CSS 过滤器
如果配置中允许了标签的 `style` 属性,则它的值会通过[cssfilter](https://github.com/leizongmin/js-css-filter) 模块处理。
`cssfilter` 模块包含了一个默认的CSS白名单,你可以通过以下的方式配置:
`cssfilter` 模块包含了一个默认的 CSS 白名单,你可以通过以下的方式配置:

@@ -311,3 +298,3 @@ ```javascript

top: true,
left: true,
left: true
}

@@ -323,3 +310,3 @@ }

myxss = new xss.FilterXSS({
css: false,
css: false
});

@@ -336,4 +323,4 @@ ```

+ `true`:去掉不在白名单上的标签
+ `false`:(默认),使用配置的`escape`函数对该标签进行转义
* `true`:去掉不在白名单上的标签
* `false`:(默认),使用配置的`escape`函数对该标签进行转义

@@ -358,5 +345,5 @@ 示例:

+ `false|null|undefined`:(默认),不特殊处理
+ `'*'|true`:去掉所有不在白名单上的标签
+ `['tag1', 'tag2']`:仅去掉指定的不在白名单上的标签
* `false|null|undefined`:(默认),不特殊处理
* `'*'|true`:去掉所有不在白名单上的标签
* `['tag1', 'tag2']`:仅去掉指定的不在白名单上的标签

@@ -377,8 +364,8 @@ 示例:

#### 去掉HTML备注
#### 去掉 HTML 备注
通过 `allowCommentTag` 来设置:
+ `true`:不处理
+ `false`:(默认),自动去掉HTML中的备注
* `true`:不处理
* `false`:(默认),自动去掉 HTML 中的备注

@@ -399,6 +386,5 @@ 示例:

## 应用实例
### 允许标签以data-开头的属性
### 允许标签以 data-开头的属性

@@ -408,4 +394,4 @@ ```javascript

var html = xss(source, {
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
if (name.substr(0, 5) === 'data-') {
onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
if (name.substr(0, 5) === "data-") {
// 通过内置的escapeAttrValue函数来对属性值进行转义

@@ -417,3 +403,3 @@ return name + '="' + xss.escapeAttrValue(value) + '"';

console.log('%s\nconvert to:\n%s', source, html);
console.log("%s\nconvert to:\n%s", source, html);
```

@@ -429,9 +415,9 @@

### 允许名称以x-开头的标签
### 允许名称以 x-开头的标签
```javascript
var source = '<x><x-1>he<x-2 checked></x-2>wwww</x-1><a>';
var source = "<x><x-1>he<x-2 checked></x-2>wwww</x-1><a>";
var html = xss(source, {
onIgnoreTag: function (tag, html, options) {
if (tag.substr(0, 2) === 'x-') {
onIgnoreTag: function(tag, html, options) {
if (tag.substr(0, 2) === "x-") {
// 不对其属性列表进行过滤

@@ -443,3 +429,3 @@ return html;

console.log('%s\nconvert to:\n%s', source, html);
console.log("%s\nconvert to:\n%s", source, html);
```

@@ -455,10 +441,11 @@

### 分析HTML代码中的图片列表
### 分析 HTML 代码中的图片列表
```javascript
var source = '<img src="img1">a<img src="img2">b<img src="img3">c<img src="img4">d';
var source =
'<img src="img1">a<img src="img2">b<img src="img3">c<img src="img4">d';
var list = [];
var html = xss(source, {
onTagAttr: function (tag, name, value, isWhiteAttr) {
if (tag === 'img' && name === 'src') {
onTagAttr: function(tag, name, value, isWhiteAttr) {
if (tag === "img" && name === "src") {
// 使用内置的friendlyAttrValue函数来对属性值进行转义,可将&lt;这类的实体标记转换成打印字符<

@@ -471,3 +458,3 @@ list.push(xss.friendlyAttrValue(value));

console.log('image list:\n%s', list.join(', '));
console.log("image list:\n%s", list.join(", "));
```

@@ -482,13 +469,13 @@

### 去除HTML标签(只保留文本内容)
### 去除 HTML 标签(只保留文本内容)
```javascript
var source = '<strong>hello</strong><script>alert(/xss/);</script>end';
var source = "<strong>hello</strong><script>alert(/xss/);</script>end";
var html = xss(source, {
whiteList: [], // 白名单为空,表示过滤所有标签
stripIgnoreTag: true, // 过滤所有非白名单标签的HTML
stripIgnoreTagBody: ['script'] // script标签较特殊,需要过滤标签中间的内容
whiteList: [], // 白名单为空,表示过滤所有标签
stripIgnoreTag: true, // 过滤所有非白名单标签的HTML
stripIgnoreTagBody: ["script"] // script标签较特殊,需要过滤标签中间的内容
});
console.log('text: %s', html);
console.log("text: %s", html);
```

@@ -502,7 +489,6 @@

## 授权协议
```
Copyright (c) 2012-2016 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
```text
Copyright (c) 2012-2017 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
http://ucdok.com

@@ -509,0 +495,0 @@

/**
* xss
*
* @author 老雷<leizongmin@gmail.com>
* @author Zongmin Lei<leizongmin@gmail.com>
*/

@@ -100,3 +100,3 @@

}
}

@@ -103,0 +103,0 @@

Copyright (c) 2012-2016 Zongmin Lei(雷宗民) <leizongmin@gmail.com>
http://ucdok.com
The MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Sorry, the diff of this file is not supported yet