diff --git a/LICENSE.md b/LICENSE similarity index 94% rename from LICENSE.md rename to LICENSE index 9f3e4da..ba36ae1 100644 --- a/LICENSE.md +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2016 Zongmin Lei(雷宗民) +Copyright (c) 2012-2017 Zongmin Lei(雷宗民) http://ucdok.com The MIT License diff --git a/README.md b/README.md index f5fe881..356f6e5 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,11 @@ [download-url]: https://npmjs.org/package/xss [license-image]: https://img.shields.io/npm/l/xss.svg -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) --------------- +--- `xss` is a module used to filter input from users to prevent XSS attacks. ([What is XSS attack?](http://en.wikipedia.org/wiki/Cross-site_scripting)) @@ -36,35 +35,30 @@ Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whi **[中文版文档](https://github.com/leizongmin/js-xss/blob/master/README.zh.md)** ---------------- - +--- ## 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 @@ -86,13 +80,12 @@ Or $ bower install https://github.com/leizongmin/js-xss.git ``` - ## Usages ### On Node.js ```javascript -var xss = require('xss'); +var xss = require("xss"); var html = xss(''); console.log(html); ``` @@ -130,7 +123,6 @@ require(['xss'], function (xss) { ``` - ## Command Line Tool ### Process File @@ -158,14 +150,13 @@ $ xss -t For more details, please run `$ xss -h` to see it. - ## Custom filter rules When using the `xss()` function, the second parameter could be used to specify custom rules: ```javascript -options = {}; // Custom rules +options = {}; // Custom rules html = xss('', options); ``` @@ -173,7 +164,7 @@ To avoid passing `options` every time, you can also do it in a faster way by creating a `FilterXSS` instance: ```javascript -options = {}; // Custom rules +options = {}; // Custom rules myxss = new xss.FilterXSS(options); // then apply myxss.process() html = myxss.process(''); @@ -190,7 +181,7 @@ and attributes not in the whitelist would be filter out. For example: // only tag a and its attributes href, title, target are allowed var options = { whiteList: { - a: ['href', 'title', 'target'] + a: ["href", "title", "target"] } }; // With the configuration specified above, the following HTML: @@ -206,7 +197,7 @@ For the default whitelist, please refer `xss.whiteList`. By specifying the handler function with `onTag`: ```javascript -function onTag (tag, html, options) { +function onTag(tag, html, options) { // tag is the name of current tag, e.g. 'a' for tag // html is the HTML of this tag, e.g. '' for tag // options is some addition informations: @@ -226,7 +217,7 @@ function onTag (tag, html, options) { By specifying the handler function with `onTagAttr`: ```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 // name is the name of current attribute, e.g. 'href' for href="#" // isWhiteAttr whether the attribute is in whitelist @@ -242,7 +233,7 @@ function onTagAttr (tag, name, value, isWhiteAttr) { By specifying the handler function with `onIgnoreTag`: ```javascript -function onIgnoreTag (tag, html, options) { +function onIgnoreTag(tag, html, options) { // Parameters are the same with onTag // If a string is returned, the tag would be replaced with the string // If return nothing, the default measure would be taken (specifies using @@ -255,7 +246,7 @@ function onIgnoreTag (tag, html, options) { By specifying the handler function with `onIgnoreTagAttr`: ```javascript -function onIgnoreTagAttr (tag, name, value, isWhiteAttr) { +function onIgnoreTagAttr(tag, name, value, isWhiteAttr) { // Parameters are the same with onTagAttr // If a string is returned, the value would be replaced with this string // If return nothing, then keep default (remove the attribute) @@ -268,8 +259,8 @@ By specifying the handler function with `escapeHtml`. Following is the default function **(Modification is not recommended)**: ```javascript -function escapeHtml (html) { - return html.replace(//g, '>'); +function escapeHtml(html) { + return html.replace(//g, ">"); } ``` @@ -278,7 +269,7 @@ function escapeHtml (html) { By specifying the handler function with `safeAttrValue`: ```javascript -function safeAttrValue (tag, name, value) { +function safeAttrValue(tag, name, value) { // Parameters are the same with onTagAttr (without options) // Return the value as a string } @@ -294,7 +285,7 @@ myxss = new xss.FilterXSS({ whiteList: { position: /^fixed|relative$/, top: true, - left: true, + left: true } } }); @@ -305,7 +296,7 @@ If you don't want to filter out the `style` content, just specify `false` to the ```javascript myxss = new xss.FilterXSS({ - css: false, + css: false }); ``` @@ -317,8 +308,8 @@ For more help, please see https://github.com/leizongmin/js-css-filter By using `stripIgnoreTag` parameter: -+ `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 Example: @@ -338,9 +329,9 @@ code:alert(/xss/); By using `stripIgnoreTagBody` parameter: -+ `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 Example: @@ -360,8 +351,8 @@ code: By using `allowCommentTag` parameter: -+ `true`: do nothing -+ `false` by default: filter out HTML comments +* `true`: do nothing +* `false` by default: filter out HTML comments Example: @@ -377,7 +368,6 @@ would output filtered: code: END ``` - ## Examples ### Allow attributes of whitelist tags start with `data-` @@ -385,15 +375,15 @@ code: END ```javascript var source = '
hello
'; 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 return name + '="' + xss.escapeAttrValue(value) + '"'; } } }); -console.log('%s\nconvert to:\n%s', source, html); +console.log("%s\nconvert to:\n%s", source, html); ``` Result: @@ -407,17 +397,17 @@ convert to: ### Allow tags start with `x-` ```javascript -var source = 'hewwww
'; +var source = "hewwww"; 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 return html; } } }); -console.log('%s\nconvert to:\n%s', source, html); +console.log("%s\nconvert to:\n%s", source, html); ``` Result: @@ -431,11 +421,12 @@ convert to: ### Parse images in HTML ```javascript -var source = 'abcd'; +var source = + 'abcd'; 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 // values. It supports converting entity tags such as < to printable // characters such as < @@ -445,7 +436,7 @@ var html = xss(source, { } }); -console.log('image list:\n%s', list.join(', ')); +console.log("image list:\n%s", list.join(", ")); ``` Result: @@ -458,15 +449,15 @@ img1, img2, img3, img4 ### Filter out HTML tags (keeps only plain text) ```javascript -var source = 'helloend'; +var source = "helloend"; 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); ``` Result: @@ -475,11 +466,10 @@ Result: text: helloend ``` - ## License -``` -Copyright (c) 2012-2016 Zongmin Lei(雷宗民) +```text +Copyright (c) 2012-2017 Zongmin Lei(雷宗民) http://ucdok.com The MIT License diff --git a/README.zh.md b/README.zh.md index 221731c..3e70a72 100644 --- a/README.zh.md +++ b/README.zh.md @@ -20,49 +20,41 @@ [download-url]: https://npmjs.org/package/xss [license-image]: https://img.shields.io/npm/l/xss.svg -根据白名单过滤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`模块通过白名单来控制允许的标签及相关的标签属性,另外还提供了一系列的接口以便用户扩展,比其他同类模块更为灵活。 **项目主页:** http://jsxss.com **在线测试:** http://jsxss.com/zh/try.html ---------------- - +--- ## 特性 -+ 白名单控制允许的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 目录 - ## 安装 ### NPM @@ -83,20 +75,19 @@ $ bower install xss $ bower install https://github.com/leizongmin/js-xss.git ``` - ## 使用方法 -### 在Node.js中使用 +### 在 Node.js 中使用 ```javascript -var xss = require('xss'); +var xss = require("xss"); var html = xss(''); console.log(html); ``` ### 在浏览器端使用 -Shim模式(参考文件 `test/test.html`): +Shim 模式(参考文件 `test/test.html`): ```html @@ -107,7 +98,7 @@ alert(html); ``` -AMD模式(参考文件 `test/test_amd.html`): +AMD 模式(参考文件 `test/test_amd.html`): ```html ``` - -### 使用命令行工具来对文件进行XSS处理 +### 使用命令行工具来对文件进行 XSS 处理 ### 处理文件 -可通过内置的 `xss` 命令来对输入的文件进行XSS处理。使用方法: +可通过内置的 `xss` 命令来对输入的文件进行 XSS 处理。使用方法: ```bash xss -i <源文件> -o <目标文件> @@ -146,7 +136,7 @@ $ xss -i origin.html -o target.html ### 在线测试 -执行以下命令,可在命令行中输入HTML代码,并看到过滤后的代码: +执行以下命令,可在命令行中输入 HTML 代码,并看到过滤后的代码: ```bash $ xss -t @@ -154,18 +144,16 @@ $ xss -t 详细命令行参数说明,请输入 `$ xss -h` 来查看。 - ## 自定义过滤规则 在调用 `xss()` 函数进行过滤时,可通过第二个参数来设置自定义规则: ```javascript -options = {}; // 自定义规则 +options = {}; // 自定义规则 html = xss('', options); ``` -如果不想每次都传入一个 `options` 参数,可以创建一个 `FilterXSS` 实例 -(使用这种方法速度更快): +如果不想每次都传入一个 `options` 参数,可以创建一个 `FilterXSS` 实例(使用这种方法速度更快): ``` options = {}; // 自定义规则 @@ -178,14 +166,13 @@ html = myxss.process(''); ### 白名单 -通过 `whiteList` 来指定,格式为:`{'标签名': ['属性1', '属性2']}`。不在白名单上 -的标签将被过滤,不在白名单上的属性也会被过滤。以下是示例: +通过 `whiteList` 来指定,格式为:`{'标签名': ['属性1', '属性2']}`。不在白名单上的标签将被过滤,不在白名单上的属性也会被过滤。以下是示例: ```javascript // 只允许a标签,该标签只允许href, title, target这三个属性 var options = { whiteList: { - a: ['href', 'title', 'target'] + a: ["href", "title", "target"] } }; // 使用以上配置后,下面的HTML @@ -201,7 +188,7 @@ var options = { 通过 `onTag` 来指定相应的处理函数。以下是详细说明: ```javascript -function onTag (tag, html, options) { +function onTag(tag, html, options) { // tag是当前的标签名称,比如标签,则tag的值是'a' // html是该标签的HTML,比如标签,则html的值是'' // options是一些附加的信息,具体如下: @@ -221,7 +208,7 @@ function onTag (tag, html, options) { 通过 `onTagAttr` 来指定相应的处理函数。以下是详细说明: ```javascript -function onTagAttr (tag, name, value, isWhiteAttr) { +function onTagAttr(tag, name, value, isWhiteAttr) { // tag是当前的标签名称,比如标签,则tag的值是'a' // name是当前属性的名称,比如href="#",则name的值是'href' // value是当前属性的值,比如href="#",则value的值是'#' @@ -238,7 +225,7 @@ function onTagAttr (tag, name, value, isWhiteAttr) { 通过 `onIgnoreTag` 来指定相应的处理函数。以下是详细说明: ```javascript -function onIgnoreTag (tag, html, options) { +function onIgnoreTag(tag, html, options) { // 参数说明与onTag相同 // 如果返回一个字符串,则当前标签将被替换为该字符串 // 如果不返回任何值,则使用默认的处理方法(通过escape指定,详见下文) @@ -250,20 +237,20 @@ function onIgnoreTag (tag, html, options) { 通过 `onIgnoreTagAttr` 来指定相应的处理函数。以下是详细说明: ```javascript -function onIgnoreTagAttr (tag, name, value, isWhiteAttr) { +function onIgnoreTagAttr(tag, name, value, isWhiteAttr) { // 参数说明与onTagAttr相同 // 如果返回一个字符串,则当前属性值将被替换为该字符串 // 如果不返回任何值,则使用默认的处理方法(删除该属) } ``` -### 自定义HTML转义函数 +### 自定义 HTML 转义函数 通过 `escapeHtml` 来指定相应的处理函数。以下是默认代码 **(不建议修改)** : ```javascript -function escapeHtml (html) { - return html.replace(//g, '>'); +function escapeHtml(html) { + return html.replace(//g, ">"); } ``` @@ -272,16 +259,16 @@ function escapeHtml (html) { 通过 `safeAttrValue` 来指定相应的处理函数。以下是详细说明: ```javascript -function safeAttrValue (tag, name, value) { +function safeAttrValue(tag, name, value) { // 参数说明与onTagAttr相同(没有options参数) // 返回一个字符串表示该属性值 } ``` -### 自定义CSS过滤器 +### 自定义 CSS 过滤器 如果配置中允许了标签的 `style` 属性,则它的值会通过[cssfilter](https://github.com/leizongmin/js-css-filter) 模块处理。 -`cssfilter` 模块包含了一个默认的CSS白名单,你可以通过以下的方式配置: +`cssfilter` 模块包含了一个默认的 CSS 白名单,你可以通过以下的方式配置: ```javascript myxss = new xss.FilterXSS({ @@ -289,7 +276,7 @@ myxss = new xss.FilterXSS({ whiteList: { position: /^fixed|relative$/, top: true, - left: true, + left: true } } }); @@ -300,7 +287,7 @@ html = myxss.process(''); ```javascript myxss = new xss.FilterXSS({ - css: false, + css: false }); ``` @@ -312,8 +299,8 @@ myxss = new xss.FilterXSS({ 通过 `stripIgnoreTag` 来设置: -+ `true`:去掉不在白名单上的标签 -+ `false`:(默认),使用配置的`escape`函数对该标签进行转义 +* `true`:去掉不在白名单上的标签 +* `false`:(默认),使用配置的`escape`函数对该标签进行转义 示例: @@ -333,9 +320,9 @@ code:alert(/xss/); 通过 `stripIgnoreTagBody` 来设置: -+ `false|null|undefined`:(默认),不特殊处理 -+ `'*'|true`:去掉所有不在白名单上的标签 -+ `['tag1', 'tag2']`:仅去掉指定的不在白名单上的标签 +* `false|null|undefined`:(默认),不特殊处理 +* `'*'|true`:去掉所有不在白名单上的标签 +* `['tag1', 'tag2']`:仅去掉指定的不在白名单上的标签 示例: @@ -351,12 +338,12 @@ code: code: ``` -#### 去掉HTML备注 +#### 去掉 HTML 备注 通过 `allowCommentTag` 来设置: -+ `true`:不处理 -+ `false`:(默认),自动去掉HTML中的备注 +* `true`:不处理 +* `false`:(默认),自动去掉 HTML 中的备注 示例: @@ -372,23 +359,22 @@ code: END code: END ``` - ## 应用实例 -### 允许标签以data-开头的属性 +### 允许标签以 data-开头的属性 ```javascript var source = '
hello
'; 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函数来对属性值进行转义 return name + '="' + xss.escapeAttrValue(value) + '"'; } } }); -console.log('%s\nconvert to:\n%s', source, html); +console.log("%s\nconvert to:\n%s", source, html); ``` 运行结果: @@ -399,20 +385,20 @@ convert to:
hello
``` -### 允许名称以x-开头的标签 +### 允许名称以 x-开头的标签 ```javascript -var source = 'hewwww
'; +var source = "hewwww"; 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-") { // 不对其属性列表进行过滤 return html; } } }); -console.log('%s\nconvert to:\n%s', source, html); +console.log("%s\nconvert to:\n%s", source, html); ``` 运行结果: @@ -423,14 +409,15 @@ convert to: <x>hewwww ``` -### 分析HTML代码中的图片列表 +### 分析 HTML 代码中的图片列表 ```javascript -var source = 'abcd'; +var source = + 'abcd'; 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函数来对属性值进行转义,可将<这类的实体标记转换成打印字符< list.push(xss.friendlyAttrValue(value)); } @@ -438,7 +425,7 @@ var html = xss(source, { } }); -console.log('image list:\n%s', list.join(', ')); +console.log("image list:\n%s", list.join(", ")); ``` 运行结果: @@ -448,17 +435,17 @@ image list: img1, img2, img3, img4 ``` -### 去除HTML标签(只保留文本内容) +### 去除 HTML 标签(只保留文本内容) ```javascript -var source = 'helloend'; +var source = "helloend"; 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); ``` 运行结果: @@ -467,11 +454,10 @@ console.log('text: %s', html); text: helloend ``` - ## 授权协议 -``` -Copyright (c) 2012-2016 Zongmin Lei(雷宗民) +```text +Copyright (c) 2012-2017 Zongmin Lei(雷宗民) http://ucdok.com The MIT License diff --git a/bin/xss b/bin/xss index 225a1b1..35e902f 100755 --- a/bin/xss +++ b/bin/xss @@ -3,7 +3,7 @@ /** * 命令行工具 * - * @author 老雷 + * @author Zongmin Lei */ var fs = require('fs'); diff --git a/changelogs.md b/changelogs.md deleted file mode 100644 index 778fd66..0000000 --- a/changelogs.md +++ /dev/null @@ -1,4 +0,0 @@ -## v0.2.9 - -+ `href`属性默认允许`#`开头的值 - diff --git a/dist/xss.js b/dist/xss.js index f17f1e3..dc0bfbd 100644 --- a/dist/xss.js +++ b/dist/xss.js @@ -2,7 +2,7 @@ /** * 默认配置 * - * @author 老雷 + * @author Zongmin Lei */ var FilterCSS = require('cssfilter').FilterCSS; @@ -418,7 +418,7 @@ exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList; /** * 模块入口 * - * @author 老雷 + * @author Zongmin Lei */ var DEFAULT = require('./default'); @@ -455,7 +455,7 @@ if (typeof window !== 'undefined') { /** * 简单 HTML Parser * - * @author 老雷 + * @author Zongmin Lei */ var _ = require('./util'); @@ -731,7 +731,7 @@ module.exports = { /** * 过滤XSS * - * @author 老雷 + * @author Zongmin Lei */ var FilterCSS = require('cssfilter').FilterCSS; @@ -944,7 +944,7 @@ module.exports = FilterXSS; /** * cssfilter * - * @author 老雷 + * @author Zongmin Lei */ var DEFAULT = require('./default'); @@ -1056,7 +1056,7 @@ module.exports = FilterCSS; /** * cssfilter * - * @author 老雷 + * @author Zongmin Lei */ function getDefaultWhiteList () { @@ -1456,7 +1456,7 @@ exports.safeAttrValue = safeAttrValue; /** * cssfilter * - * @author 老雷 + * @author Zongmin Lei */ var DEFAULT = require('./default'); @@ -1490,7 +1490,7 @@ if (typeof window !== 'undefined') { /** * cssfilter * - * @author 老雷 + * @author Zongmin Lei */ var _ = require('./util'); diff --git a/example/allows_attr_prefix.js b/example/allows_attr_prefix.js index 6d27bcf..5d47863 100644 --- a/example/allows_attr_prefix.js +++ b/example/allows_attr_prefix.js @@ -1,7 +1,7 @@ /** * 应用实例:允许标签以data-开头的属性 * - * @author 老雷 + * @author Zongmin Lei */ var xss = require('../'); diff --git a/example/allows_tag_prefix.js b/example/allows_tag_prefix.js index ee03c36..0820564 100644 --- a/example/allows_tag_prefix.js +++ b/example/allows_tag_prefix.js @@ -1,7 +1,7 @@ /** * 应用实例:允许名称以x-开头的标签 * - * @author 老雷 + * @author Zongmin Lei */ var xss = require('../'); diff --git a/example/analyse_img_list.js b/example/analyse_img_list.js index 3183fba..b35d7b4 100644 --- a/example/analyse_img_list.js +++ b/example/analyse_img_list.js @@ -1,7 +1,7 @@ /** * 应用实例:分析HTML代码中的图片列表 * - * @author 老雷 + * @author Zongmin Lei */ var xss = require('../'); diff --git a/example/strip_tag.js b/example/strip_tag.js index 43e2bf1..c22645d 100644 --- a/example/strip_tag.js +++ b/example/strip_tag.js @@ -1,7 +1,7 @@ /** * 应用实例:去除HTML标签(只保留文本内容) * - * @author 老雷 + * @author Zongmin Lei */ var xss = require('../'); diff --git a/lib/cli.js b/lib/cli.js index 322f9d4..d7d2825 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,7 +1,7 @@ /** - * 命令行测试工具 + * command line tool * - * @author 老雷 + * @author Zongmin Lei */ var xss = require('./'); diff --git a/lib/default.js b/lib/default.js index d6c9eb2..98d48a7 100644 --- a/lib/default.js +++ b/lib/default.js @@ -1,14 +1,13 @@ /** - * 默认配置 + * default settings * - * @author 老雷 + * @author Zongmin Lei */ var FilterCSS = require('cssfilter').FilterCSS; var getDefaultCSSWhiteList = require('cssfilter').getDefaultWhiteList; var _ = require('./util'); -// 默认白名单 function getDefaultWhiteList () { return { a: ['target', 'href', 'title'], @@ -77,11 +76,10 @@ function getDefaultWhiteList () { }; } -// 默认CSS Filter var defaultCSSFilter = new FilterCSS(); /** - * 匹配到标签时的处理方法 + * default onTag function * * @param {String} tag * @param {String} html @@ -93,7 +91,7 @@ function onTag (tag, html, options) { } /** - * 匹配到不在白名单上的标签时的处理方法 + * default onIgnoreTag function * * @param {String} tag * @param {String} html @@ -105,7 +103,7 @@ function onIgnoreTag (tag, html, options) { } /** - * 匹配到标签属性时的处理方法 + * default onTagAttr function * * @param {String} tag * @param {String} name @@ -117,7 +115,7 @@ function onTagAttr (tag, name, value) { } /** - * 匹配到不在白名单上的标签属性时的处理方法 + * default onIgnoreTagAttr function * * @param {String} tag * @param {String} name @@ -129,7 +127,7 @@ function onIgnoreTagAttr (tag, name, value) { } /** - * HTML转义 + * default escapeHtml function * * @param {String} html */ @@ -138,7 +136,7 @@ function escapeHtml (html) { } /** - * 安全的标签属性值 + * default safeAttrValue function * * @param {String} tag * @param {String} name @@ -147,12 +145,12 @@ function escapeHtml (html) { * @return {String} */ function safeAttrValue (tag, name, value, cssFilter) { - // 转换为友好的属性值,再做判断 + // unescape attribute value firstly value = friendlyAttrValue(value); if (name === 'href' || name === 'src') { - // 过滤 href 和 src 属性 - // 仅允许 http:// | https:// | mailto: | / | # 开头的地址 + // 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://' || @@ -164,24 +162,19 @@ function safeAttrValue (tag, name, value, cssFilter) { return ''; } } else if (name === 'background') { - // 过滤 background 属性 (这个xss漏洞较老了,可能已经不适用) - // javascript: + // 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 ''; } } else if (name === 'style') { - // /*注释*/ - /*REGEXP_DEFAULT_ON_TAG_ATTR_3.lastIndex = 0; - if (REGEXP_DEFAULT_ON_TAG_ATTR_3.test(value)) { - return ''; - }*/ - // expression() + // `expression()` REGEXP_DEFAULT_ON_TAG_ATTR_7.lastIndex = 0; if (REGEXP_DEFAULT_ON_TAG_ATTR_7.test(value)) { return ''; } - // url() + // `url()` REGEXP_DEFAULT_ON_TAG_ATTR_8.lastIndex = 0; if (REGEXP_DEFAULT_ON_TAG_ATTR_8.test(value)) { REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0; @@ -195,12 +188,12 @@ function safeAttrValue (tag, name, value, cssFilter) { } } - // 输出时需要转义<>" + // escape `<>"` before returns value = escapeAttrValue(value); return value; } -// 正则表达式 +// RegExp list var REGEXP_LT = //g; var REGEXP_QUOTE = /"/g; @@ -216,7 +209,7 @@ 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*\(.* var REGEXP_DEFAULT_ON_TAG_ATTR_8 = /u\s*r\s*l\s*\(.*/ig; /** - * 对双引号进行转义 + * escape doube quote * * @param {String} str * @return {String} str @@ -226,7 +219,7 @@ function escapeQuote (str) { } /** - * 对双引号进行转义 + * unescape double quote * * @param {String} str * @return {String} str @@ -236,7 +229,7 @@ function unescapeQuote (str) { } /** - * 对html实体编码进行转义 + * escape html entities * * @param {String} str * @return {String} @@ -250,7 +243,7 @@ function escapeHtmlEntities (str) { } /** - * 对html5新增的危险实体编码进行转义 + * escape html5 new danger entities * * @param {String} str * @return {String} @@ -261,7 +254,7 @@ function escapeDangerHtml5Entities (str) { } /** - * 清除不可见字符 + * clear nonprintable characters * * @param {String} str * @return {String} @@ -275,21 +268,21 @@ function clearNonPrintableCharacter (str) { } /** - * 将标签的属性值转换成一般字符,便于分析 + * get friendly attribute value * * @param {String} str * @return {String} */ function friendlyAttrValue (str) { - str = unescapeQuote(str); // 双引号 - str = escapeHtmlEntities(str); // 转换HTML实体编码 - str = escapeDangerHtml5Entities(str); // 转换危险的HTML5新增实体编码 - str = clearNonPrintableCharacter(str); // 清除不可见字符 + str = unescapeQuote(str); + str = escapeHtmlEntities(str); + str = escapeDangerHtml5Entities(str); + str = clearNonPrintableCharacter(str); return str; } /** - * 转义用于输出的标签属性值 + * unescape attribute value * * @param {String} str * @return {String} @@ -301,17 +294,18 @@ function escapeAttrValue (str) { } /** - * 去掉不在白名单中的标签onIgnoreTag处理方法 + * `onIgnoreTag` function for removing all the tags that are not in whitelist */ 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') { @@ -324,8 +318,8 @@ function StripTagBody (tags, next) { return (_.indexOf(tags, tag) !== -1); } - var removeList = []; // 要删除的位置范围列表 - var posStart = false; // 当前标签开始位置 + var removeList = []; + var posStart = false; return { onIgnoreTag: function (tag, html, options) { @@ -360,7 +354,7 @@ function StripTagBody (tags, next) { } /** - * 去除备注标签 + * remove html comments * * @param {String} html * @return {String} @@ -371,7 +365,7 @@ function stripCommentTag (html) { var STRIP_COMMENT_TAG_REGEXP = //g; /** - * 去除不可见字符 + * remove invisible characters * * @param {String} html * @return {String} diff --git a/lib/index.js b/lib/index.js index 22d3f5a..d1c86a1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,7 @@ /** - * 模块入口 + * xss * - * @author 老雷 + * @author Zongmin Lei */ var DEFAULT = require('./default'); @@ -10,10 +10,10 @@ 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) { @@ -22,14 +22,13 @@ function filterXSS (html, options) { } -// 输出 exports = module.exports = filterXSS; exports.FilterXSS = FilterXSS; for (var i in DEFAULT) exports[i] = DEFAULT[i]; for (var i in parser) exports[i] = parser[i]; -// 在浏览器端使用 +// using `xss` on the browser, output `filterXSS` to the globals if (typeof window !== 'undefined') { window.filterXSS = module.exports; } diff --git a/lib/parser.js b/lib/parser.js index 157c99f..9a186f4 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1,15 +1,15 @@ /** - * 简单 HTML Parser + * Simple HTML Parser * - * @author 老雷 + * @author Zongmin Lei */ var _ = require('./util'); /** - * 获取标签的名称 + * get tag name * - * @param {String} html 如:'' + * @param {String} html e.g. '' * @return {String} */ function getTagName (html) { @@ -26,7 +26,7 @@ function getTagName (html) { } /** - * 是否为闭合标签 + * is close tag? * * @param {String} html 如:'' * @return {Boolean} @@ -36,27 +36,24 @@ function isClosing (html) { } /** - * 分析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'; - 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) { @@ -85,7 +82,6 @@ function parseTag (html, onTag, escapeHtml) { tagStart = false; continue; } - // HTML标签内的引号仅当前一个字符是等于号时才有效 if ((c === '"' || c === "'") && html.charAt(currentPos - 1) === '=') { quoteStart = c; continue; @@ -105,28 +101,26 @@ function parseTag (html, onTag, escapeHtml) { return rethtml; } -// 不符合属性名称规则的正则表达式 -var REGEXP_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img; +var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9_:\.\-]/img; /** - * 分析标签HTML代码,调用相应的函数处理,返回HTML + * parse input attributes and returns processed attributes * - * @param {String} html 如标签'' 则为 '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'; - 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) { 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 || ''); if (ret) retAttrs.push(ret); @@ -142,7 +136,6 @@ function parseAttr (html, onAttr) { continue; } if (tmpName !== false) { - // HTML标签内的引号仅当前一个字符是等于号时才有效 if (i === lastPos && (c === '"' || c === "'") && html.charAt(i - 1) === '=') { j = html.indexOf(c, i + 1); if (j === -1) { diff --git a/lib/xss.js b/lib/xss.js index 97e134f..a54f307 100644 --- a/lib/xss.js +++ b/lib/xss.js @@ -1,7 +1,7 @@ /** - * 过滤XSS + * filter xss * - * @author 老雷 + * @author Zongmin Lei */ var FilterCSS = require('cssfilter').FilterCSS; @@ -13,7 +13,7 @@ var _ = require('./util'); /** - * 返回值是否为空 + * returns `true` if the input value is `undefined` or `null` * * @param {Object} obj * @return {Boolean} @@ -23,7 +23,7 @@ function isNull (obj) { } /** - * 取标签内的属性列表字符串 + * get attributes for a tag * * @param {String} html * @return {Object} @@ -48,7 +48,7 @@ function getAttrs (html) { } /** - * 浅拷贝对象 + * shallow copy * * @param {Object} obj * @return {Object} @@ -62,13 +62,13 @@ function shallowCopyObject (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) { options = shallowCopyObject(options || {}); @@ -98,13 +98,13 @@ function FilterXSS (options) { } /** - * 开始处理 + * start process and returns result * * @param {String} html * @return {String} */ FilterXSS.prototype.process = function (html) { - // 兼容各种奇葩输入 + // compatible with the input html = html || ''; html = html.toString(); if (!html) return ''; @@ -120,17 +120,17 @@ FilterXSS.prototype.process = function (html) { var escapeHtml = options.escapeHtml; var cssFilter = me.cssFilter; - // 是否清除不可见字符 + // remove invisible characters if (options.stripBlankChar) { html = DEFAULT.stripBlankChar(html); } - // 是否禁止备注标签 + // remove html comments if (!options.allowCommentTag) { html = DEFAULT.stripCommentTag(html); } - // 如果开启了stripIgnoreTagBody + // if enable stripIgnoreTagBody var stripIgnoreTagBody = false; if (options.stripIgnoreTagBody) { var stripIgnoreTagBody = DEFAULT.StripTagBody(options.stripIgnoreTagBody, onIgnoreTag); @@ -145,14 +145,11 @@ FilterXSS.prototype.process = function (html) { isWhite: (tag in whiteList) }; - // 调用onTag处理 + // call `onTag()` var ret = onTag(tag, html, info); if (!isNull(ret)) return ret; - // 默认标签处理方法 if (info.isWhite) { - // 白名单标签,解析标签属性 - // 如果是闭合标签,则不需要解析属性 if (info.isClosing) { return ''; } @@ -161,14 +158,13 @@ FilterXSS.prototype.process = function (html) { var whiteAttrList = whiteList[tag]; var attrsHtml = parseAttr(attrs.html, function (name, value) { - // 调用onTagAttr处理 + // call `onTagAttr()` var isWhiteAttr = (_.indexOf(whiteAttrList, name) !== -1); var ret = onTagAttr(tag, name, value, isWhiteAttr); if (!isNull(ret)) return ret; - // 默认的属性处理方法 if (isWhiteAttr) { - // 白名单属性,调用safeAttrValue过滤属性值 + // call `safeAttrValue()` value = safeAttrValue(tag, name, value, cssFilter); if (value) { return name + '="' + value + '"'; @@ -176,14 +172,14 @@ FilterXSS.prototype.process = function (html) { return name; } } else { - // 非白名单属性,调用onIgnoreTagAttr处理 + // call `onIgnoreTagAttr()` var ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr); if (!isNull(ret)) return ret; return; } }); - // 构造新的标签代码 + // build new tag html var html = '<' + tag; if (attrsHtml) html += ' ' + attrsHtml; if (attrs.closing) html += ' /'; @@ -191,7 +187,7 @@ FilterXSS.prototype.process = function (html) { return html; } else { - // 非白名单标签,调用onIgnoreTag处理 + // call `onIgnoreTag()` var ret = onIgnoreTag(tag, html, info); if (!isNull(ret)) return ret; return escapeHtml(html); @@ -199,7 +195,7 @@ FilterXSS.prototype.process = function (html) { }, escapeHtml); - // 如果开启了stripIgnoreTagBody,需要对结果再进行处理 + // if enable stripIgnoreTagBody if (stripIgnoreTagBody) { retHtml = stripIgnoreTagBody.remove(retHtml); } diff --git a/test/test_custom_method.js b/test/test_custom_method.js index b80a35f..27a72e5 100644 --- a/test/test_custom_method.js +++ b/test/test_custom_method.js @@ -1,7 +1,7 @@ /** - * 测试XSS 自定义处理函数 + * tests for custom method * - * @author 老雷 + * @author Zongmin Lei */ var assert = require('assert'); diff --git a/test/test_html_parser.js b/test/test_html_parser.js index 1a44bfe..389641c 100644 --- a/test/test_html_parser.js +++ b/test/test_html_parser.js @@ -1,7 +1,7 @@ /** - * 测试 html parser + * tests for html parser * - * @author 老雷 + * @author Zongmin Lei */ var assert = require('assert'); diff --git a/test/test_xss.js b/test/test_xss.js index 110c3f7..1a06754 100644 --- a/test/test_xss.js +++ b/test/test_xss.js @@ -1,7 +1,7 @@ /** - * 测试XSS + * tests for xss() function * - * @author 老雷 + * @author Zongmin Lei */ var assert = require('assert'); diff --git a/typings/xss-tests.ts b/typings/xss-tests.ts index 19a9953..22298c3 100644 --- a/typings/xss-tests.ts +++ b/typings/xss-tests.ts @@ -3,7 +3,7 @@ /** * xss typings test * - * @author 老雷 + * @author Zongmin Lei */ import xss = require('xss'); diff --git a/typings/xss.d.ts b/typings/xss.d.ts index f657ac0..f53a34a 100644 --- a/typings/xss.d.ts +++ b/typings/xss.d.ts @@ -1,7 +1,7 @@ /** * xss * - * @author 老雷 + * @author Zongmin Lei */ declare namespace XSS { @@ -97,7 +97,7 @@ declare namespace XSS { export interface ICSSFilter { process(value: string): string; } - + } declare module 'xss' {