From 125544728ab093d09bc88047244242fbcdd83d18 Mon Sep 17 00:00:00 2001 From: Phinome Date: Thu, 22 Dec 2016 16:27:52 +0800 Subject: [PATCH] fixed xss bug --- .gitignore | 3 +- _examples/editor_api.js | 1 + _src/plugins/link.js | 8 ++-- _src/plugins/video.js | 1 + _src/plugins/xssFilter.js | 79 +++++++++++++++++++++++++++++++++++++++ _src/ui/widget.js | 2 +- umeditor.config.js | 77 +++++++++++++++++++++++++++++++++++++- 7 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 _src/plugins/xssFilter.js diff --git a/.gitignore b/.gitignore index b5b131ea..a0fd5508 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules dist -asp/upload \ No newline at end of file +asp/upload +.vscode diff --git a/_examples/editor_api.js b/_examples/editor_api.js index 0aa7cee5..3346dcdc 100755 --- a/_examples/editor_api.js +++ b/_examples/editor_api.js @@ -39,6 +39,7 @@ 'plugins/autosave.js', 'plugins/autoupload.js', 'plugins/formula.js', + 'plugins/xssFilter.js', 'ui/widget.js', 'ui/button.js', 'ui/toolbar.js', diff --git a/_src/plugins/link.js b/_src/plugins/link.js index e935352e..d39abb79 100755 --- a/_src/plugins/link.js +++ b/_src/plugins/link.js @@ -57,7 +57,7 @@ UM.plugins['link'] = function(){ this.addOutputRule(function(root){ $.each(root.getNodesByTagName('a'),function(i,a){ - var _href = utils.html(a.getAttr('_href')); + var _href = a.getAttr('_href'); if(!/^(ftp|https?|\/|file)/.test(_href)){ _href = 'http://' + _href; } @@ -70,7 +70,7 @@ UM.plugins['link'] = function(){ }); this.addInputRule(function(root){ $.each(root.getNodesByTagName('a'),function(i,a){ - a.setAttr('_href', utils.html(a.getAttr('href'))); + a.setAttr('_href', a.getAttr('href')); }) }); me.commands['link'] = { @@ -78,13 +78,15 @@ UM.plugins['link'] = function(){ var me = this; var rng = me.selection.getRange(); + opt._href && (opt._href = utils.unhtml(opt._href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); + opt.href && (opt.href = utils.unhtml(opt.href, /[<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g)); if(rng.collapsed){ var start = rng.startContainer; if(start = domUtils.findParentByTagName(start,'a',true)){ $(start).attr(opt); rng.selectNode(start).select() }else{ - rng.insertNode($('' +opt.href+'').attr(opt)[0]).select() + rng.insertNode($('' + opt.href +'').attr(opt)[0]).select() } diff --git a/_src/plugins/video.js b/_src/plugins/video.js index 96d7d146..43b4f62c 100755 --- a/_src/plugins/video.js +++ b/_src/plugins/video.js @@ -52,6 +52,7 @@ UM.plugins['video'] = function (){ var html = [],id = 'tmpVedio'; for(var i=0,vi,len = videoObjs.length;i'](?:(amp|lt|quot|gt|#39|nbsp);)?/g); html.push(creatInsertStr( vi.url, vi.width || 420, vi.height || 280, id + i,vi.align,false)); } me.execCommand("inserthtml",html.join(""),true); diff --git a/_src/plugins/xssFilter.js b/_src/plugins/xssFilter.js new file mode 100644 index 00000000..2437cfd5 --- /dev/null +++ b/_src/plugins/xssFilter.js @@ -0,0 +1,79 @@ +/** + * @file xssFilter.js + * @desc xss过滤器 + * @author robbenmu + */ + +UM.plugins.xssFilter = function() { + + var config = UMEDITOR_CONFIG; + var whiteList = config.whiteList; + + function filter(node) { + + var tagName = node.tagName; + var attrs = node.attrs; + + if (!whiteList.hasOwnProperty(tagName)) { + node.parentNode.removeChild(node); + return false; + } + + UM.utils.each(attrs, function (val, key) { + + if (whiteList[tagName].indexOf(key) === -1) { + node.setAttr(key); + } + }); + } + + // 添加inserthtml\paste等操作用的过滤规则 + if (whiteList && config.xssFilterRules) { + this.options.filterRules = function () { + + var result = {}; + + UM.utils.each(whiteList, function(val, key) { + result[key] = function (node) { + return filter(node); + }; + }); + + return result; + }(); + } + + var tagList = []; + + UM.utils.each(whiteList, function (val, key) { + tagList.push(key); + }); + + // 添加input过滤规则 + // + if (whiteList && config.inputXssFilter) { + this.addInputRule(function (root) { + + root.traversal(function(node) { + if (node.type !== 'element') { + return false; + } + filter(node); + }); + }); + } + // 添加output过滤规则 + // + if (whiteList && config.outputXssFilter) { + this.addOutputRule(function (root) { + + root.traversal(function(node) { + if (node.type !== 'element') { + return false; + } + filter(node); + }); + }); + } + +}; \ No newline at end of file diff --git a/_src/ui/widget.js b/_src/ui/widget.js index 36acbc89..a257dce1 100644 --- a/_src/ui/widget.js +++ b/_src/ui/widget.js @@ -2,7 +2,7 @@ //对jquery的扩展 $.parseTmpl = function parse(str, data) { var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) { - return "'," + code.replace(/\\'/g, "'") + ",'"; + return "',obj." + code.replace(/\\'/g, "'") + ",'"; }).replace(/<%([\s\S]+?)%>/g,function (match, code) { return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('"; }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');"; diff --git a/umeditor.config.js b/umeditor.config.js index 212dc8e9..8fc2eb82 100755 --- a/umeditor.config.js +++ b/umeditor.config.js @@ -244,6 +244,81 @@ //,topOffset:30 //填写过滤规则 - //,filterRules: {} + ,filterRules: {} + // xss 过滤是否开启,inserthtml等操作 + ,xssFilterRules: true + //input xss过滤 + ,inputXssFilter: true + //output xss过滤 + ,outputXssFilter: true + // xss过滤白名单 名单来源: https://raw.githubusercontent.com/leizongmin/js-xss/master/lib/default.js + ,whiteList: { + a: ['target', 'href', 'title', 'style', 'class', 'id'], + abbr: ['title', 'style', 'class', 'id'], + address: ['style', 'class', 'id'], + area: ['shape', 'coords', 'href', 'alt', 'style', 'class', 'id'], + article: ['style', 'class', 'id'], + aside: ['style', 'class', 'id'], + audio: ['autoplay', 'controls', 'loop', 'preload', 'src', 'style', 'class', 'id'], + b: ['style', 'class', 'id'], + bdi: ['dir'], + bdo: ['dir'], + big: [], + blockquote: ['cite', 'style', 'class', 'id'], + br: [], + caption: ['style', 'class', 'id'], + center: [], + cite: [], + code: ['style', 'class', 'id'], + col: ['align', 'valign', 'span', 'width', 'style', 'class', 'id'], + colgroup: ['align', 'valign', 'span', 'width', 'style', 'class', 'id'], + dd: ['style', 'class', 'id'], + del: ['datetime', 'style', 'class', 'id'], + details: ['open', 'style', 'class', 'id'], + div: ['style', 'class', 'id'], + dl: ['style', 'class', 'id'], + dt: ['style', 'class', 'id'], + em: ['style', 'class', 'id'], + embed: ['style', 'class', 'id', '_url', 'type', 'pluginspage', 'src', 'width', 'height', 'wmode', 'play', 'loop', 'menu', 'allowscriptaccess', 'allowfullscreen'], + font: ['color', 'size', 'face', 'style', 'class', 'id'], + footer: ['style', 'class', 'id'], + h1: ['style', 'class', 'id'], + h2: ['style', 'class', 'id'], + h3: ['style', 'class', 'id'], + h4: ['style', 'class', 'id'], + h5: ['style', 'class', 'id'], + h6: ['style', 'class', 'id'], + header: ['style', 'class', 'id'], + hr: ['style', 'class', 'id'], + i: ['style', 'class', 'id'], + iframe: ['style', 'class', 'id', 'src', 'frameborder', 'data-latex'], + img: ['src', 'alt', 'title', 'width', 'height', 'style', 'class', 'id', '_url'], + ins: ['datetime', 'style', 'class', 'id'], + li: ['style', 'class', 'id'], + mark: [], + nav: [], + ol: ['style', 'class', 'id'], + p: ['style', 'class', 'id'], + pre: ['style', 'class', 'id'], + s: [], + section:[], + small: ['style', 'class', 'id'], + span: ['style', 'class', 'id'], + sub: ['style', 'class', 'id'], + sup: ['style', 'class', 'id'], + strong: ['style', 'class', 'id'], + table: ['width', 'border', 'align', 'valign', 'style', 'class', 'id'], + tbody: ['align', 'valign', 'style', 'class', 'id'], + td: ['width', 'rowspan', 'colspan', 'align', 'valign', 'style', 'class', 'id'], + tfoot: ['align', 'valign', 'style', 'class', 'id'], + th: ['width', 'rowspan', 'colspan', 'align', 'valign', 'style', 'class', 'id'], + thead: ['align', 'valign', 'style', 'class', 'id'], + tr: ['rowspan', 'align', 'valign', 'style', 'class', 'id'], + tt: ['style', 'class', 'id'], + u: [], + ul: ['style', 'class', 'id'], + svg: ['style', 'class', 'id', 'width', 'height', 'xmlns', 'fill', 'viewBox'], + video: ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width', 'style', 'class', 'id'] + } }; })();